E2E Runner supports multiple auth strategies. Choose the one that matches your app.
Fill in the login form like a real user. Works with any authentication system:
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "test@example.com" },
{ "type": "type", "selector": "#password", "value": "test-password" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "selector": ".dashboard" }
]
},
"tests": [
{
"name": "profile-page",
"actions": [
{ "type": "goto", "value": "/profile" },
{ "type": "assert_text", "text": "My Profile" }
]
}
]
}Skip the login form by injecting the token into localStorage:
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/" },
{ "type": "set_storage", "value": "accessToken=eyJhbGciOiJIUzI1NiIs..." },
{ "type": "goto", "value": "/dashboard" },
{ "type": "wait", "selector": ".dashboard-loaded" }
]
},
"tests": [...]
}Common storage key names:
| Framework / Library | Typical key | Storage |
|---|---|---|
| Custom JWT | accessToken, token, jwt |
localStorage |
| Auth0 SPA SDK | @@auth0spajs@@::* |
localStorage |
| Firebase Auth | firebase:authUser:* |
localStorage |
| AWS Amplify | CognitoIdentityServiceProvider.* |
localStorage |
| Supabase | sb-<ref>-auth-token |
localStorage |
| NextAuth (client) | next-auth.session-token |
cookie (see Strategy 4) |
Using sessionStorage instead:
{ "type": "set_storage", "value": "token=eyJhbG...", "selector": "session" }Asserting the token was stored correctly:
{ "type": "assert_storage", "value": "accessToken" }
{ "type": "assert_storage", "value": "accessToken=eyJhbG..." }Set it once in config — injected into localStorage automatically:
// e2e.config.js
export default {
authToken: 'eyJhbGciOiJIUzI1NiIs...',
authStorageKey: 'accessToken', // default
};Or via environment variables:
AUTH_TOKEN="eyJhbGciOiJIUzI1NiIs..." npx e2e-runner run --allOr via CLI:
npx e2e-runner run --all --auth-token "eyJhbG..." --auth-storage-key "jwt"MCP tools (e2e_capture, e2e_issue) also accept authToken and authStorageKey per call.
For apps that use HTTP cookies (Rails, Django, Laravel, Express sessions, NextAuth):
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/" },
{ "type": "evaluate", "value": "document.cookie = 'session_id=abc123; path=/; SameSite=Lax'" },
{ "type": "goto", "value": "/dashboard" }
]
},
"tests": [...]
}For HttpOnly cookies, use the UI login strategy instead — the browser will store them automatically.
Override fetch to add Authorization headers:
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/" },
{ "type": "evaluate", "value": "const origFetch = window.fetch; window.fetch = (url, opts = {}) => { opts.headers = { ...opts.headers, 'Authorization': 'Bearer eyJhbG...' }; return origFetch(url, opts); }" }
]
},
"tests": [...]
}OAuth flows redirect to external providers (Google, GitHub, Okta) which can't be automated reliably. Workarounds:
Option A — Test environment bypass:
{ "type": "goto", "value": "/auth/test-login?user=test@example.com" }Option B — Pre-authenticated token:
{ "type": "set_storage", "value": "oidc.user:https://auth.example.com:client_id={\"access_token\":\"...\"}" }Option C — Session cookie from CI:
SESSION=$(curl -s -c - https://api.example.com/auth/login -d '{"email":"test@example.com","password":"secret"}' | grep session_id | awk '{print $NF}')
AUTH_TOKEN="$SESSION" AUTH_STORAGE_KEY="session_id" npx e2e-runner run --allExtract your auth strategy into a module:
// e2e/modules/login.json
{
"$module": "login",
"params": {
"email": { "required": true },
"password": { "required": true },
"redirectTo": { "default": "/dashboard" }
},
"actions": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "{{email}}" },
{ "type": "type", "selector": "#password", "value": "{{password}}" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "selector": "{{redirectTo}}" }
]
}Use in tests:
{ "$use": "login", "params": { "email": "admin@test.com", "password": "secret" } }[
{
"name": "admin-sees-settings",
"actions": [
{ "$use": "login", "params": { "email": "admin@test.com", "password": "admin-pass" } },
{ "type": "goto", "value": "/settings" },
{ "type": "assert_visible", "selector": ".admin-panel" }
]
},
{
"name": "viewer-cannot-access-settings",
"actions": [
{ "$use": "login", "params": { "email": "viewer@test.com", "password": "viewer-pass" } },
{ "type": "goto", "value": "/settings" },
{ "type": "assert_text", "text": "Access Denied" }
]
}
]Each test runs in a fresh browser context, so cookies and storage are automatically clean. To clear mid-test:
{ "type": "clear_cookies" }| Auth type | Strategy | Key actions |
|---|---|---|
| Username/password form | UI Login | goto + type + click in beforeEach |
| JWT in localStorage | Token Injection | set_storage in beforeEach |
| JWT in sessionStorage | Token Injection | set_storage with selector: "session" |
| Session cookies | Cookie | evaluate to set document.cookie |
| HttpOnly cookies | UI Login | Must go through login form |
| OAuth / SSO | Test bypass | App-specific test login endpoint |
| API auth headers | Header Override | evaluate to patch fetch |
| Config-level token | Config | authToken + authStorageKey in config |