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
1
User Registration
User provides email/phone and password
2
Account Creation
Service creates user account and sends verification
3
Email/SMS Verification
User clicks verification link or enters OTP
4
Account Activation
Account is activated and user can sign in
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: 'test@example.com',
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: 'integration@example.com',
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
});
});