Test passwordless flows
This guide shows you how to automate passwordless authentication testing with Cypress using Mailosaur. Learn more about testing passwordless flows with Kinde.
What you need
Link to this section- Completed the Cypress setup
- A test user and a running Kinde application.
- A Mailosaur account (Sign up for free trial)
Setup Mailosaur
Link to this section-
Sign in to your Mailosaur account.
-
Copy the Server ID from the dashboard.
-
Go to API Keys > select Create standard key and copy the value.
-
Install the Mailosaur client in your Cypress project:
Terminal window npm install mailosaur --save-dev -
Update your
cypress.env.jsonfile with your Mailosaur API key and Server ID:{// ...your other environment variables..."MAILOSAUR_API_KEY": "your-api-key","MAILOSAUR_SERVER_ID": "your-server-id"}ImportantMake sure to add the
cypress.env.jsonfile to your.gitignorefile to prevent committing sensitive information to source control. -
Create a helper utility file
cypress/support/utils/mailosaur.js:Terminal window mkdir -p cypress/support/utilstouch cypress/support/utils/mailosaur.js -
Add the following code to
mailosaur.js:cypress/support/utils/mailosaur.js const MailosaurClient = require('mailosaur')async function getOTPFromEmail(email, apiKey, serverId) {const mailosaur = new MailosaurClient(apiKey)const message = await mailosaur.messages.get(serverId, { sentTo: email })const otpMatch = message.text?.body?.match(/\b(\d{6})\b/)const otpCode = otpMatch?.[1]if (!otpCode) {throw new Error("Could not extract OTP code from email")}return {emailId: message.id,otpCode,}}async function deleteEmail(emailId, apiKey) {const mailosaur = new MailosaurClient(apiKey)await mailosaur.messages.del(emailId)}module.exports = {getOTPFromEmail,deleteEmail,} -
Add the following tasks to your
cypress.config.jsfile:cypress.config.js const { defineConfig } = require('cypress')const { getOTPFromEmail, deleteEmail } = require('./cypress/support/utils/mailosaur')module.exports = defineConfig({e2e: {baseUrl: 'http://localhost:3000',setupNodeEvents(on, config) {on('task', {async getOTPFromEmail({ email, apiKey, serverId }) {return await getOTPFromEmail(email, apiKey, serverId)},async deleteEmail({ emailId, apiKey }) {await deleteEmail(emailId, apiKey)return null},})},},})
Test passwordless sign-up flow
Link to this sectionTo test the passwordless sign-up flow, enable the Email + code option in authentication methods in your Kinde application.
-
Create a new test file:
Terminal window touch cypress/e2e/passwordless-signup.cy.js -
Add the following code:
cypress/e2e/passwordless-signup.cy.js const serverId = Cypress.env('MAILOSAUR_SERVER_ID')describe('Passwordless Authentication Flows', () => {it('user can sign up with email and code', () => {// Generate unique email for this test runconst timestamp = Date.now()const testEmail = `test-${timestamp}@${serverId}.mailosaur.net`cy.visit(Cypress.env('TEST_APP_URL'))cy.get('[data-testid="sign-up-button"]').click()// Wait for Kinde sign-up pagecy.url().should('include', 'kinde.com')// Fill sign-up formcy.origin(Cypress.env('KINDE_ISSUER_URL'), { args: { testEmail } }, ({ testEmail }) => {cy.get('input[name="p_first_name"]').type('John')cy.get('input[name="p_last_name"]').type('Doe')cy.get('input[name="p_email"]').type(testEmail)cy.get('input[name="p_has_clickwrap_accepted"]').check() // Accept terms and conditionscy.get('button[type="submit"]').click()})// Wait for OTP email and extract codecy.task('getOTPFromEmail', {email: testEmail,apiKey: Cypress.env('MAILOSAUR_API_KEY'),serverId: Cypress.env('MAILOSAUR_SERVER_ID')}).then((result) => {cy.origin(Cypress.env('KINDE_ISSUER_URL'), { args: { otpCode: result.otpCode } }, ({ otpCode }) => {cy.get('input[name="p_confirmation_code"]').type(otpCode)cy.get('[data-kinde-button-variant="primary"]').click()})// Wait for successful authcy.url().should('include', new URL(Cypress.env('TEST_APP_URL')).hostname)cy.get('[data-testid="user-profile"]').should('be.visible')// Clean up OTP emailcy.task('deleteEmail', {emailId: result.emailId,apiKey: Cypress.env('MAILOSAUR_API_KEY')})})})})
Test passwordless sign-in flow
Link to this sectionTo test the passwordless sign-in flow:
-
Enable the Email + code option in Authentication methods.
-
Sign up for a new test user using the Mailosaur domain (e.g., test-123@your-server-id.mailosaur.net).
-
Save the test user email to your
cypress.env.jsonfile asTEST_USER_EMAIL. -
Create a new test file:
Terminal window touch cypress/e2e/passwordless-signin.cy.js -
Add the following code:
cypress/e2e/passwordless-signin.cy.js describe('Passwordless Authentication Flows', () => {it('user can sign in with email and code', () => {cy.visit(Cypress.env('TEST_APP_URL'))cy.get('[data-testid="sign-in-button"]').click()// Wait for Kinde sign-in pagecy.url().should('include', 'kinde.com')// Fill sign-in formcy.origin(Cypress.env('KINDE_ISSUER_URL'), () => {cy.get('input[name="p_email"]').type(Cypress.env('TEST_USER_EMAIL'))cy.get('[data-kinde-button-variant="primary"]').click()})// Wait for OTP email and extract codecy.task('getOTPFromEmail', {email: Cypress.env('TEST_USER_EMAIL'),apiKey: Cypress.env('MAILOSAUR_API_KEY'),serverId: Cypress.env('MAILOSAUR_SERVER_ID')}).then((result) => {cy.origin(Cypress.env('KINDE_ISSUER_URL'), { args: { otpCode: result.otpCode } }, ({ otpCode }) => {cy.get('input[name="p_confirmation_code"]').type(otpCode)cy.get('[data-kinde-button-variant="primary"]').click()})// Wait for successful authcy.url().should('include', new URL(Cypress.env('TEST_APP_URL')).hostname)cy.get('[data-testid="user-profile"]').should('be.visible')// Clean up OTP emailcy.task('deleteEmail', {emailId: result.emailId,apiKey: Cypress.env('MAILOSAUR_API_KEY')})})})})
Test sign-up flow with email and password
Link to this sectionWe have included this example because OTP verification is required for all new users in Kinde regardless of the authentication method used.
This example uses the Kinde email + password authentication method to sign up a new test user.
-
Create a new test file:
Terminal window touch cypress/e2e/email-password-signup.cy.js -
Add the following code:
cypress/e2e/email-password-signup.cy.js const serverId = Cypress.env('MAILOSAUR_SERVER_ID')describe('Authentication Flows', () => {it('user can sign up with email and password', () => {// Generate unique email for this test runconst timestamp = Date.now()const testEmail = `test-${timestamp}@${serverId}.mailosaur.net`cy.visit(Cypress.env('TEST_APP_URL'))cy.get('[data-testid="sign-up-button"]').click()// Wait for Kinde sign-up pagecy.url().should('include', 'kinde.com')// Fill sign-up formcy.origin(Cypress.env('KINDE_ISSUER_URL'), { args: { testEmail } }, ({ testEmail }) => {cy.get('input[name="p_first_name"]').type('John')cy.get('input[name="p_last_name"]').type('Doe')cy.get('input[name="p_email"]').type(testEmail)cy.get('input[name="p_has_clickwrap_accepted"]').check() // Accept terms and conditionscy.get('[data-kinde-button-variant="primary"]').click()})// Wait for OTP emailcy.task('getOTPFromEmail', {email: testEmail,apiKey: Cypress.env('MAILOSAUR_API_KEY'),serverId: Cypress.env('MAILOSAUR_SERVER_ID')}).then((result) => {cy.origin(Cypress.env('KINDE_ISSUER_URL'), { args: { otpCode: result.otpCode } }, ({ otpCode }) => {cy.get('input[name="p_confirmation_code"]').type(otpCode)cy.get('[data-kinde-button-variant="primary"]').click()})// Enter passwordcy.origin(Cypress.env('KINDE_ISSUER_URL'), () => {cy.get('input[name="p_first_password"]').type(Cypress.env('TEST_USER_PASSWORD'))cy.get('input[name="p_second_password"]').type(Cypress.env('TEST_USER_PASSWORD'))cy.get('[data-kinde-button-variant="primary"]').click()})// Wait for successful authcy.url().should('include', new URL(Cypress.env('TEST_APP_URL')).hostname)cy.get('[data-testid="user-profile"]').should('be.visible')// Clean up OTP emailcy.task('deleteEmail', {emailId: result.emailId,apiKey: Cypress.env('MAILOSAUR_API_KEY')})})})})
Run the tests
Link to this section-
Use the following command:
Terminal window npx cypress open -
Select the test spec to run.
You should now see the Cypress tests passing in the browser.
-
Go to your Kinde dashboard > Users to find the test users you created.