Overview
This guide walks you through implementing a complete user registration flow using Strike Auth Service. We’ll cover different signup methods, email verification, error handling, and best practices.Basic Signup Flow
Implementation Examples
Frontend Implementation (React)
Copy
Ask AI
import { useState } from 'react';
function SignupForm() {
const [formData, setFormData] = useState({
email: '',
password: '',
firstName: '',
lastName: ''
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setError('');
try {
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: formData.email,
password: formData.password,
data: {
first_name: formData.firstName,
last_name: formData.lastName
}
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.msg || 'Signup failed');
}
const user = await response.json();
setSuccess(true);
// Redirect to verification page or show success message
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (success) {
return (
<div className="success-message">
<h2>Check your email!</h2>
<p>We've sent a verification link to {formData.email}</p>
<p>Please click the link to activate your account.</p>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="signup-form">
<h2>Create Account</h2>
{error && (
<div className="error-message">
{error}
</div>
)}
<div className="form-group">
<label htmlFor="firstName">First Name</label>
<input
type="text"
id="firstName"
value={formData.firstName}
onChange={(e) => setFormData({
...formData,
firstName: e.target.value
})}
required
/>
</div>
<div className="form-group">
<label htmlFor="lastName">Last Name</label>
<input
type="text"
id="lastName"
value={formData.lastName}
onChange={(e) => setFormData({
...formData,
lastName: e.target.value
})}
required
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
value={formData.email}
onChange={(e) => setFormData({
...formData,
email: e.target.value
})}
required
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={formData.password}
onChange={(e) => setFormData({
...formData,
password: e.target.value
})}
required
minLength={8}
/>
<small>
Password must be at least 8 characters with uppercase, lowercase, number, and special character.
</small>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Creating Account...' : 'Sign Up'}
</button>
<p>
Already have an account? <a href="/login">Sign in</a>
</p>
</form>
);
}
export default SignupForm;
Backend API Route (Node.js/Express)
Copy
Ask AI
const express = require('express');
const { body, validationResult } = require('express-validator');
const router = express.Router();
// Validation middleware
const signupValidation = [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/),
body('data.first_name').trim().isLength({ min: 1 }),
body('data.last_name').trim().isLength({ min: 1 })
];
router.post('/signup', signupValidation, async (req, res) => {
try {
// Check validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
code: 400,
msg: 'Validation failed',
details: errors.array()
});
}
// Call Strike Auth Service
const response = await fetch(`${process.env.AUTH_SERVICE_URL}/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(req.body),
});
const data = await response.json();
if (!response.ok) {
return res.status(response.status).json(data);
}
// Log successful signup
console.log(`New user registered: ${data.email}`);
// Return user data (without sensitive information)
res.status(201).json({
id: data.id,
email: data.email,
email_confirmed_at: data.email_confirmed_at,
created_at: data.created_at
});
} catch (error) {
console.error('Signup error:', error);
res.status(500).json({
code: 500,
msg: 'Internal server error',
details: 'Please try again later'
});
}
});
module.exports = router;
Email Verification Flow
Verification Email Template
Copy
Ask AI
<!DOCTYPE html>
<html>
<head>
<title>Verify Your Email - Strike</title>
</head>
<body>
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h1>Welcome to Strike!</h1>
<p>Hi {{.UserMetaData.first_name}},</p>
<p>Thanks for signing up! Please verify your email address by clicking the button below:</p>
<a href="{{.ConfirmationURL}}"
style="background-color: #0D9373; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">
Verify Email Address
</a>
<p>Or copy and paste this link into your browser:</p>
<p>{{.ConfirmationURL}}</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>
<p>Best regards,<br>The Strike Team</p>
</div>
</body>
</html>
Handling Verification Callback
Copy
Ask AI
// Verification callback handler
router.get('/verify', async (req, res) => {
const { type, token, redirect_to } = req.query;
try {
// Verify the token with Strike Auth Service
const response = await fetch(`${process.env.AUTH_SERVICE_URL}/verify?type=${type}&token=${token}`, {
method: 'GET',
});
if (response.ok) {
// Successful verification - redirect to app
const redirectUrl = redirect_to || '/dashboard';
res.redirect(redirectUrl);
} else {
// Verification failed
res.redirect('/verification-failed');
}
} catch (error) {
console.error('Verification error:', error);
res.redirect('/verification-failed');
}
});
// Resend verification email
router.post('/resend-verification', async (req, res) => {
const { email } = req.body;
try {
const response = await fetch(`${process.env.AUTH_SERVICE_URL}/resend`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'signup',
email: email
}),
});
if (response.ok) {
res.json({ message: 'Verification email sent' });
} else {
const error = await response.json();
res.status(response.status).json(error);
}
} catch (error) {
res.status(500).json({
code: 500,
msg: 'Failed to resend verification email'
});
}
});
Phone Number Signup
SMS Verification Flow
Copy
Ask AI
function PhoneSignupForm() {
const [step, setStep] = useState('phone'); // 'phone' or 'verify'
const [phoneNumber, setPhoneNumber] = useState('');
const [otp, setOtp] = useState('');
const [password, setPassword] = useState('');
const handlePhoneSubmit = async (e) => {
e.preventDefault();
try {
// Send OTP
await fetch('/api/auth/otp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: phoneNumber,
create_user: true
})
});
setStep('verify');
} catch (error) {
console.error('Failed to send OTP:', error);
}
};
const handleVerifySubmit = async (e) => {
e.preventDefault();
try {
// Verify OTP and create account
const response = await fetch('/api/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'sms',
token: otp,
phone: phoneNumber,
password: password
})
});
if (response.ok) {
// Account created and verified
window.location.href = '/dashboard';
}
} catch (error) {
console.error('Verification failed:', error);
}
};
if (step === 'verify') {
return (
<form onSubmit={handleVerifySubmit}>
<h2>Verify Your Phone</h2>
<p>Enter the code sent to {phoneNumber}</p>
<input
type="text"
placeholder="Enter 6-digit code"
value={otp}
onChange={(e) => setOtp(e.target.value)}
maxLength={6}
required
/>
<input
type="password"
placeholder="Create password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Verify & Create Account</button>
</form>
);
}
return (
<form onSubmit={handlePhoneSubmit}>
<h2>Sign Up with Phone</h2>
<input
type="tel"
placeholder="+1 (555) 123-4567"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
required
/>
<button type="submit">Send Verification Code</button>
</form>
);
}
OAuth Signup
Social Login Buttons
Copy
Ask AI
function SocialSignup() {
const handleOAuthSignup = (provider) => {
const redirectUrl = encodeURIComponent(window.location.origin + '/auth/callback');
window.location.href = `/api/auth/authorize?provider=${provider}&redirect_to=${redirectUrl}`;
};
return (
<div className="social-signup">
<h3>Or sign up with</h3>
<button
onClick={() => handleOAuthSignup('google')}
className="oauth-button google"
>
<img src="/icons/google.svg" alt="Google" />
Continue with Google
</button>
<button
onClick={() => handleOAuthSignup('github')}
className="oauth-button github"
>
<img src="/icons/github.svg" alt="GitHub" />
Continue with GitHub
</button>
<button
onClick={() => handleOAuthSignup('apple')}
className="oauth-button apple"
>
<img src="/icons/apple.svg" alt="Apple" />
Continue with Apple
</button>
</div>
);
}
OAuth Callback Handler
Copy
Ask AI
router.get('/auth/callback', async (req, res) => {
const { code, state, error } = req.query;
if (error) {
return res.redirect('/signup?error=' + encodeURIComponent(error));
}
try {
// Exchange code for tokens
const response = await fetch(`${process.env.AUTH_SERVICE_URL}/callback?code=${code}&state=${state}`, {
method: 'GET',
});
if (response.ok) {
// OAuth signup successful
const data = await response.json();
// Set session cookies
res.cookie('access_token', data.access_token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: data.expires_in * 1000
});
res.cookie('refresh_token', data.refresh_token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
});
res.redirect('/dashboard');
} else {
res.redirect('/signup?error=oauth_failed');
}
} catch (error) {
console.error('OAuth callback error:', error);
res.redirect('/signup?error=oauth_failed');
}
});
Error Handling
Common Signup Errors
Copy
Ask AI
const handleSignupError = (error) => {
switch (error.code) {
case 400:
if (error.msg.includes('email')) {
return 'Please enter a valid email address';
}
if (error.msg.includes('password')) {
return 'Password must be at least 8 characters with uppercase, lowercase, number, and special character';
}
return 'Please check your input and try again';
case 422:
if (error.msg.includes('already registered')) {
return 'An account with this email already exists. Try signing in instead.';
}
return error.msg;
case 429:
return 'Too many signup attempts. Please wait a few minutes and try again.';
case 500:
return 'Something went wrong on our end. Please try again later.';
default:
return 'Signup failed. Please try again.';
}
};
User-Friendly Error Messages
Copy
Ask AI
function ErrorMessage({ error }) {
const getErrorMessage = (error) => {
if (typeof error === 'string') return error;
return handleSignupError(error);
};
if (!error) return null;
return (
<div className="error-message" role="alert">
<div className="error-icon">⚠️</div>
<div className="error-text">
{getErrorMessage(error)}
</div>
</div>
);
}
Best Practices
User Experience
User Experience
- Provide clear password requirements
- Show real-time validation feedback
- Use progressive disclosure for complex forms
- Offer multiple signup options (email, phone, OAuth)
- Implement proper loading states
Security
Security
- Validate input on both client and server
- Implement rate limiting
- Use HTTPS for all requests
- Hash passwords securely
- Implement CAPTCHA for high-risk scenarios
Email Verification
Email Verification
- Send verification emails immediately
- Provide clear instructions
- Include resend functionality
- Set appropriate expiration times
- Handle edge cases gracefully
Error Handling
Error Handling
- Provide specific, actionable error messages
- Don’t reveal sensitive information
- Log errors for debugging
- Implement retry mechanisms
- Graceful degradation for network issues
Testing Your Signup Flow
Unit Tests
Copy
Ask AI
describe('Signup API', () => {
test('should create user with valid data', async () => {
const userData = {
email: '[email protected]',
password: 'SecurePass123!',
data: {
first_name: 'Test',
last_name: 'User'
}
};
const response = await request(app)
.post('/api/auth/signup')
.send(userData)
.expect(201);
expect(response.body.email).toBe(userData.email);
expect(response.body.id).toBeDefined();
});
test('should reject invalid email', async () => {
const userData = {
email: 'invalid-email',
password: 'SecurePass123!'
};
await request(app)
.post('/api/auth/signup')
.send(userData)
.expect(400);
});
});
Integration Tests
Copy
Ask AI
describe('Signup Flow Integration', () => {
test('complete signup and verification flow', async () => {
// 1. Sign up user
const signupResponse = await request(app)
.post('/api/auth/signup')
.send({
email: '[email protected]',
password: 'SecurePass123!'
});
expect(signupResponse.status).toBe(201);
// 2. Verify email (mock verification)
const verifyResponse = await request(app)
.get('/api/auth/verify')
.query({
type: 'signup',
token: 'mock-verification-token'
});
expect(verifyResponse.status).toBe(302); // Redirect
});
});