Email Authentication
Traditional email and password authentication for secure user access.
How It Works
Email authentication provides a familiar login experience:
- User registers with email and password
- Email verification link sent
- User verifies email
- Login with email/password to get access token
Security: Passwords are hashed using bcrypt with minimum 8 characters requirement.
Registration Flow
1
Register User
Create new account with email and password
typescript
POST https://auth.dedot.io/v1/auth/email/register
{
"email": "[email protected]",
"password": "SecurePass123!",
"name": "John Doe",
"userType": "client" // client, agent, lsp, csp, dev
}Response:
json
{
"success": true,
"message": "Registration successful. Please verify your email.",
"user": {
"id": "usr_123456",
"email": "[email protected]",
"name": "John Doe",
"verified": false,
"createdAt": "2026-01-16T10:00:00Z"
},
"verificationToken": "verify_abc123def456"
}2
Verify Email
Confirm email address via verification link
typescript
GET https://auth.dedot.io/v1/auth/email/verify?token=verify_abc123def456
// Or via POST
POST https://auth.dedot.io/v1/auth/email/verify
{
"token": "verify_abc123def456"
}Response:
json
{
"success": true,
"message": "Email verified successfully",
"user": {
"id": "usr_123456",
"email": "[email protected]",
"verified": true
}
}3
Login
Authenticate with credentials
typescript
POST https://auth.dedot.io/v1/auth/email/login
{
"email": "[email protected]",
"password": "SecurePass123!"
}Response:
json
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "refresh_token_here",
"expiresIn": 3600,
"user": {
"id": "usr_123456",
"email": "[email protected]",
"name": "John Doe",
"verified": true,
"userType": "client"
}
}Complete Implementation
React Registration Component
typescript
import { useState } from 'react'
function EmailRegister() {
const [formData, setFormData] = useState({
email: '',
password: '',
name: '',
confirmPassword: ''
})
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState(false)
const validateForm = () => {
if (!formData.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
setError('Invalid email format')
return false
}
if (formData.password.length < 8) {
setError('Password must be at least 8 characters')
return false
}
if (formData.password !== formData.confirmPassword) {
setError('Passwords do not match')
return false
}
return true
}
const handleRegister = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
if (!validateForm()) return
setLoading(true)
try {
const response = await fetch('https://auth.dedot.io/v1/auth/email/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: formData.email,
password: formData.password,
name: formData.name,
userType: 'client'
})
})
const data = await response.json()
if (data.success) {
setSuccess(true)
} else {
setError(data.message || 'Registration failed')
}
} catch (err) {
setError('Network error. Please try again.')
} finally {
setLoading(false)
}
}
if (success) {
return (
<div className="success-message">
<h3>Registration Successful!</h3>
<p>Please check your email to verify your account.</p>
</div>
)
}
return (
<form onSubmit={handleRegister}>
<input
type="text"
placeholder="Full Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
<input
type="email"
placeholder="Email Address"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
required
/>
<input
type="password"
placeholder="Password (min 8 characters)"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
required
/>
<input
type="password"
placeholder="Confirm Password"
value={formData.confirmPassword}
onChange={(e) => setFormData({ ...formData, confirmPassword: e.target.value })}
required
/>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? 'Registering...' : 'Register'}
</button>
</form>
)
}React Login Component
typescript
import { useState } from 'react'
function EmailLogin() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError('')
try {
const response = await fetch('https://auth.dedot.io/v1/auth/email/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
})
const data = await response.json()
if (data.success) {
// Store tokens
localStorage.setItem('accessToken', data.accessToken)
localStorage.setItem('refreshToken', data.refreshToken)
// Redirect
window.location.href = '/dashboard'
} else {
setError(data.message || 'Login failed')
}
} catch (err) {
setError('Network error. Please try again.')
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleLogin}>
<input
type="email"
placeholder="Email Address"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
<a href="/forgot-password">Forgot Password?</a>
</form>
)
}Password Reset
1
Request Reset
Send password reset email
typescript
POST https://auth.dedot.io/v1/auth/email/forgot-password
{
"email": "[email protected]"
}Response:
json
{
"success": true,
"message": "Password reset email sent"
}2
Reset Password
Set new password with reset token
typescript
POST https://auth.dedot.io/v1/auth/email/reset-password
{
"token": "reset_abc123def456",
"newPassword": "NewSecurePass123!"
}Response:
json
{
"success": true,
"message": "Password reset successful"
}Password Reset Implementation
typescript
function ForgotPassword() {
const [email, setEmail] = useState('')
const [loading, setLoading] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError('')
try {
const response = await fetch('https://auth.dedot.io/v1/auth/email/forgot-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
})
const data = await response.json()
if (data.success) {
setSuccess(true)
} else {
setError(data.message || 'Failed to send reset email')
}
} catch (err) {
setError('Network error. Please try again.')
} finally {
setLoading(false)
}
}
if (success) {
return (
<div className="success-message">
<p>Password reset instructions sent to {email}</p>
</div>
)
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? 'Sending...' : 'Send Reset Link'}
</button>
</form>
)
}
function ResetPassword() {
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [loading, setLoading] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState('')
// Get token from URL query params
const urlParams = new URLSearchParams(window.location.search)
const token = urlParams.get('token')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (password !== confirmPassword) {
setError('Passwords do not match')
return
}
if (password.length < 8) {
setError('Password must be at least 8 characters')
return
}
setLoading(true)
setError('')
try {
const response = await fetch('https://auth.dedot.io/v1/auth/email/reset-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, newPassword: password })
})
const data = await response.json()
if (data.success) {
setSuccess(true)
setTimeout(() => {
window.location.href = '/login'
}, 2000)
} else {
setError(data.message || 'Failed to reset password')
}
} catch (err) {
setError('Network error. Please try again.')
} finally {
setLoading(false)
}
}
if (success) {
return (
<div className="success-message">
<p>Password reset successful! Redirecting to login...</p>
</div>
)
}
return (
<form onSubmit={handleSubmit}>
<input
type="password"
placeholder="New Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<input
type="password"
placeholder="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
{error && <div className="error">{error}</div>}
<button type="submit" disabled={loading}>
{loading ? 'Resetting...' : 'Reset Password'}
</button>
</form>
)
}Request Parameters
Registration
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Valid email address |
password | string | Yes | Minimum 8 characters |
name | string | Yes | User's full name |
userType | string | Yes | client, agent, lsp, csp, or dev |
Login
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Registered email address |
password | string | Yes | User's password |
Password Requirements
- Minimum length: 8 characters
- Recommended: Mix of uppercase, lowercase, numbers, and symbols
- Hashing: bcrypt with cost factor 12
- Storage: Never stored in plain text
Security Best Practices
Do
- Implement client-side password validation
- Use HTTPS for all requests
- Store tokens securely (httpOnly cookies preferred)
- Implement rate limiting
- Add CAPTCHA for registration
- Enable two-factor authentication
Don't
- Don't store passwords in plain text
- Don't log authentication requests
- Don't expose user emails in URLs
- Don't allow weak passwords
- Don't skip email verification
- Don't use GET for login requests
Error Codes
| Code | Status | Description |
|---|---|---|
| 200 | Success | Operation successful |
| 400 | Bad Request | Invalid input or missing parameters |
| 401 | Unauthorized | Invalid credentials |
| 409 | Conflict | Email already registered |
| 429 | Rate Limit | Too many requests |
| 500 | Server Error | Internal server error |
Rate Limiting
Email authentication endpoints have rate limits:
- Register: 5 requests per IP per hour
- Login: 10 failed attempts per email per 15 minutes
- Password Reset: 3 requests per email per hour
Testing
Development test accounts:
| Password | Behavior | |
|---|---|---|
| [email protected] | Test123! | Always succeeds |
| [email protected] | Any | Always fails (invalid credentials) |
| [email protected] | Any | Account locked error |
Development Only
Test accounts only work in development. Production requires real email verification.

