Verify Email
Verify a user's email address using a verification token.
Endpoint
GET /v1/auth/verify-emailDescription
Verifies a user's email address using a one-time verification token sent via email during registration. The token is consumed upon successful verification and cannot be reused.
Authentication
Required: No (public endpoint - uses token from email)
Headers
No special headers required.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Email verification token from the verification email |
Request
Example Request
GET /v1/auth/verify-email?token=evt_a1b2c3d4e5f6g7h8Response
Success Response
Status Code: 200 OK
{
"message": "Email verified successfully"
}Note: If the email is already verified, the endpoint still returns success (idempotent).
Error Responses
400 Bad Request - Missing or Invalid Token
Missing token:
{
"type": "https://api.cerberus-iam.com/errors/bad-request",
"title": "Bad Request",
"status": 400,
"detail": "Missing verification token"
}Invalid or expired token:
{
"type": "https://api.cerberus-iam.com/errors/bad-request",
"title": "Bad Request",
"status": 400,
"detail": "Invalid or expired verification token"
}404 Not Found - User Not Found
User associated with token not found:
{
"type": "https://api.cerberus-iam.com/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "User not found for provided token"
}Token Details
Token Format
- Prefix:
evt_(email verification token) - Length: Variable (secure random string)
- Expiration: Configurable (typically 24-48 hours)
- One-time use: Token is consumed/deleted after successful verification
Token Generation
Tokens are generated during:
- User registration (POST /v1/auth/register)
- Email change requests (if implemented)
- Resend verification email (if implemented)
Side Effects
User record updated:
emailVerifiedAtfield set to current timestamp
Token consumed:
- Token record deleted from database
- Cannot be used again
Audit log entry created (if audit logging is enabled)
Email Verification Flow
- User registers: POST /v1/auth/register
- Server sends email: Contains verification link with token
- User clicks link: Opens this endpoint with token parameter
- Server verifies token: Updates user record
- Client shows success: Redirect to login or dashboard
Code Examples
Email Link (Sent to User)
<p>Please verify your email address by clicking the link below:</p>
<a href="https://app.cerberus.local/verify-email?token=evt_a1b2c3d4e5f6g7h8">
Verify Email Address
</a>
<p>This link will expire in 24 hours.</p>cURL
curl -X GET 'http://localhost:4000/v1/auth/verify-email?token=evt_a1b2c3d4e5f6g7h8'JavaScript (fetch)
async function verifyEmail(token) {
const response = await fetch(
`http://localhost:4000/v1/auth/verify-email?token=${encodeURIComponent(token)}`,
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail);
}
const data = await response.json();
return data;
}
// Usage (extract token from URL)
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
if (token) {
try {
const result = await verifyEmail(token);
console.log(result.message); // "Email verified successfully"
// Show success message and redirect
showSuccessMessage('Email verified! You can now login.');
setTimeout(() => {
window.location.href = '/login';
}, 2000);
} catch (error) {
console.error('Verification failed:', error.message);
showErrorMessage(error.message);
}
} else {
showErrorMessage('No verification token provided');
}TypeScript (with React)
import { useEffect, useState } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom';
interface VerificationResult {
message: string;
}
interface ProblemDetails {
type: string;
title: string;
status: number;
detail: string;
}
export function EmailVerificationPage() {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const [status, setStatus] = useState<'verifying' | 'success' | 'error'>('verifying');
const [message, setMessage] = useState<string>('');
useEffect(() => {
const token = searchParams.get('token');
if (!token) {
setStatus('error');
setMessage('No verification token provided');
return;
}
verifyEmail(token);
}, [searchParams]);
async function verifyEmail(token: string) {
try {
const response = await fetch(
`http://localhost:4000/v1/auth/verify-email?token=${encodeURIComponent(token)}`
);
if (!response.ok) {
const problem: ProblemDetails = await response.json();
throw new Error(problem.detail);
}
const result: VerificationResult = await response.json();
setStatus('success');
setMessage(result.message);
// Redirect to login after 3 seconds
setTimeout(() => {
navigate('/login?verified=1');
}, 3000);
} catch (error) {
setStatus('error');
setMessage(error instanceof Error ? error.message : 'Verification failed');
}
}
return (
<div className="verification-page">
{status === 'verifying' && (
<div className="loading">
<h2>Verifying your email...</h2>
<div className="spinner" />
</div>
)}
{status === 'success' && (
<div className="success">
<h2>Email Verified!</h2>
<p>{message}</p>
<p>Redirecting to login...</p>
</div>
)}
{status === 'error' && (
<div className="error">
<h2>Verification Failed</h2>
<p>{message}</p>
<button onClick={() => navigate('/login')}>
Go to Login
</button>
</div>
)}
</div>
);
}Server-Side Verification (Node.js)
// Useful for server-side email verification flows
async function verifyEmailServerSide(token: string): Promise<boolean> {
const response = await fetch(
`http://localhost:4000/v1/auth/verify-email?token=${encodeURIComponent(token)}`,
);
return response.ok;
}
// Usage in an Express route
app.get('/verify', async (req, res) => {
const { token } = req.query;
if (!token || typeof token !== 'string') {
return res.status(400).render('error', {
message: 'Invalid verification link',
});
}
const verified = await verifyEmailServerSide(token);
if (verified) {
res.render('success', {
title: 'Email Verified',
message: 'Your email has been verified successfully. You can now login.',
});
} else {
res.render('error', {
title: 'Verification Failed',
message: 'The verification link is invalid or has expired.',
});
}
});Security Considerations
- One-Time Use: Tokens are deleted after successful verification
- Expiration: Tokens expire after a set period (typically 24-48 hours)
- Secure Random: Tokens use cryptographically secure random generation
- No Personal Data in Token: Token is an opaque identifier
- HTTPS Required: Verification links should use HTTPS in production
- Rate Limiting: Consider rate limiting this endpoint (though less critical than login)
Best Practices
Client Implementation
- Extract Token from URL: Parse query parameters on page load
- Show Loading State: Display spinner while verifying
- Handle Success: Show success message and redirect to login
- Handle Errors: Show clear error messages with actionable steps
- Resend Option: Provide link to resend verification email (if implemented)
Email Template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Verify Your Email</title>
</head>
<body>
<h1>Welcome to Cerberus IAM!</h1>
<p>Thank you for registering. Please verify your email address by clicking the button below:</p>
<a
href="https://app.cerberus.local/verify-email?token={{token}}"
style="display: inline-block; padding: 12px 24px; background: #007bff; color: white; text-decoration: none; border-radius: 4px;"
>
Verify Email Address
</a>
<p>Or copy and paste this link into your browser:</p>
<p>
<a href="https://app.cerberus.local/verify-email?token={{token}}"
>https://app.cerberus.local/verify-email?token={{token}}</a
>
</p>
<p>This link will expire in 24 hours.</p>
<p>If you didn't create an account, you can safely ignore this email.</p>
</body>
</html>Common Issues
Token Already Used
Problem: User clicks verification link multiple times.
Solution: Endpoint is idempotent - returns success if email is already verified.
Token Expired
Problem: User clicks link after token expiration.
Solution: Return 400 error. Implement "Resend Verification Email" feature.
User Not Found
Problem: User account was deleted after token was sent.
Solution: Return 404 error. User needs to register again.
Related Endpoints
- POST /v1/auth/register - Register and receive verification email
- POST /v1/auth/login - Login after email verification
- Resend Verification Email - (if implemented)