# 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: 1. **Test Emails & Phone Numbers** - Fake credentials with fixed OTP codes 2. **Session Tokens** - Generate valid tokens via Backend API 3. **Testing Tokens** - Bypass bot detection in test suites 4. **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): 1. Navigate to Clerk Dashboard → **Settings** 2. 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](https://en.wikipedia.org/wiki/555_(telephone_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_test` or 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 ```tsx 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 ```tsx 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**: 1. **Create User** (if needed) 2. **Create Session** for user 3. **Create Session Token** from session ID 4. **Use Token** in Authorization header ### Step-by-Step Implementation #### Step 1: Create User **Endpoint**: `POST https://api.clerk.com/v1/users` ```bash 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**: ```json { "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` ```bash 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**: ```json { "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` ```bash curl -X POST https://api.clerk.com/v1/sessions/sess_2xyz789abc123/tokens \ -H "Authorization: Bearer sk_test_YOUR_SECRET_KEY" ``` **Response**: ```json { "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "object": "token" } ``` **Save**: `jwt` token #### Step 4: Use Token in Requests ```bash 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** ```typescript beforeEach(async () => { sessionToken = await refreshSessionToken(sessionId) }) ``` **Option 2: Interval Timer** ```typescript 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/testing` package (handles automatically) - Playwright integration (recommended) - Cypress integration (recommended) ### Obtain Testing Token **Endpoint**: `POST https://api.clerk.com/v1/testing_tokens` ```bash curl -X POST https://api.clerk.com/v1/testing_tokens \ -H "Authorization: Bearer sk_test_YOUR_SECRET_KEY" ``` **Response**: ```json { "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 ```bash npm install -D @clerk/testing ``` ### Set Environment Variables In your test runner (e.g., `.env.test` or GitHub Actions secrets): ```bash 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` ```typescript 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_TOKEN` environment variable - Makes token available for all tests ### Use in Tests Import `setupClerkTestingToken()` and call before navigating to auth pages: **File**: `auth.spec.ts` ```typescript 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: ```bash 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**: 1. Clone repo 2. Add Clerk API keys to `.env.local` 3. Create test user with username + password 4. Enable username/password auth in Clerk Dashboard 5. 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 1. Clerk Dashboard → **Email, Phone, Username** 2. Enable **Email verification code** strategy 3. 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/testing` for 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_KEY` securely ❌ **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/testing` for 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**: 1. Install `@clerk/testing` 2. Configure `clerkSetup()` in global setup 3. 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_test` subaddress - 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