title | slug |
---|---|
Descope Authentication |
/guides/end-to-end-testing/descope-authentication |
:::info
- Log in to Descope via the UI
- Programmatically authenticate with Descope via a custom Cypress command
:::
To get started with Descope, an application needs to be setup within the Descope Dashboard via the following steps:
- Visit the Descope Dashboard and click the "+ Project" button under the project dropdown.
- Enter the desired name for your application.
- Hit the "Create" button
To have access to test user credentials within our tests we need to configure
Cypress to use the Descope environment variables set in the
.env
file.
:::cypress-config-example
// Populate process.env with values from .env file
require('dotenv').config()
{
e2e: {
includeShadowDom: true, // For interacting with Descope components
},
env: {
descope_project_id: process.env.REACT_APP_DESCOPE_PROJECT_ID,
descope_management_key: process.env.REACT_APP_DESCOPE_MANAGEMENT_KEY
},
}
:::
There are two ways you can authenticate to Descope:
For both UI and programatic login, you'll need to initialize a test user, get your env variables, and create a delete users command.
We'll do this in the cypress/support/commands.js
file.
// cypress/support/commands.js
const projectId = Cypress.env('descope_project_id')
const managementKey = Cypress.env('descope_management_key')
const descopeAPIDomain = 'api.descope.com'
// Define the authorization header
const authHeader = {
Authorization: `Bearer ${projectId}:${managementKey}`,
}
// Define the base URL for Descope API
const descopeApiBaseURL = `https://${descopeAPIDomain}/v1`
const testUserLoginId =
'testUser' + Math.floor(1000 + Math.random() * 9000) + '@gmail.com' // Must match email to pass validation
// Define the test user details
const testUser = {
loginId: testUserLoginId,
email: testUserLoginId,
phone: '+11231231234',
verifiedEmail: true,
verifiedPhone: true,
displayName: 'Test User',
test: true,
}
To clean up the created test users so we don’t go over the test user limit,
we'll have a deleteAllTestUsers
function.
// cypress/support/commands.js
Cypress.Commands.add('deleteAllTestUsers', () => {
cy.request({
method: 'DELETE',
url: `${descopeApiBaseURL}/mgmt/user/test/delete/all`,
headers: authHeader,
})
})
Next, we'll write a custom command called loginViaDescopeUI
to perform a login to
Descope using the Test User Management API
and navigating via the user interface. This command will
- Navigate to the Descope login
- Use the Test User Management API to perform the login (create user and generate OTP code).
- Enter the user login ID and code that we just generated to log in via the user interface.
// cypress/support/commands.js
Cypress.Commands.add('loginViaDescopeUI', () => {
cy.request({
method: 'POST',
url: `${descopeApiBaseURL}/mgmt/user/create`,
headers: authHeader,
body: testUser,
}).then(({ body }) => {
const loginId = body['user']['loginIds'][0]
cy.request({
method: 'POST',
url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`,
headers: authHeader,
body: {
loginId: loginId,
deliveryMethod: 'email',
},
}).then(({ body }) => {
const otpCode = body['code']
const loginID = body['loginId']
cy.visit('/login')
cy.get('descope-wc').find('input').type(loginID)
cy.get('descope-wc').find('button').contains('Continue').click()
cy.get('descope-wc')
.find('.descope-input-wrapper')
.find('input')
.should('exist') // Assertion added to wait for the OTP code input to appear
let otpCodeArray = Array.from(otpCode) // Convert the OTP code string to an array
for (var i = 0; i < otpCodeArray.length; i++) {
cy.get('descope-wc')
.find('.descope-input-wrapper')
.find('input')
.eq(i + 1)
.type(otpCodeArray[i], { force: true })
}
cy.get('descope-wc').find('button').contains('Submit').click()
// Customize these steps based on your authentication flow
})
})
})
Now, we can use our loginViaDescopeUI
command in the test. Below is our test to
login as a user via Descope and run a basic sanity check.
describe('Descope', function () {
beforeEach(function () {
cy.deleteAllTestUsers()
cy.loginViaDescopeUI()
cy.visit('/')
})
it('shows welcome page', function () {
cy.contains('Welcome').should('be.visible')
})
})
We'll now write a command to programmatically login into
Descope using the Test User Management API
and set an item in localStorage
with the authenticated users details, which we
will use in our application code to verify we are authenticated under test.
The loginViaDescopeAPI
command will execute the following steps:
- Use the Test User Management API to perform the programmatic login (create user, generate OTP code, and verify OTP code).
- Set the
refreshToken
andsessionToken
items in localStorage.
// cypress/support/commands.js
Cypress.Commands.add('loginViaDescopeAPI', () => {
cy.request({
method: 'POST',
url: `${descopeApiBaseURL}/mgmt/user/create`,
headers: authHeader,
body: testUser,
}).then(({ body }) => {
const loginId = body['user']['loginIds'][0]
cy.request({
method: 'POST',
url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`,
headers: authHeader,
body: {
loginId: loginId,
deliveryMethod: 'email',
},
}).then(({ body }) => {
const otpCode = body['code']
cy.request({
method: 'POST',
url: `${descopeApiBaseURL}/auth/otp/verify/email`,
headers: authHeader,
body: {
loginId: loginId,
code: otpCode,
},
}).then(({ body }) => {
const sessionJwt = body['sessionJwt']
const refreshJwt = body['refreshJwt']
/** Default name for the session cookie name / local storage key */
const SESSION_TOKEN_KEY = 'DS'
/** Default name for the refresh local storage key */
const REFRESH_TOKEN_KEY = 'DSR'
// // Store the JWT in the browser's local storage.
cy.window().then((win) => {
win.localStorage.setItem(SESSION_TOKEN_KEY, sessionJwt)
win.localStorage.setItem(REFRESH_TOKEN_KEY, refreshJwt)
})
// // Now navigate to the root URL of your application.
cy.visit('/')
})
})
})
})
With our Descope app setup properly in the Descope Developer console,
necessary environment variables in place, and our
loginViaDescopeAPI
command implemented, we will be able to authenticate
with Descope while our app is under test. Below is
a test to login as a user using our loginViaDescopeAPI
function and verify the welcome page is showing.
describe('Descope', function () {
beforeEach(function () {
cy.deleteAllTestUsers()
cy.loginViaDescopeAPI()
})
it('shows welcome page', function () {
cy.contains('Welcome').should('be.visible')
})
})