Status: shipped 2026-08-04.
@signet/testing is a dependency-free, repository-owned helper package for Playwright, Cypress, and API tests. It signs in through the ordinary POST /sign-in/email wire, reads the signed session credential exposed by Signet's certified bearer plugin, and installs the ordinary better-auth.session_token cookie. The resulting browser state is the same state an application gets after a real sign-in.
This is deliberately not a privileged "testing token." Clerk's testing token bypasses its edge bot detection; authentication is a separate operation. Signet has no in-binary bot scorer or challenge to bypass, so inventing a bypass token would add a production backdoor without solving a Signet problem. The helper cannot create users, ignore a ban or lockout, skip email admission, disable rate limits, bypass MFA, extend a session, or mint a session from an admin secret.
The package is marked private and is not published to npm under the proprietary product name. Install it from this repository or a vendored workspace path, for example:
{
"devDependencies": {
"@signet/testing": "file:../vendor/signet-testing"
}
}baseURL always means the full Signet auth API URL, including the configured base path: https://auth.example.com/api/auth.
Playwright
Use a dedicated test account created through your normal fixture/seed process. The password still passes every configured Signet policy.
import { test } from "@playwright/test"
import { signInAndInstallSession } from "@signet/testing/playwright"
test.beforeEach(async ({ context }) => {
await signInAndInstallSession({
context,
baseURL: process.env.SIGNET_AUTH_URL!,
email: process.env.E2E_USER_EMAIL!,
password: process.env.E2E_USER_PASSWORD!,
})
})The request returns the set-auth-token value that Signet exposes from the real signed session cookie. The adapter installs it as an HttpOnly, SameSite=Lax cookie on the authentication origin; Secure follows the URL scheme. It does not write the raw database session token.
Cypress
import { signInAndInstallSession } from "@signet/testing/cypress"
beforeEach(() => {
signInAndInstallSession({
cy,
baseURL: Cypress.env("SIGNET_AUTH_URL"),
email: Cypress.env("E2E_USER_EMAIL"),
password: Cypress.env("E2E_USER_PASSWORD"),
})
})Signet's authentication origin must be in the application's trusted-origin/CORS configuration. If the application and auth service use different origins, follow Cypress's current cross-origin test rules (cy.origin) for any subsequent browser commands which visit the auth origin. The cookie itself is installed for the host named by SIGNET_AUTH_URL, not for the application host.
API tests and cleanup
The core export is runner-independent:
import {
authorizationHeader,
signInWithPassword,
signOut,
} from "@signet/testing"
const session = await signInWithPassword({
baseURL: process.env.SIGNET_AUTH_URL!,
email: process.env.E2E_USER_EMAIL!,
password: process.env.E2E_USER_PASSWORD!,
})
const response = await fetch(`${process.env.SIGNET_AUTH_URL}/get-session`, {
headers: authorizationHeader(session.token),
})
await signOut({ baseURL: process.env.SIGNET_AUTH_URL!, token: session.token })signOut revokes the server-side session, not merely the local cookie. Use it in teardown when a test does not discard its entire database. Never print the returned token or put credentials in a committed fixture.
MFA is an explicit test boundary
Password sign-in for an MFA-enabled account creates a second-factor challenge rather than a session. The helper stops with TWO_FACTOR_REQUIRED and tells the caller to use either a dedicated non-MFA E2E account or the real TOTP/OTP/backup-code UI flow. It never completes or disables the factor behind the application's back. Tests for step-up or MFA behavior should drive those routes directly instead of using this setup shortcut.