14 KiB
Clerk Authentication - Testing Guide
Last Updated: 2025-10-28 Source: https://clerk.com/docs/guides/development/testing/overview
This guide covers testing Clerk authentication in your applications, including test credentials, session tokens, testing tokens, and E2E testing with Playwright.
Overview
Testing authentication flows is essential for reliable applications. Clerk provides several tools to make testing easier:
- Test Emails & Phone Numbers - Fake credentials with fixed OTP codes
- Session Tokens - Generate valid tokens via Backend API
- Testing Tokens - Bypass bot detection in test suites
- Framework Integrations - Playwright and Cypress helpers
Quick Start: Test Mode
Enable Test Mode
Every development instance has test mode enabled by default.
For production instances (NOT recommended for real customer data):
- Navigate to Clerk Dashboard → Settings
- Enable the Enable test mode toggle
Warning
: Never use test mode on instances managing actual customers.
Test Emails & Phone Numbers
Fake Email Addresses
Any email with the +clerk_test subaddress is a test email address:
jane+clerk_test@example.com
john+clerk_test@gmail.com
test+clerk_test@mycompany.com
Behavior:
- ✅ No emails sent (saves your email quota)
- ✅ Fixed OTP code:
424242 - ✅ Works in all sign-up/sign-in flows
Fake Phone Numbers
Any fictional phone number is a test phone number:
Format: +1 (XXX) 555-0100 to +1 (XXX) 555-0199
Examples:
+12015550100
+19735550133
+14155550142
Behavior:
- ✅ No SMS sent (saves your SMS quota)
- ✅ Fixed OTP code:
424242 - ✅ Works in all verification flows
Monthly Limits (Development Instances)
Clerk development instances have limits:
- 20 SMS messages per month
- 100 emails per month
Excluded from limits:
- SMS to US numbers
- SMS/emails to test addresses (with
+clerk_testor 555 numbers) - Self-delivered messages (your own SMTP/SMS provider)
- Paid subscription apps
To request higher limits, contact Clerk support.
Code Examples: Test Credentials
Sign In with Test Email
import { useSignIn } from '@clerk/clerk-react'
const testSignInWithEmailCode = async () => {
const { signIn } = useSignIn()
const emailAddress = 'john+clerk_test@example.com'
// Step 1: Create sign-in attempt
const signInResp = await signIn.create({ identifier: emailAddress })
// Step 2: Find email code factor
const { emailAddressId } = signInResp.supportedFirstFactors.find(
(ff) => ff.strategy === 'email_code' && ff.safeIdentifier === emailAddress,
)! as EmailCodeFactor
// Step 3: Prepare email verification
await signIn.prepareFirstFactor({
strategy: 'email_code',
emailAddressId: emailAddressId,
})
// Step 4: Verify with fixed code
const attemptResponse = await signIn.attemptFirstFactor({
strategy: 'email_code',
code: '424242', // Fixed test code
})
if (attemptResponse.status === 'complete') {
console.log('Sign in successful!')
} else {
console.error('Sign in failed')
}
}
Sign Up with Test Phone
import { useSignUp } from '@clerk/clerk-react'
const testSignUpWithPhoneNumber = async () => {
const { signUp } = useSignUp()
// Step 1: Create sign-up with test phone
await signUp.create({
phoneNumber: '+12015550100',
})
// Step 2: Prepare phone verification
await signUp.preparePhoneNumberVerification()
// Step 3: Verify with fixed code
const res = await signUp.attemptPhoneNumberVerification({
code: '424242', // Fixed test code
})
if (res.verifications.phoneNumber.status === 'verified') {
console.log('Sign up successful!')
} else {
console.error('Sign up failed')
}
}
Session Tokens (Backend API)
For testing API endpoints or backend services, you need valid session tokens.
Flow: Generate Session Token
4-Step Process:
- Create User (if needed)
- Create Session for user
- Create Session Token from session ID
- Use Token in Authorization header
Step-by-Step Implementation
Step 1: Create User
Endpoint: POST https://api.clerk.com/v1/users
curl -X POST https://api.clerk.com/v1/users \
-H "Authorization: Bearer sk_test_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"email_address": ["test+clerk_test@example.com"],
"password": "TestPassword123!"
}'
Response:
{
"id": "user_2abc123def456",
"email_addresses": [
{
"id": "idn_2xyz789",
"email_address": "test+clerk_test@example.com"
}
]
}
Save: user_id for next step
Step 2: Create Session
Endpoint: POST https://api.clerk.com/v1/sessions
curl -X POST https://api.clerk.com/v1/sessions \
-H "Authorization: Bearer sk_test_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_2abc123def456"
}'
Response:
{
"id": "sess_2xyz789abc123",
"user_id": "user_2abc123def456",
"status": "active"
}
Save: session_id for next step
Step 3: Create Session Token
Endpoint: POST https://api.clerk.com/v1/sessions/{session_id}/tokens
curl -X POST https://api.clerk.com/v1/sessions/sess_2xyz789abc123/tokens \
-H "Authorization: Bearer sk_test_YOUR_SECRET_KEY"
Response:
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"object": "token"
}
Save: jwt token
Step 4: Use Token in Requests
curl https://yourdomain.com/api/protected \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Token Lifetime
CRITICAL: Session tokens are valid for 60 seconds only.
Refresh Strategies:
Option 1: Before Each Test
beforeEach(async () => {
sessionToken = await refreshSessionToken(sessionId)
})
Option 2: Interval Timer
setInterval(async () => {
sessionToken = await refreshSessionToken(sessionId)
}, 50000) // Refresh every 50 seconds
Node.js Script Example
See scripts/generate-session-token.js for a complete implementation.
Testing Tokens (Bot Detection Bypass)
Testing Tokens bypass Clerk's bot detection mechanisms during automated testing.
What Are Testing Tokens?
- Purpose: Prevent "Bot traffic detected" errors in test suites
- Lifetime: Short-lived (expires after use)
- Scope: Valid only for specific Clerk instance
- Source: Obtained via Backend API
When to Use
Use Testing Tokens when:
- Running E2E tests with Playwright or Cypress
- Automated test suites triggering bot detection
- CI/CD pipelines running authentication flows
Alternatives:
- Use
@clerk/testingpackage (handles automatically) - Playwright integration (recommended)
- Cypress integration (recommended)
Obtain Testing Token
Endpoint: POST https://api.clerk.com/v1/testing_tokens
curl -X POST https://api.clerk.com/v1/testing_tokens \
-H "Authorization: Bearer sk_test_YOUR_SECRET_KEY"
Response:
{
"token": "1713877200-c_2J2MvPu9PnXcuhbPZNao0LOXqK9A7YrnBn0HmIWxy"
}
Use Testing Token
Add __clerk_testing_token query parameter to Frontend API requests:
POST https://happy-hippo-1.clerk.accounts.dev/v1/client/sign_ups?__clerk_testing_token=1713877200-c_2J2MvPu9PnXcuhbPZNao0LOXqK9A7YrnBn0HmIWxy
Production Limitations
Testing Tokens work in both development and production, but:
❌ Not Supported in Production:
- Code-based authentication (SMS OTP, Email OTP)
✅ Supported in Production:
- Email + password authentication
- Email magic link (sign-in via email)
E2E Testing with Playwright
Clerk provides first-class Playwright support via @clerk/testing.
Install
npm install -D @clerk/testing
Set Environment Variables
In your test runner (e.g., .env.test or GitHub Actions secrets):
CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
Security: Use GitHub Actions secrets or similar for CI/CD.
Global Setup
Configure clerkSetup() to obtain Testing Token once for all tests:
File: global.setup.ts
import { clerkSetup } from '@clerk/testing/playwright'
import { test as setup } from '@playwright/test'
// Run setup serially (important for fully parallel Playwright config)
setup.describe.configure({ mode: 'serial' })
setup('global setup', async ({}) => {
await clerkSetup()
})
What This Does:
- Obtains Testing Token from Clerk Backend API
- Stores token in
CLERK_TESTING_TOKENenvironment variable - Makes token available for all tests
Use in Tests
Import setupClerkTestingToken() and call before navigating to auth pages:
File: auth.spec.ts
import { setupClerkTestingToken } from '@clerk/testing/playwright'
import { test, expect } from '@playwright/test'
test('sign up flow', async ({ page }) => {
// Inject Testing Token for this test
await setupClerkTestingToken({ page })
// Navigate to sign-up page
await page.goto('/sign-up')
// Fill form with test credentials
await page.fill('input[name="emailAddress"]', 'test+clerk_test@example.com')
await page.fill('input[name="password"]', 'TestPassword123!')
await page.click('button[type="submit"]')
// Verify with fixed OTP
await page.fill('input[name="code"]', '424242')
await page.click('button[type="submit"]')
// Assert success
await expect(page).toHaveURL('/dashboard')
})
test('sign in with test phone', async ({ page }) => {
await setupClerkTestingToken({ page })
await page.goto('/sign-in')
await page.fill('input[name="identifier"]', '+12015550100')
await page.click('button[type="submit"]')
// Enter fixed OTP
await page.fill('input[name="code"]', '424242')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/dashboard')
})
Manual Testing Token Setup (Alternative)
Instead of clerkSetup(), manually set the environment variable:
export CLERK_TESTING_TOKEN="1713877200-c_2J2MvPu9PnXcuhbPZNao0LOXqK9A7YrnBn0HmIWxy"
Then run tests as usual. setupClerkTestingToken() will use this value.
Demo Repository
Clerk provides a complete example:
Repository: https://github.com/clerk/clerk-playwright-nextjs
Features:
- Next.js App Router with Clerk
- Playwright E2E tests
- Testing Tokens setup
- Test user authentication
To Run:
- Clone repo
- Add Clerk API keys to
.env.local - Create test user with username + password
- Enable username/password auth in Clerk Dashboard
- Run
npm test
Testing Email Links (Magic Links)
Email links are challenging to test in E2E suites.
Recommendation: Use email verification codes instead.
Enable Email Verification Code
- Clerk Dashboard → Email, Phone, Username
- Enable Email verification code strategy
- Use the code flow in tests (easier to automate)
Code flow and link flow are functionally equivalent for most use cases.
Best Practices
Development Testing
✅ Do:
- Use test emails (
+clerk_test) and phone numbers (555-01XX) - Fixed OTP:
424242 - Enable test mode in Clerk Dashboard
- Use
@clerk/testingfor Playwright/Cypress
❌ Don't:
- Send real emails/SMS during tests (wastes quota)
- Use production keys in tests
- Enable test mode on production instances with real users
Backend/API Testing
✅ Do:
- Generate session tokens via Backend API
- Refresh tokens before each test or on interval
- Use test users created via API
- Store
CLERK_SECRET_KEYsecurely
❌ Don't:
- Hardcode session tokens (expire in 60 seconds)
- Reuse expired tokens
- Expose secret keys in logs or version control
E2E Testing
✅ Do:
- Use
@clerk/testingfor automatic Testing Token management - Configure global setup for token generation
- Use test credentials in all flows
- Run tests in CI/CD with secret environment variables
❌ Don't:
- Skip
setupClerkTestingToken()(triggers bot detection) - Manually implement Testing Token logic (use helpers)
- Test code-based auth in production with Testing Tokens
Troubleshooting
"Bot traffic detected" Error
Cause: Missing Testing Token in test suite
Solution:
- Install
@clerk/testing - Configure
clerkSetup()in global setup - Call
setupClerkTestingToken({ page })in tests
Session Token Expired
Cause: Token lifetime is 60 seconds
Solution:
- Refresh token before each test:
beforeEach(() => refreshToken()) - Use interval timer:
setInterval(() => refreshToken(), 50000)
Test Email Not Working
Cause: Test mode not enabled, or wrong email format
Solution:
- Ensure email has
+clerk_testsubaddress - Enable test mode in Clerk Dashboard
- Use fixed OTP:
424242
20 SMS / 100 Email Limit Reached
Cause: Exceeded monthly limit for development instance
Solution:
- Use test credentials (excluded from limits)
- Contact Clerk support to request higher limits
- Use self-delivered SMS/email provider
Reference Links
Official Docs:
- Testing Overview: https://clerk.com/docs/guides/development/testing/overview
- Test Emails & Phones: https://clerk.com/docs/guides/development/testing/test-emails-and-phones
- Playwright Integration: https://clerk.com/docs/guides/development/testing/playwright/overview
- Backend API Reference: https://clerk.com/docs/reference/backend-api
Packages:
@clerk/testing: https://github.com/clerk/javascript/tree/main/packages/testing- Demo Repository: https://github.com/clerk/clerk-playwright-nextjs
API Endpoints:
- Create User:
POST /v1/users - Create Session:
POST /v1/sessions - Create Session Token:
POST /v1/sessions/{session_id}/tokens - Create Testing Token:
POST /v1/testing_tokens
Last Updated: 2025-10-28