Files
2025-11-30 08:24:03 +08:00

576 lines
14 KiB
Markdown

# 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