cypress/integration/ete/sign_in_passcode.8.cy.ts (424 lines of code) (raw):

import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, randomPassword, } from '../../support/commands/testUser'; describe('Sign In flow, with passcode', () => { // set up useful variables const returnUrl = 'https://www.theguardian.com/world/2013/jun/09/edward-snowden-nsa-whistleblower-surveillance'; const encodedReturnUrl = encodeURIComponent(returnUrl); const appClientId = 'appClientId1'; const fromURI = '/oauth2/v1/authorize'; const sendEmailAndValidatePasscode = ({ emailAddress, expectedReturnUrl = 'https://m.code.dev-theguardian.com/', params, expectedEmailBody = 'Your one-time passcode', additionalTests, }: { emailAddress: string; expectedReturnUrl?: string; params?: string; expectedEmailBody?: 'Your one-time passcode' | 'Your verification code'; additionalTests?: 'passcode-incorrect' | 'resend-email' | 'change-email'; }) => { cy.setCookie('cypress-mock-state', '1'); // passcode send again timer cy.visit(`/signin?${params ? `${params}&` : ''}usePasscodeSignIn=true`); cy.get('input[name=email]').clear().type(emailAddress); const timeRequestWasMade = new Date(); cy.get('[data-cy="main-form-submit-button"]').click(); cy.checkForEmailAndGetDetails(emailAddress, timeRequestWasMade).then( ({ body, codes }) => { // email expect(body).to.have.string(expectedEmailBody); expect(codes?.length).to.eq(1); const code = codes?.[0].value; expect(code).to.match(/^\d{6}$/); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); switch (additionalTests) { case 'resend-email': { const timeRequestWasMade2 = new Date(); cy.wait(1000); // wait for the send again button to be enabled cy.contains('send again').click(); cy.checkForEmailAndGetDetails( emailAddress, timeRequestWasMade2, ).then(({ body, codes }) => { // email expect(body).to.have.string(expectedEmailBody); expect(codes?.length).to.eq(1); const code = codes?.[0].value; expect(code).to.match(/^\d{6}$/); cy.contains('Sign in'); cy.get('input[name=code]').type(code!); cy.url().should('include', expectedReturnUrl); cy.getTestOktaUser(emailAddress).then((user) => { expect(user.status).to.eq('ACTIVE'); expect(user.profile.emailValidated).to.eq(true); }); }); } break; case 'change-email': cy.contains('try another address').click(); cy.url().should('include', '/signin'); break; case 'passcode-incorrect': cy.contains('Sign in'); cy.get('input[name=code]').type(`${+code! + 1}`); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); cy.get('input[name=code]').clear().type(code!); cy.contains('Sign in').click(); cy.url().should('include', expectedReturnUrl); cy.getTestOktaUser(emailAddress).then((user) => { expect(user.status).to.eq('ACTIVE'); expect(user.profile.emailValidated).to.eq(true); }); break; default: { cy.contains('Sign in'); cy.get('input[name=code]').type(code!); cy.url().should('include', expectedReturnUrl); cy.getTestOktaUser(emailAddress).then((user) => { expect(user.status).to.eq('ACTIVE'); expect(user.profile.emailValidated).to.eq(true); }); } } }, ); }; beforeEach(() => { // Intercept the external redirect pages. // We just want to check that the redirect happens, not that the page loads. cy.intercept('GET', 'https://m.code.dev-theguardian.com/', (req) => { req.reply(200); }); cy.intercept('GET', returnUrl, (req) => { req.reply(200); }); cy.intercept( 'GET', `https://${Cypress.env('BASE_URI')}${decodeURIComponent(fromURI)}`, (req) => { req.reply(200); }, ); }); context('ACTIVE user - with email authenticator', () => { it('should sign in with passcode', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, }); }); }); it('should sign in with passocde - preserve returnUrl', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, expectedReturnUrl: returnUrl, params: `returnUrl=${encodedReturnUrl}`, }); }); }); it('should sign in with passcode - preserve fromURI', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, expectedReturnUrl: fromURI, params: `fromURI=${fromURI}&appClientId=${appClientId}`, }); }); }); it('selects password option to sign in from initial sign in page', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress, finalPassword }) => { cy.visit(`/signin`); cy.get('input[name=email]').type(emailAddress); cy.contains('Sign in with a password instead').click(); // password page cy.url().should('include', '/signin/password'); cy.get('input[name=email]').should('have.value', emailAddress); cy.get('input[name=password]').type(finalPassword); cy.get('[data-cy="main-form-submit-button"]').click(); cy.url().should('include', 'https://m.code.dev-theguardian.com/'); }); }); it('selects password option to sign in from the initial sign in page and show correct error page on incorrect password', () => { const emailAddress = randomMailosaurEmail(); cy.visit(`/signin`); cy.get('input[name=email]').type(emailAddress); cy.contains('Sign in with a password instead').click(); // password page cy.url().should('include', '/signin/password'); cy.get('input[name=email]').should('have.value', emailAddress); cy.get('input[name=password]').type(randomPassword()); cy.get('[data-cy="main-form-submit-button"]').click(); cy.url().should('include', '/signin/password'); cy.contains('Email and password don’t match'); }); it('selects password option to sign in from passcode page', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress, finalPassword }) => { cy.visit(`/signin?usePasscodeSignIn=true`); cy.get('input[name=email]').type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); cy.contains('sign in with a password instead').click(); // password page cy.url().should('include', '/signin/password'); cy.get('input[name=email]').should('have.value', emailAddress); cy.get('input[name=password]').type(finalPassword); cy.get('[data-cy="main-form-submit-button"]').click(); cy.url().should('include', 'https://m.code.dev-theguardian.com/'); }); }); it('selects password option to sign in from passcode page and show correct error page on incorrect password', () => { const emailAddress = randomMailosaurEmail(); cy.visit(`/signin?usePasscodeSignIn=true`); cy.get('input[name=email]').type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); cy.contains('sign in with a password instead').click(); // password page cy.url().should('include', '/signin/password'); cy.get('input[name=email]').should('have.value', emailAddress); cy.get('input[name=password]').type(randomPassword()); cy.get('[data-cy="main-form-submit-button"]').click(); cy.url().should('include', '/signin/password'); cy.contains('Email and password don’t match'); }); it('should sign in with passcode - resend email', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, additionalTests: 'resend-email', }); }); }); it('should sign in with passcode - change email', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, additionalTests: 'change-email', }); }); }); it('should sign in with passcode - passcode incorrect', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { sendEmailAndValidatePasscode({ emailAddress, additionalTests: 'passcode-incorrect', }); }); }); it('should redirect with error when multiple passcode attempts fail', () => { cy .createTestUser({ isUserEmailValidated: true, }) ?.then(({ emailAddress }) => { cy.visit(`/signin?usePasscodeSignIn=true`); cy.get('input[name=email]').clear().type(emailAddress); const timeRequestWasMade = new Date(); cy.get('[data-cy="main-form-submit-button"]').click(); cy.checkForEmailAndGetDetails(emailAddress, timeRequestWasMade).then( ({ body, codes }) => { // email expect(body).to.have.string('Your one-time passcode'); expect(codes?.length).to.eq(1); const code = codes?.[0].value; expect(code).to.match(/^\d{6}$/); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); // attempt 1 cy.contains('Sign in'); cy.get('input[name=code]').type(`${+code! + 1}`); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 2 cy.get('input[name=code]').type(`${+code! + 1}`); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 3 cy.get('input[name=code]').type(`${+code! + 1}`); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 4 cy.get('input[name=code]').type(`${+code! + 1}`); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 5 cy.get('input[name=code]').type(`${+code! + 1}`); cy.contains('Sign in').click(); cy.url().should('include', '/signin'); cy.contains('Your code has expired'); }, ); }); }); }); context('ACTIVE user - with only password authenticator', () => { it('should sign in with passcode', () => { /** * START - SETUP USER WITH ONLY PASSWORD AUTHENTICATOR */ const emailAddress = randomMailosaurEmail(); cy.visit(`/register/email`); const timeRequestWasMade = new Date(); cy.get('input[name=email]').type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); cy.contains('Enter your code'); cy.contains(emailAddress); cy.checkForEmailAndGetDetails(emailAddress, timeRequestWasMade).then( ({ body, codes }) => { // email expect(body).to.have.string('Your verification code'); expect(codes?.length).to.eq(1); const code = codes?.[0].value; expect(code).to.match(/^\d{6}$/); // passcode page cy.url().should('include', '/register/email-sent'); // make sure we don't use a passcode // we instead reset their password using classic flow to set a password cy.visit('/reset-password?useOktaClassic=true'); const timeRequestWasMade = new Date(); cy.get('input[name=email]').clear().type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); cy.checkForEmailAndGetDetails( emailAddress, timeRequestWasMade, /\/set-password\/([^"]*)/, ).then(({ links, body }) => { expect(body).to.have.string('Welcome back'); expect(body).to.have.string('Create password'); expect(links.length).to.eq(2); const setPasswordLink = links.find((s) => s.text?.includes('Create password'), ); cy.visit(setPasswordLink?.href as string); const password = randomPassword(); cy.get('input[name=password]').type(password); cy.get('[data-cy="main-form-submit-button"]') .click() .should('be.disabled'); cy.contains('Password created'); cy.contains(emailAddress.toLowerCase()); /** * END - SETUP USER WITH ONLY PASSWORD AUTHENTICATOR */ cy.visit('/signin?usePasscodeSignIn=true'); cy.contains('Sign in with a different email').click(); sendEmailAndValidatePasscode({ emailAddress, expectedEmailBody: 'Your verification code', }); }); }, ); }); }); context('non-ACTIVE user', () => { it('STAGED user - should sign in with passcode', () => { cy.createTestUser({ isGuestUser: true })?.then(({ emailAddress }) => { cy.getTestOktaUser(emailAddress).then((oktaUser) => { expect(oktaUser.status).to.eq(Status.STAGED); sendEmailAndValidatePasscode({ emailAddress, }); }); }); }); it('PROVISIONED user - should sign in with passcode', () => { cy.createTestUser({ isGuestUser: true })?.then(({ emailAddress }) => { cy.activateTestOktaUser(emailAddress).then(() => { cy.getTestOktaUser(emailAddress).then((oktaUser) => { expect(oktaUser.status).to.eq(Status.PROVISIONED); sendEmailAndValidatePasscode({ emailAddress }); }); }); }); }); it('RECOVERY user - should sign in with passcode', () => { cy.createTestUser({ isGuestUser: false })?.then(({ emailAddress }) => { cy.resetOktaUserPassword(emailAddress).then(() => { cy.getTestOktaUser(emailAddress).then((oktaUser) => { expect(oktaUser.status).to.eq(Status.RECOVERY); sendEmailAndValidatePasscode({ emailAddress }); }); }); }); }); it('PASSWORD_EXPIRED user - should sign in with passcode', () => { cy.createTestUser({ isGuestUser: false })?.then(({ emailAddress }) => { cy.expireOktaUserPassword(emailAddress).then(() => { cy.getTestOktaUser(emailAddress).then((oktaUser) => { expect(oktaUser.status).to.eq(Status.PASSWORD_EXPIRED); sendEmailAndValidatePasscode({ emailAddress }); }); }); }); }); it('NON_EXISTENT user - should show email sent page with no email sent', () => { const emailAddress = randomMailosaurEmail(); cy.visit(`/signin?usePasscodeSignIn=true`); cy.contains('Sign in'); cy.get('input[name=email]').type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); cy.contains('Don’t have an account?'); cy.contains('Sign in'); cy.get('input[name=code]').clear().type('123456'); cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); cy.contains('Don’t have an account?'); cy.contains('Incorrect code'); }); it('NON_EXISTENT user - should redirect with error when multiple passcode attempts fail', () => { const emailAddress = randomMailosaurEmail(); cy.visit(`/signin?usePasscodeSignIn=true`); cy.contains('Sign in'); cy.get('input[name=email]').type(emailAddress); cy.get('[data-cy="main-form-submit-button"]').click(); // passcode page cy.url().should('include', '/signin/code'); cy.contains('Enter your one-time code'); cy.contains('Don’t have an account?'); // attempt 1 cy.contains('Sign in'); cy.get('input[name=code]').type('123456'); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 2 cy.get('input[name=code]').type('123456'); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 3 cy.get('input[name=code]').type('123456'); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 4 cy.get('input[name=code]').type('123456'); cy.contains('Sign in').click(); cy.url().should('include', '/signin/code'); cy.contains('Incorrect code'); // attempt 5 cy.get('input[name=code]').type('123456'); cy.contains('Sign in').click(); cy.url().should('include', '/signin'); cy.contains('Your code has expired'); }); }); });