Guides
Auth strategies and storage state
Log in once per user, reuse the browser state across scenarios, and capture state for recording.
What you'll learn
Which authentication strategies SDODS supports, where storage state is cached, how a leased user's state is applied to a browser context, and how to capture state for the recorder.
Strategies
| Strategy | How state is produced |
|---|---|
none | no login |
form | a headless login with the selectors in auth.form, then context.storageState() |
token | fetch a token, place it as a header, cookie or local-storage entry |
oauth-client-credentials | client-credentials grant against tokenUrl |
sso | interactive: sdods auth capture --interactive opens codegen so a person completes SSO |
custom | your defineAuth implementation in steps/auth.ts |
auth:
strategy: form
storageState: true
maxAgeMinutes: 120
form:
loginPath: /
usernameSelector: '#user-name'
passwordSelector: '#password'
submitSelector: '#login-button'
readyUrl: /inventory.htmlWhere state lives
projects/demo-shop/.auth/staging/standard-0.json # gitignored
projects/demo-shop/.auth/staging/standard-1.jsonThe index is the pool position of the user. A sidecar records when the state was captured; the fixture recaptures form and token state after maxAgeMinutes.
Capture state on demand
bun run sdods auth capture -p demo-shop -e staging --user standard
bun run sdods auth capture -p demo-shop -e staging --user standard --all
bun run sdods auth capture -p demo-shop -e staging --user admin --interactive
bun run sdods auth list -p demo-shop -e stagingThe recorder uses the same files through --load-storage, so you can record flows as any pool user.
Custom strategy
import { defineAuth } from '@sdods/core/auth';
export const auth = defineAuth({
strategy: 'custom',
async login({ browser, config, user }) {
const context = await browser.newContext({ baseURL: config.env.ui.baseUrl });
const page = await context.newPage();
await page.goto('/');
await page.getByLabel('Username').fill(user.username);
await page.getByLabel('Password').fill(user.password);
await page.getByRole('button', { name: 'Login' }).click();
await page.waitForURL('**/inventory.html');
return context; // SDODS saves and closes it
},
});