Skip to content

Latest commit

 

History

History
262 lines (215 loc) · 7.69 KB

descope-authentication.mdx

File metadata and controls

262 lines (215 loc) · 7.69 KB
title slug
Descope Authentication
/guides/end-to-end-testing/descope-authentication

:::info

What you'll learn

  • Log in to Descope via the UI
  • Programmatically authenticate with Descope via a custom Cypress command

:::

Descope Application Setup

To get started with Descope, an application needs to be setup within the Descope Dashboard via the following steps:

  1. Visit the Descope Dashboard and click the "+ Project" button under the project dropdown.
  2. Enter the desired name for your application.
  3. Hit the "Create" button

Setting Descope app credentials in Cypress

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
  },
}

:::

Custom Command for Descope Authentication

There are two ways you can authenticate to Descope:

Initialize test user & variables

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,
  })
})

Login with UI

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

  1. Navigate to the Descope login
  2. Use the Test User Management API to perform the login (create user and generate OTP code).
  3. 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')
  })
})

Programmatic Login

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:

  1. Use the Test User Management API to perform the programmatic login (create user, generate OTP code, and verify OTP code).
  2. Set the refreshToken and sessionToken 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')
  })
})