Skip to content

Email Authentication

Traditional email and password authentication for secure user access.

How It Works

Email authentication provides a familiar login experience:

  1. User registers with email and password
  2. Email verification link sent
  3. User verifies email
  4. 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

ParameterTypeRequiredDescription
emailstringYesValid email address
passwordstringYesMinimum 8 characters
namestringYesUser's full name
userTypestringYesclient, agent, lsp, csp, or dev

Login

ParameterTypeRequiredDescription
emailstringYesRegistered email address
passwordstringYesUser'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

CodeStatusDescription
200SuccessOperation successful
400Bad RequestInvalid input or missing parameters
401UnauthorizedInvalid credentials
409ConflictEmail already registered
429Rate LimitToo many requests
500Server ErrorInternal 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:

EmailPasswordBehavior
[email protected]Test123!Always succeeds
[email protected]AnyAlways fails (invalid credentials)
[email protected]AnyAccount locked error

Development Only

Test accounts only work in development. Production requires real email verification.

Next Steps