Last Updated: Oct 31, 2024
Overview
The user is building a Next.js application with protected pages and API routes and is looking for guidance on how to completely isolate the automated test from Auth0.
Is it possible to generate a mock appSession
cookie to be used for test purposes?
Applies To
Next.js application
Session Cookie
Solution
The appSession cookie should be used by the SDK to establish a local session and avoid needing to call the Auth0 endpoints unnecessarily. For mocking the session cookie, use this utility to mock the Auth0 authentication flow for E2E tests:
auth0:vNext
โ auth0:feature/testing-helper
opened 05:43AM - 10 Sep 22 UTC
<!--
โ For general support or usage questions, use the Auth0 Community forums oโฆ r raise a support ticket.
By submitting a pull request to this repository, you agree to the terms within the Auth0 Code of Conduct: https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md.
-->
- [X] All new/changed/fixed functionality is covered by tests (or N/A)
- [X] I have added documentation for all new/changed functionality (or N/A)
<!--
โ All the above items are required. Pull requests with an incomplete or missing checklist will be closed.
-->
### ๐ Changes
This PR adds a the `generateSessionCookie` helper function for generating session cookies, which can be used to mock the Auth0 authentication flow in e2e tests.
#### Cypress Example
`generateSessionCookie` can only run on Node.js, not on the browser, so it must be wrapped in a Cypress [task](https://docs.cypress.io/api/commands/task):
```ts
/// <reference types="cypress" />
import { Session } from '@auth0/nextjs-auth0';
import { generateSessionCookie, GenerateSessionCookieConfig } from '@auth0/nextjs-auth0/testing';
module.exports = (on: any) => {
on('task', {
getSessionCookie(params: { session: Session; config: GenerateSessionCookieConfig }) {
const { session, config } = params;
return generateSessionCookie(session, config);
}
});
};
```
That task can be invoked from a custom command:
```ts
/// <reference types="cypress" />
const cookieName = 'appSession';
Cypress.Commands.add('login', (email: string, password: string) => {
const tokenEndpoint = `${Cypress.env('AUTH0_ISSUER_BASE_URL')}oauth/token`;
const clientId = Cypress.env('AUTH0_CLIENT_ID');
const clientSecret = Cypress.env('AUTH0_CLIENT_SECRET');
const audience = Cypress.env('AUTH0_AUDIENCE');
const scope = Cypress.env('AUTH0_SCOPE');
const cookieSecret = Cypress.env('AUTH0_SECRET');
const options = {
body: {
client_id: clientId,
client_secret: clientSecret,
audience: audience,
scope,
username: email,
password,
grant_type: 'http://auth0.com/oauth/grant-type/password-realm',
realm: 'Username-Password-Authentication'
},
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
url: tokenEndpoint
};
// Use the Resource Owner Password Flow to get the test user's access token
cy.request(options).then(async ({ body }) => {
const { access_token: accessToken } = body;
// Invoke the task
cy.task('getSessionCookie', {
session: { accessToken, user: { email } },
config: { secret: cookieSecret }
}).then((cookie) => {
// Set the cookie
cy.setCookie(cookieName, cookie as string);
});
});
});
Cypress.Commands.add('logout', () => {
cy.clearCookie(cookieName);
});
```
Then, this custom command can be used on test suites to log the test user in:
```ts
before(() => {
cy.login(EMAIL, PASSWORD);
cy.visit('/');
});
after(() => {
cy.logout();
});
```
### ๐ References
Fixes https://github.com/auth0/nextjs-auth0/issues/335 https://github.com/auth0/nextjs-auth0/issues/548
### ๐ฏ Testing
Besides adding unit tests, the changes were tested manually on the [sample app](https://github.com/auth0-samples/auth0-nextjs-samples/tree/main/Sample-01):
<img width="448" alt="Screen Shot 2022-09-10 at 02 37 38" src="https://user-images.githubusercontent.com/5055789/189470827-3bec75cc-726d-40ba-b1f9-c3f930f9162a.png">
There is also some more general testing advice for the Next.js SDK here:
# Testing
By default, the SDK creates and manages a singleton instance to run for the lifetime of the application. When testing your application, you may need to reset this instance, so its state does not leak between tests.
If you're using Jest, we recommend using `jest.resetModules()` after each test. Alternatively, you can look at [creating your own instance of the SDK](./EXAMPLES.md#create-your-own-instance-of-the-sdk), so it can be recreated between tests.
For end to end tests, have a look at how we use a [mock OIDC Provider](./scripts/oidc-provider.js).