Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update okta login guide for realworld app #4883

Merged
merged 3 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/img/examples/okta-origin.mp4
Binary file not shown.
Binary file added assets/img/examples/okta-session-restore.mp4
Binary file not shown.
161 changes: 146 additions & 15 deletions content/guides/end-to-end-testing/okta-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ e2eSpecific: true

## <Icon name="graduation-cap"></Icon> What you'll learn
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty much the same feedback as #4882

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we settle on #4882 ill add the same feedback here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made same updates from #4882 in 31b0c6c


- Programmatically authenticate with [Okta](https://okta.com) via a custom
Cypress command
- Adapting your [Okta](https://okta.com) application for programmatic
- Authenticate with [`cy.origin()`](/api/commands/origin) or programmatically
(legacy) with [Okta](https://okta.com) via a custom Cypress command
- If needed, adapting your [Okta](https://okta.com) application for programmatic
authentication during testing

</Alert>
Expand All @@ -18,20 +18,21 @@ e2eSpecific: true

The scope of this guide is to demonstrate authentication solely against the
[Okta Universal Directory](https://www.okta.com/products/universal-directory/).
Future guides will expand to include cover [Okta](https://okta.com)
authentication with other identity providers.
Future guides will expand to cover [Okta](https://okta.com) authentication with
other identity providers.

</Alert>

<Alert type="success">

<strong class="alert-header">Why authenticate programmatically?</strong>
<strong class="alert-header">Authenticate by visiting a different domain with
[`cy.origin()`](/api/commands/origin)</strong>

Typically, logging in a user within your app by authenticating via a third-party
provider requires visiting login pages hosted on a different domain. Since each
Cypress test is limited to visiting domains of the same origin, we can subvert
visiting and testing third-party login pages by programmatically interacting
with the third-party authentication API to login a user.
Programmatic authentication for Okta is now considered a legacy recommendation.
As of Cypress [v12.0.0](https://on.cypress.io/changelog#12-0-0), you can easily
authenticate to
[Okta Universal Directory](https://www.okta.com/products/universal-directory/)
as Cypress tests are no longer limited to visiting domains of a single origin.

</Alert>

Expand Down Expand Up @@ -70,11 +71,141 @@ require('dotenv').config()

## Custom Command for Okta Authentication

There are two ways you can authenticate to Okta

- [Login with cy.origin()](/guides/end-to-end-testing/okta-authentication#Login-with-cy-origin)
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
- [Legacy Programmatic Access](/guides/end-to-end-testing/okta-authentication#Programmatic-Login-Legacy)

### Login with [`cy.origin()`](/api/commands/origin)

Next, we'll write a command to perform a login to [Okta](https://okta.com) using
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
[`cy.origin()`](/api/commands/origin) to navigate to Okta, inputting user
credentials, and signing in via OAuth to redirect back to our application, and
caching the results with [`cy.session()`](/api/commands/session).

In this `loginByOkta` command, we redirect to the okta login screen via
navigation guards. We then enter our user credentials and sign in, which
redirects us back to the
[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app).

```jsx
// cypress/support/auth-provider-commands/okta.ts
// Okta
Cypress.Commands.add('loginByOkta', (username: string, password: string) => {
Cypress.log({
displayName: 'OKTA LOGIN',
message: [`🔐 Authenticating | ${username}`],
// @ts-ignore
autoEnd: false,
})

cy.visit('/')

cy.origin(
Cypress.env('okta_domain'),
{ args: { username, password } },
({ username, password }) => {
cy.get('input[name="identifier"]').type(username)
cy.get('input[name="credentials.passcode"]').type(password, {
log: false,
})
cy.get('[type="submit"]').click()
}
)

cy.get('[data-test="sidenav-username"]').should('contain', username)
})
```

Secondly, we can use our `loginByOkta` command in the test. Below is our test to
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
login as a user via [Okta](https://okta.com) and run a few basic sanity tests.
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved

<Alert type="success">

The
[runnable version of this test](https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/okta.spec.ts)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the custom command found in this file or the support file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

support file, which in the code snippet at the top has something like cypress/support/auth-provider-commands/okta.ts. I'm hopiong thats enough to make it discoverable since that is what we've done elsewhere, even though just clicking on the custom command just takes you the the typings definition

is in the
[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app).

</Alert>

```jsx
describe('Okta', function () {
beforeEach(function () {
cy.task('db:seed')

cy.loginByOkta(Cypress.env('okta_username'), Cypress.env('okta_password'))
})

it('verifies signed in user does not have a bank account', function () {
cy.get('[data-test="sidenav-bankaccounts"]').click()
cy.get('[data-test="empty-list-header"]').should('be.visible')
})
})
```

<DocsVideo src="/img/examples/okta-origin.mp4"></DocsVideo>

Lastly, we can refactor our login command to take advantage of
[`cy.session()`](/api/commands/session) to store our logged in user so we don't
have to reauthenticate with everything test.

```jsx
Cypress.Commands.add('loginByOkta', (username: string, password: string) => {
cy.session(
`okta-${username}`,
() => {
Cypress.log({
displayName: 'OKTA LOGIN',
message: [`🔐 Authenticating | ${username}`],
// @ts-ignore
autoEnd: false,
})

cy.visit('/')

cy.origin(
Cypress.env('okta_domain'),
{ args: { username, password } },
({ username, password }) => {
cy.get('input[name="identifier"]').type(username)
cy.get('input[name="credentials.passcode"]').type(password, {
log: false,
})
cy.get('[type="submit"]').click()
}
)

cy.get('[data-test="sidenav-username"]').should('contain', username)
},
{
validate() {
cy.visit('/')
cy.get('[data-test="sidenav-username"]').should('contain', username)
},
}
)
})
```

<DocsVideo src="/img/examples/okta-session-restore.mp4"></DocsVideo>

Unlike programmatic login, authenticating with
[`cy.origin()`](/api/commands/origin) does not require adapting the application
to work. It simply works without intervention exactly how your users would
consume the application!

### Programmatic Login (Legacy)

Next, we will write a command named `loginByOktaApi` to perform a programmatic
login into [Okta](https://okta.com) and set an item in localStorage with the
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
authenticated users details, which we will use in our application code to verify
we are authenticated under test.

In order to make sure this is enabled inside the
[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app),
Please set the `REACT_APP_OKTA_PROGRAMMATIC` environment variable to `true`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this ENV needed? Also do we need to call it out here or can we link to a section in the read me to share how to set this up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added REACT_APP_OKTA_PROGRAMMATIC since the programmatic approach strips the auth guards out of the webapp, which we don't want with UI login with cy.origin() since we want to test how the app actually behaves and get the natural redirect to the auth page when we aren't authenticated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured here is fine since the README just points back to the guide


The `loginByOktaApi` command will execute the following steps:

1. Use the
Expand Down Expand Up @@ -166,13 +297,13 @@ is in the

</Alert>

## Adapting an Okta App for Testing
#### Adapting an Okta App for Testing

<Alert type="info">

<strong class="alert-header">Note</strong>

The previous sections focused on the recommended Okta authentication practice
The previous sections focused on the programmatic Okta authentication practice
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
within Cypress tests. To use this practice it is assumed you are testing an app
AtofStryker marked this conversation as resolved.
Show resolved Hide resolved
appropriately built or adapted to use Okta.

Expand Down Expand Up @@ -202,7 +333,7 @@ Use the `yarn dev:okta` command when starting the

</Alert>

### Adapting the back end
#### Adapting the back end

In order to validate API requests from the frontend, we install
[Okta JWT Verifier for Node.js](https://github.com/okta/okta-oidc-js/tree/master/packages/jwt-verifier)
Expand Down Expand Up @@ -281,7 +412,7 @@ if (process.env.REACT_APP_OKTA) {
// routes ...
```

### Adapting the front end
#### Adapting the front end

We need to update our front end React app to allow for authentication with
[Okta](https://okta.com) using the
Expand Down