cypress/integration/ete/reset_password_passcode.7.cy.ts (569 lines of code) (raw):
import { Status } from '../../../src/server/models/okta/User';
import {
randomMailosaurEmail,
randomPassword,
} from '../../support/commands/testUser';
describe('Password reset recovery flows - with Passcodes', () => {
context('ACTIVE user with password', () => {
it('allows the user to change their password', () => {
const encodedReturnUrl = encodeURIComponent(
'https://www.theguardian.com/technology/2017/may/04/nier-automata-sci-fi-game-sleeper-hit-designer-yoko-taro',
);
const encodedRef = 'https%3A%2F%2Fm.theguardian.com';
const refViewId = 'testRefViewId';
const clientId = 'jobs';
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.visit(
`/reset-password?returnUrl=${encodedReturnUrl}&ref=${encodedRef}&refViewId=${refViewId}&clientId=${clientId}`,
);
const timeRequestWasMade = new Date();
cy.contains('Reset password');
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.get('form')
.should('have.attr', 'action')
.and('match', new RegExp(encodedReturnUrl))
.and('match', new RegExp(refViewId))
.and('match', new RegExp(encodedRef))
.and('match', new RegExp(clientId));
cy.contains('Submit one-time code');
cy.get('input[name=code]').type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('form')
.should('have.attr', 'action')
.and('match', new RegExp(encodedReturnUrl))
.and('match', new RegExp(refViewId))
.and('match', new RegExp(encodedRef))
.and('match', new RegExp(clientId));
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// password complete page
cy.url()
.should('include', '/reset-password/complete')
.should('contain', encodedReturnUrl)
.should('contain', refViewId)
.should('contain', encodedRef)
.should('contain', clientId);
cy.contains('Password updated');
cy.contains('Continue to the Guardian').should(
'have.attr',
'href',
decodeURIComponent(encodedReturnUrl),
);
},
);
});
});
it('allows the user to change their password - with fromUri', () => {
const encodedReturnUrl = encodeURIComponent(
'https://www.theguardian.com/technology/2017/may/04/nier-automata-sci-fi-game-sleeper-hit-designer-yoko-taro',
);
const encodedRef = 'https%3A%2F%2Fm.theguardian.com';
const refViewId = 'testRefViewId';
const clientId = 'jobs';
const appClientId = 'appClientId1';
const fromURI = '/oauth2/v1/authorize';
// Intercept the external redirect page.
// We just want to check that the redirect happens, not that the page loads.
cy.intercept(
'GET',
`https://${Cypress.env('BASE_URI')}${decodeURIComponent(fromURI)}`,
(req) => {
req.reply(200);
},
);
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.visit(
`/reset-password?returnUrl=${encodedReturnUrl}&ref=${encodedRef}&refViewId=${refViewId}&clientId=${clientId}&appClientId=${appClientId}&fromURI=${fromURI}`,
);
const timeRequestWasMade = new Date();
cy.contains('Reset password');
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.get('form')
.should('have.attr', 'action')
.and('match', new RegExp(encodedReturnUrl))
.and('match', new RegExp(refViewId))
.and('match', new RegExp(encodedRef))
.and('match', new RegExp(clientId))
.and('match', new RegExp(appClientId))
.and('match', new RegExp(encodeURIComponent(fromURI)));
cy.contains('Submit one-time code');
cy.get('input[name=code]').type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('form')
.should('have.attr', 'action')
.and('match', new RegExp(encodedReturnUrl))
.and('match', new RegExp(refViewId))
.and('match', new RegExp(encodedRef))
.and('match', new RegExp(clientId))
.and('match', new RegExp(appClientId))
.and('match', new RegExp(encodeURIComponent(fromURI)));
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// fromURI redirect
cy.url().should('contain', decodeURIComponent(fromURI));
},
);
});
});
it('passcode incorrect functionality', () => {
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.visit(`/reset-password`);
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.contains('Enter your one-time code');
cy.contains(emailAddress);
cy.contains('send again');
cy.contains('try another address');
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
cy.get('input[name=code]').clear().type(code!);
cy.contains('Submit one-time code').click();
cy.url().should('contain', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/reset-password/complete');
},
);
});
});
it('passcode used functionality', () => {
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.visit(`/reset-password`);
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.contains('Enter your one-time code');
cy.contains(emailAddress);
cy.contains('send again');
cy.contains('try another address');
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
cy.url().should('contain', '/reset-password/password');
cy.go('back');
cy.url().should('contain', '/reset-password/email-sent');
cy.contains('Passcode verified');
cy.contains('Complete setting password').click();
cy.url().should('contain', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/reset-password/complete');
},
);
});
});
it('resend email functionality', () => {
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.setCookie('cypress-mock-state', '1'); // passcode send again timer
cy.visit(`/reset-password`);
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.contains('Enter your one-time code');
cy.contains(emailAddress);
cy.contains('send again');
cy.contains('try another address');
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
// 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('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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
cy.url().should('contain', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/reset-password/complete');
});
},
);
});
});
it('change email functionality', () => {
cy.createTestUser({
isUserEmailValidated: true,
}).then(({ emailAddress }) => {
cy.visit(`/reset-password`);
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.contains('Enter your one-time code');
cy.contains(emailAddress);
cy.contains('send again');
cy.contains('try another address');
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', '/reset-password/email-sent');
cy.contains('try another address').click();
cy.url().should('include', '/reset-password');
},
);
});
});
it('should redirect with error when multiple passcode attempts fail', () => {
cy
.createTestUser({
isUserEmailValidated: true,
})
?.then(({ emailAddress }) => {
cy.visit(`/reset-password`);
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.contains('Enter your one-time code');
cy.contains(emailAddress);
cy.contains('send again');
cy.contains('try another address');
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
// attempt 1 - auto submit
cy.contains('Submit one-time code');
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 2 - manual submit
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 3 - manual submit
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 4 - manual submit
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 5 - manual submit
cy.get('input[name=code]').type(`${+code! + 1}`);
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password');
cy.contains('Your code has expired');
},
);
});
});
it('ACTIVE user with only password authenticator - allow the user to change thier password and authenticate', () => {
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 to set a password
cy.visit('/reset-password');
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).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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
cy.url().should('contain', '/set-password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/set-password/complete');
},
);
},
);
});
});
context('STAGED user', () => {
it('allows the user to change their password - STAGED - No Passcode Verified', () => {
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');
cy.getTestOktaUser(emailAddress).then((oktaUser) => {
expect(oktaUser.status).to.eq(Status.STAGED);
// make sure we don't use a passcode
// we instead reset their password using passcodes
cy.visit('/reset-password');
const timeRequestWasMade = new Date();
cy.contains('Reset password');
cy.get('[data-cy="main-form-submit-button"]').click();
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
cy.url().should('contain', '/set-password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/set-password/complete');
});
});
},
);
});
it('allows the user to change their password - STAGED - Created via Classic API (i.e guest user)', () => {
cy.createTestUser({ isGuestUser: true })?.then(({ emailAddress }) => {
cy.getTestOktaUser(emailAddress).then((oktaUser) => {
expect(oktaUser.status).to.eq(Status.STAGED);
cy.visit('/reset-password');
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// password complete page
cy.url().should('include', '/reset-password/complete');
cy.contains('Password updated');
},
);
});
});
});
});
context('PROVISIONED user', () => {
it('allows the user to change their password - PROVISIONED', () => {
cy.createTestUser({ isGuestUser: true })?.then(({ emailAddress }) => {
cy.activateTestOktaUser(emailAddress).then(() => {
cy.getTestOktaUser(emailAddress).then((oktaUser) => {
expect(oktaUser.status).to.eq(Status.PROVISIONED);
cy.visit('/reset-password');
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// password complete page
cy.url().should('include', '/reset-password/complete');
cy.contains('Password updated');
});
});
});
});
});
});
context('RECOVERY user', () => {
it('allows the user to change their password - RECOVERY', () => {
cy.createTestUser({ isGuestUser: false })?.then(({ emailAddress }) => {
cy.resetOktaUserPassword(emailAddress).then(() => {
cy.getTestOktaUser(emailAddress).then((oktaUser) => {
expect(oktaUser.status).to.eq(Status.RECOVERY);
cy.visit('/reset-password');
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// password complete page
cy.url().should('include', '/reset-password/complete');
cy.contains('Password updated');
});
});
});
});
});
});
context('PASSWORD_EXPIRED user', () => {
it('allows the user to change their password - PASSWORD_EXPIRED', () => {
cy.createTestUser({ isGuestUser: false })?.then(({ emailAddress }) => {
cy.expireOktaUserPassword(emailAddress).then(() => {
cy.getTestOktaUser(emailAddress).then((oktaUser) => {
expect(oktaUser.status).to.eq(Status.PASSWORD_EXPIRED);
cy.visit('/reset-password');
const timeRequestWasMade = new Date();
cy.get('input[name=email]').type(emailAddress);
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', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type(code!);
// password page
cy.url().should('include', '/reset-password/password');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
// password complete page
cy.url().should('include', '/reset-password/complete');
cy.contains('Password updated');
});
});
});
});
});
});
context('NON_EXISTENT user', () => {
it('shows the passcode page with no account info, and using passcode returns error', () => {
const emailAddress = randomMailosaurEmail();
cy.visit(`/reset-password`);
cy.contains('Reset password');
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
// passcode page
cy.url().should('include', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Don’t have an account?');
cy.contains('Submit one-time code');
cy.get('input[name=code]').clear().type('123456');
cy.url().should('include', '/reset-password/code');
cy.contains('Enter your one-time code');
cy.contains('Don’t have an account?');
cy.contains('Incorrect code');
});
it('should redirect with error when multiple passcode attempts fail', () => {
const emailAddress = randomMailosaurEmail();
cy.visit(`/reset-password`);
cy.contains('Reset password');
cy.get('input[name=email]').type(emailAddress);
cy.get('[data-cy="main-form-submit-button"]').click();
// passcode page
cy.url().should('include', '/reset-password/email-sent');
cy.contains('Enter your one-time code');
cy.contains('Don’t have an account?');
// attempt 1
cy.contains('Submit one-time code');
cy.get('input[name=code]').type('123456');
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 2
cy.get('input[name=code]').type('123456');
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 3
cy.get('input[name=code]').type('123456');
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 4
cy.get('input[name=code]').type('123456');
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password/code');
cy.contains('Incorrect code');
// attempt 5
cy.get('input[name=code]').type('123456');
cy.contains('Submit one-time code').click();
cy.url().should('include', '/reset-password');
cy.contains('Your code has expired');
});
});
});