@@ -7,25 +7,15 @@ e2eSpecific: true
7
7
8
8
## <Icon name =" graduation-cap " ></Icon > What you'll learn
9
9
10
+ - Log in to [ Auth0] ( https://auth0.com ) through the UI with
11
+ [ ` cy.origin() ` ] ( /api/commands/origin )
10
12
- Programmatically authenticate with [ Auth0] ( https://auth0.com ) via a custom
11
13
Cypress command
12
- - Adapting your [ Auth0] ( https://auth0.com ) application for programmatic
14
+ - Adapt your [ Auth0] ( https://auth0.com ) application for programmatic
13
15
authentication during testing
14
16
15
17
</Alert >
16
18
17
- <Alert type =" success " >
18
-
19
- <strong class =" alert-header " >Why authenticate programmatically?</strong >
20
-
21
- Typically, logging in a user within your app by authenticating via a third-party
22
- provider requires visiting login pages hosted on a different domain. Since each
23
- Cypress test is limited to visiting domains of the same origin, we can subvert
24
- visiting and testing third-party login pages by programmatically interacting
25
- with the third-party authentication API to login a user.
26
-
27
- </Alert >
28
-
29
19
<Alert type =" warning " >
30
20
31
21
This guide is setup for testing against an [ Auth0] ( https://auth0.com ) Single
@@ -36,6 +26,22 @@ for automated end-to-end testing.
36
26
37
27
</Alert >
38
28
29
+ <Alert type =" success " >
30
+
31
+ <strong class =" alert-header " >Authenticate by visiting a different domain with
32
+ [ ` cy.origin() ` ] ( /api/commands/origin ) </strong >
33
+
34
+ Typically, logging in a user within your app by authenticating via a third-party
35
+ provider requires visiting a login page hosted on a different domain. Before
36
+ Cypress [ v12.0.0] ( https://on.cypress.io/changelog#12-0-0 ) , Cypress tests were
37
+ limited to visiting domains of the same origin, making programmatic login the
38
+ only option for authenticating users with a third-party API. As of Cypress
39
+ [ v12.0.0] ( https://on.cypress.io/changelog#12-0-0 ) , Cypress tests are no longer
40
+ limited to visiting domains of a single origin, meaning you can easily
41
+ authenticate with [ Auth0] ( https://auth0.com ) via the UI!
42
+
43
+ </Alert >
44
+
39
45
## Auth0 Application Setup
40
46
41
47
To get started with Auth0, an application needs to be setup within the
@@ -111,23 +117,151 @@ require('dotenv').config()
111
117
}
112
118
```
113
119
120
+ Note that ` auth0_client_secret ` is only needed for
121
+ [ programmatic login] ( #Programmatic-Login ) .
122
+
114
123
:::
115
124
116
125
## Custom Command for Auth0 Authentication
117
126
127
+ There are two ways you can authenticate to Auth0:
128
+
129
+ - [ Login with ` cy.origin() ` ] ( #Login-with-cy-origin )
130
+ - [ Programmatic Login] ( #Programmatic-Login )
131
+
132
+ ### Login with [ ` cy.origin() ` ] ( /api/commands/origin )
133
+
134
+ Next, we'll write a custom command called ` loginToAuth0 ` to perform a login to
135
+ [ Auth0] ( https://auth0.com ) . This command will use
136
+ [ ` cy.origin() ` ] ( /api/commands/origin ) to
137
+
138
+ 1 . Navigate to the Auth0 login
139
+ 2 . Input user credentials
140
+ 3 . Sign in and redirect back to the
141
+ [ Cypress Real World App] ( https://github.com/cypress-io/cypress-realworld-app )
142
+ 4 . Cache the results with [ ` cy.session() ` ] ( /api/commands/session )
143
+
144
+ ``` js
145
+ // cypress/support/auth-provider-commands/auth0.ts
146
+
147
+ function loginViaAuth0Ui (username : string , password : string ) {
148
+ // App landing page redirects to Auth0.
149
+ cy .visit (' /' )
150
+
151
+ // Login on Auth0.
152
+ cy .origin (
153
+ Cypress .env (' auth0_domain' ),
154
+ { args: { username, password } },
155
+ ({ username, password }) => {
156
+ cy .get (' input#username' ).type (username)
157
+ cy .get (' input#password' ).type (password, { log: false })
158
+ cy .contains (' button[value=default]' , ' Continue' ).click ()
159
+ }
160
+ )
161
+
162
+ // Ensure Auth0 has redirected us back to the RWA.
163
+ cy .url ().should (' equal' , ' http://localhost:3000/' )
164
+ }
165
+
166
+ Cypress .Commands .add (' loginToAuth0' , (username : string , password : string ) => {
167
+ const log = Cypress .log ({
168
+ displayName: ' AUTH0 LOGIN' ,
169
+ message: [` 🔐 Authenticating | ${ username} ` ],
170
+ // @ts-ignore
171
+ autoEnd: false ,
172
+ })
173
+ log .snapshot (' before' )
174
+
175
+ loginViaAuth0Ui (username, password)
176
+
177
+ log .snapshot (' after' )
178
+ log .end ()
179
+ })
180
+ ```
181
+
182
+ Now, we can use our ` loginToAuth0 ` command in the test. Below is our test to
183
+ login as a user via Auth0 and run a basic sanity check.
184
+
185
+ <Alert type =" success " >
186
+
187
+ The
188
+ [ runnable version of this test] ( https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/auth0.spec.ts )
189
+ is in the
190
+ [ Cypress Real World App] ( https://github.com/cypress-io/cypress-realworld-app ) .
191
+
192
+ </Alert >
193
+
194
+ ``` js
195
+ describe (' Auth0' , function () {
196
+ beforeEach (function () {
197
+ cy .task (' db:seed' )
198
+ cy .intercept (' POST' , ' /graphql' ).as (' createBankAccount' )
199
+ cy .loginToAuth0 (
200
+ Cypress .env (' auth0_username' ),
201
+ Cypress .env (' auth0_password' )
202
+ )
203
+ cy .visit (' /' )
204
+ })
205
+
206
+ it (' shows onboarding' , function () {
207
+ cy .contains (' Get Started' ).should (' be.visible' )
208
+ })
209
+ })
210
+ ```
211
+
212
+ <DocsVideo src =" /img/examples/auth0-origin.mp4 " ></DocsVideo >
213
+
214
+ Lastly, we can refactor our login command to take advantage of
215
+ [ ` cy.session() ` ] ( /api/commands/session ) to store our logged in user so we don't
216
+ have to reauthenticate before every test.
217
+
218
+ ``` js
219
+ Cypress .Commands .add (' loginToAuth0' , (username : string , password : string ) => {
220
+ const log = Cypress .log ({
221
+ displayName: ' AUTH0 LOGIN' ,
222
+ message: [` 🔐 Authenticating | ${ username} ` ],
223
+ // @ts-ignore
224
+ autoEnd: false ,
225
+ })
226
+ log .snapshot (' before' )
227
+
228
+ cy .session (
229
+ ` auth0-${ username} ` ,
230
+ () => {
231
+ loginViaAuth0Ui (username, password)
232
+ },
233
+ {
234
+ validate : () => {
235
+ // Validate presence of access token in localStorage.
236
+ cy .wrap (localStorage)
237
+ .invoke (' getItem' , ' authAccessToken' )
238
+ .should (' exist' )
239
+ },
240
+ }
241
+ )
242
+
243
+ log .snapshot (' after' )
244
+ log .end ()
245
+ })
246
+ ```
247
+
248
+ <DocsVideo src =" /img/examples/auth0-session-restore.mp4 " ></DocsVideo >
249
+
250
+ ### Programmatic Login
251
+
118
252
Below is a command to programmatically login into [ Auth0] ( https://auth0.com ) ,
119
253
using the
120
254
[ /oauth/token endpoint] ( https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint )
121
- and set an item in localStorage with the authenticated users details, which we
255
+ and set an item in ` localStorage ` with the authenticated users details, which we
122
256
will use in our application code to verify we are authenticated under test.
123
257
124
258
The ` loginByAuth0Api ` command will execute the following steps:
125
259
126
260
1 . Use the
127
261
[ /oauth/token endpoint] ( https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint )
128
262
to perform the programmatic login.
129
- 2 . Finally the ` auth0Cypress ` localStorage item is set with the ` access token ` ,
130
- ` id_token ` and user profile.
263
+ 2 . Finally the ` auth0Cypress ` ` localStorage ` item is set with the
264
+ ` access token ` , ` id_token ` and user profile.
131
265
132
266
``` jsx
133
267
// cypress/support/commands.js
@@ -216,17 +350,6 @@ describe('Auth0', function () {
216
350
})
217
351
```
218
352
219
- <Alert type =" success " >
220
-
221
- <strong class =" alert-header " >Try it out</strong >
222
-
223
- The
224
- [ runnable version of this test] ( https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/auth0.spec.ts )
225
- is in the
226
- [ Cypress Real World App] ( https://github.com/cypress-io/cypress-realworld-app ) .
227
-
228
- </Alert >
229
-
230
353
## Adapting an Auth0 App for Testing
231
354
232
355
<Alert type =" info " >
@@ -238,7 +361,11 @@ within Cypress tests. To use this practice it is assumed you are testing an app
238
361
appropriately built or adapted to use Auth0.
239
362
240
363
The following sections provides guidance on building or adapting an app to use
241
- Auth0 authentication.
364
+ Auth0 authentication. Please note that if you are
365
+ [ logging in with ` cy.origin() ` ] ( #Login-with-cy-origin ) and your app is already
366
+ successfully integrated with Auth0, you do not need to make any further changes
367
+ to your app and the remainder of this guide should be regarded as purely
368
+ informational.
242
369
243
370
</Alert >
244
371
@@ -420,7 +547,7 @@ if (process.env.REACT_APP_AUTH0) {
420
547
421
548
An update to our
422
549
[ AppAuth0.tsx component] ( https://github.com/cypress-io/cypress-realworld-app/blob/develop/src/containers/AppAuth0.tsx )
423
- is needed to conditionally use the ` auth0Cypress ` localStorage item.
550
+ is needed to conditionally use the ` auth0Cypress ` ` localStorage ` item.
424
551
425
552
In the code below, we conditionally apply a ` useEffect ` block based on being
426
553
under test with Cypress (using ` window.Cypress ` ).
0 commit comments