diff --git a/assets/img/examples/aws-cognito-origin.mp4 b/assets/img/examples/aws-cognito-origin.mp4 new file mode 100644 index 0000000000..b9594ff0d0 Binary files /dev/null and b/assets/img/examples/aws-cognito-origin.mp4 differ diff --git a/assets/img/examples/cognito-session-restore.mp4 b/assets/img/examples/cognito-session-restore.mp4 new file mode 100644 index 0000000000..413613d9ee Binary files /dev/null and b/assets/img/examples/cognito-session-restore.mp4 differ diff --git a/content/guides/end-to-end-testing/amazon-cognito-authentication.md b/content/guides/end-to-end-testing/amazon-cognito-authentication.md index 495b57ffa8..167bdce093 100644 --- a/content/guides/end-to-end-testing/amazon-cognito-authentication.md +++ b/content/guides/end-to-end-testing/amazon-cognito-authentication.md @@ -7,6 +7,8 @@ e2eSpecific: true ## What you'll learn +- Log in to [Amazon Cognito](https://aws.amazon.com/cognito) through the UI with + [`cy.origin()`](/api/commands/origin) - Programmatically authenticate with [Amazon Cognito](https://aws.amazon.com/cognito) via a custom Cypress command - Adapting your [Amazon Cognito](https://aws.amazon.com/cognito) application for @@ -16,13 +18,17 @@ e2eSpecific: true -Why authenticate programmatically? +Authenticate by visiting a different domain with +[`cy.origin()`](/api/commands/origin) 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. +provider requires visiting a login page hosted on a different domain. Before +Cypress [v12.0.0](https://on.cypress.io/changelog#12-0-0), Cypress tests were +limited to visiting domains of the same origin, making programmatic login the +only option for authenticating users with a third-party API. As of Cypress +[v12.0.0](https://on.cypress.io/changelog#12-0-0), Cypress tests are no longer +limited to visiting domains of a single origin, meaning you can easily +authenticate to federated AWS Cognito via the UI! @@ -36,7 +42,7 @@ mobile apps quickly and easily" and "scales to millions of users and supports sign-in with social identity providers, such as Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0." -## Programmatic Authentication with Amazon Cognito +## Authentication with Amazon Cognito The documentation for [Amazon Cognito](https://aws.amazon.com/cognito) recommends using the @@ -157,8 +163,135 @@ const awsConfig = require('./aws-exports-es5.js') ## Custom Command for Amazon Cognito Authentication +There are two ways you can authenticate to AWS Cognito: + +- [Login with `cy.origin()`](/guides/end-to-end-testing/amazon-cognito-authentication#Login-with-cy-origin) +- [Programmatic Access](/guides/end-to-end-testing/amazon-cognito-authentication#Programmatic-Login) + +### Login with [`cy.origin()`](/api/commands/origin) + +Next, we'll write a custom command called `loginByCognito` to perform a login to +[Amazon Cognito](https://aws.amazon.com/cognito). This command will use +[`cy.origin()`](/api/commands/origin) to + +1. navigate to the Cognito origin +2. input user credentials +3. sign in and redirect back to the + [Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app) +4. cache the results with [`cy.session()`](/api/commands/session) + +```jsx +// cypress/support/auth-provider-commands/cognito.ts +// Amazon Cognito +const loginToCognito = (username: string, password: string) => { + Cypress.log({ + displayName: 'COGNITO LOGIN', + message: [`🔐 Authenticating | ${username}`], + autoEnd: false, + }) + + cy.visit('/') + cy.contains('Sign in with AWS', { + includeShadowDom: true, + }).click() + + cy.origin( + Cypress.env('cognito_domain'), + { + args: { + username, + password, + }, + }, + ({ username, password }) => { + // Cognito log in page has some elements of the same id but are off screen. + // We only want the visible elements to log in + cy.get('input[name="username"]:visible').type(username) + cy.get('input[name="password"]:visible').type(password, { + // use log: false to prevent your password from showing in the Command Log + log: false, + }) + cy.get('input[name="signInSubmitButton"]:visible').click() + } + ) + + // give a few seconds for redirect to settle + cy.wait(2000) + + // verify we have made it passed the login screen + cy.contains('Get Started').should('be.visible') +} + +// right now our custom command is light. More on this later! +Cypress.Commands.add('loginByCognito', (username, password) => { + return loginToCognito(username, password) +}) +``` + +Now, we can use our `loginByCognito` command in the test. Below is our test to +login as a user via [Amazon Cognito](https://aws.amazon.com/cognito), complete +the onboarding process and logout. + + + +The +[runnable version of this test](https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/cognito.spec.ts) +is in the +[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app). + + + +```jsx +describe('Cognito', function () { + beforeEach(function () { + // Seed database with test data + cy.task('db:seed') + + // login via Amazon Cognito via cy.origin() + cy.loginByCognito( + Cypress.env('cognito_username'), + Cypress.env('cognito_password') + ) + }) + + it('shows onboarding', function () { + cy.contains('Get Started').should('be.visible') + }) +}) +``` + + + +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/support/auth-provider-commands/cognito.ts +// Amazon Cognito +Cypress.Commands.add('loginByCognito', (username, password) => { + cy.session( + `cognito-${username}`, + () => { + return loginToCognito(username, password) + }, + { + validate() { + cy.visit('/') + // revalidate our session to make sure we are logged in + cy.contains('Get Started').should('be.visible') + }, + } + ) +}) +``` + + + +### Programmatic Login + Next, we'll write a command to perform a programmatic login into -[Amazon Cognito](https://aws.amazon.com/cognito) and set items in localStorage +[Amazon Cognito](https://aws.amazon.com/cognito) and set items in `localStorage` with the authenticated users details, which we will use in our application code to verify we are authenticated under test. @@ -255,7 +388,17 @@ describe('Cognito', function () { }) ``` -## Adapting an Amazon Cognito App for Testing +### Adapting an Amazon Cognito App for Testing + + + +Programmatic Login + +Unlike programmatic login, authenticating with +[`cy.origin()`](/api/commands/origin) does not require adapting the application +to work. This step is only needed if implementing programmatic login. + + The [Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app) is @@ -267,7 +410,7 @@ The front end uses the The back end uses the [express-jwt](https://github.com/auth0/express-jwt) to validate JWTs from [Amazon Cognito](https://aws.amazon.com/cognito). -### Adapting the back end +#### Adapting the back end In order to validate API requests from the frontend, we install [express-jwt](https://github.com/auth0/express-jwt) and @@ -312,7 +455,7 @@ if (process.env.REACT_APP_AWS_COGNITO) { // routes ... ``` -### Adapting the front end +#### Adapting the front end We need to update our front end React app to allow for authentication with [Amazon Cognito](https://aws.amazon.com/cognito) using the