Skip to content

Authentication Overview

Complete guide to authentication in CodeNotify API.

Authentication Method

CodeNotify uses JWT (JSON Web Tokens) for stateless authentication with refresh token rotation.

Token Types

TokenPurposeExpiryStorage
Access TokenAPI authentication15 minutesClient memory/state
Refresh TokenToken renewal7 daysSecure storage (httpOnly cookie recommended)

Authentication Flow

Initial Authentication

mermaid
sequenceDiagram
    participant Client
    participant API
    participant Database

    Client->>API: POST /auth/signup or /auth/signin
    API->>Database: Verify credentials
    Database-->>API: User data
    API->>API: Generate tokens (15min + 7day)
    API->>Database: Store hashed refresh token
    API-->>Client: { user, accessToken, refreshToken }
    Client->>Client: Store tokens

Making Authenticated Requests

mermaid
sequenceDiagram
    participant Client
    participant API
    participant JwtGuard
    participant Controller

    Client->>API: GET /users/profile<br/>Authorization: Bearer <accessToken>
    API->>JwtGuard: Validate token
    JwtGuard->>JwtGuard: Verify signature<br/>Check expiration<br/>Load user
    JwtGuard-->>API: User object
    API->>Controller: Execute with user context
    Controller-->>Client: Protected resource

Token Refresh Flow

mermaid
sequenceDiagram
    participant Client
    participant API
    participant Database

    Client->>API: POST /auth/refresh<br/>{ userId, refreshToken }
    API->>Database: Get user & verify token
    Database-->>API: User with hashed refresh token
    API->>API: Compare tokens (bcrypt)
    API->>API: Generate NEW access token
    API-->>Client: { accessToken (NEW), refreshToken (SAME) }
    Client->>Client: Update access token

Endpoints

1. Sign Up

Endpoint: POST /auth/signup
Access: Public

Create a new user account and receive authentication tokens.

Request:

json
{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "securepass123",
  "phoneNumber": "+1234567890"
}

Response (201):

json
{
  "user": {
    "id": "507f1f77bcf86cd799439011",
    "email": "john@example.com",
    "name": "John Doe",
    "phoneNumber": "+1234567890",
    "role": "user"
  },
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Full Documentation →

2. Sign In

Endpoint: POST /auth/signin
Access: Public

Authenticate with existing credentials and receive tokens.

Request:

json
{
  "email": "john@example.com",
  "password": "securepass123"
}

Response (200):

json
{
  "user": {
    "id": "507f1f77bcf86cd799439011",
    "email": "john@example.com",
    "name": "John Doe",
    "role": "user"
  },
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Full Documentation →

3. Refresh Token

Endpoint: POST /auth/refresh
Access: Public

Obtain a new access token using a valid refresh token.

Request:

json
{
  "userId": "507f1f77bcf86cd799439011",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response (200):

json
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Note: Refresh token returned is the SAME token you sent. Only access token is regenerated.

Full Documentation →

4. Sign Out

Endpoint: POST /auth/signout
Access: Protected (JWT required)

Invalidate refresh token and end session.

Request: No body (user from JWT)

Response (200):

json
{
  "message": "Successfully signed out"
}

Full Documentation →

Using Authentication

Include Access Token

All protected endpoints require the access token in the Authorization header:

http
GET /users/profile HTTP/1.1
Host: api.codenotify.dev
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token Format

Authorization: Bearer <access_token>
  • Scheme: Bearer
  • Token: JWT access token (no quotes)
  • Separator: Single space

Example Requests

cURL

bash
curl -X GET https://api.codenotify.dev/users/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

JavaScript (Fetch)

javascript
const response = await fetch('https://api.codenotify.dev/users/profile', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

Axios

javascript
axios.get('https://api.codenotify.dev/users/profile', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

Python (Requests)

python
import requests

response = requests.get(
    'https://api.codenotify.dev/users/profile',
    headers={'Authorization': f'Bearer {access_token}'}
)

Token Management

Storing Tokens

Client-Side (Web)

Access Token:

  • Store in memory (React state, Vuex store)
  • Or sessionStorage (cleared on tab close)
  • Never in localStorage (XSS risk)

Refresh Token:

  • httpOnly cookie (recommended)
  • Or secure storage with encryption
  • Never in localStorage

Mobile Apps

  • Secure storage (Keychain on iOS, Keystore on Android)
  • Encrypted storage
  • Never in plain text

Token Lifecycle

Sign In

Receive tokens (access: 15min, refresh: 7days)

Use access token for API calls

Access token expires (after 15min)

Call /auth/refresh with refresh token

Receive new access token (refresh token unchanged)

Continue using new access token

Refresh token expires (after 7 days)

User must sign in again

Automatic Token Refresh

JavaScript Example

javascript
let accessToken = '';
let refreshToken = '';

// Axios interceptor for automatic refresh
axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;

    // If 401 and haven't retried yet
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        // Refresh token
        const response = await axios.post('/auth/refresh', {
          userId: getUserId(),
          refreshToken: refreshToken
        });

        // Update access token
        accessToken = response.data.accessToken;

        // Retry original request with new token
        originalRequest.headers['Authorization'] = `Bearer ${accessToken}`;
        return axios(originalRequest);
      } catch (refreshError) {
        // Refresh failed - redirect to login
        window.location.href = '/login';
        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  }
);

React Hook Example

typescript
import { useEffect, useState } from 'react';

export const useAuth = () => {
  const [accessToken, setAccessToken] = useState('');
  const [refreshToken, setRefreshToken] = useState('');

  // Refresh token before expiry
  useEffect(() => {
    const refreshInterval = setInterval(async () => {
      if (refreshToken) {
        try {
          const response = await fetch('/auth/refresh', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              userId: getUserId(),
              refreshToken
            })
          });

          const data = await response.json();
          setAccessToken(data.accessToken);
        } catch (error) {
          // Refresh failed - clear tokens
          setAccessToken('');
          setRefreshToken('');
        }
      }
    }, 14 * 60 * 1000); // Refresh every 14 minutes (before 15min expiry)

    return () => clearInterval(refreshInterval);
  }, [refreshToken]);

  return { accessToken, refreshToken, setAccessToken, setRefreshToken };
};

Security Best Practices

Token Security

  1. Never expose tokens in URLs (use headers only)
  2. Use HTTPS in production (prevent token interception)
  3. Store refresh tokens securely (httpOnly cookies or encrypted storage)
  4. Implement token rotation (already done - refresh returns same token)
  5. Set appropriate expiration (15min access, 7day refresh)

Password Security

  1. Minimum 6 characters (current) - consider increasing to 8+
  2. Hash with bcrypt (12 salt rounds)
  3. Never log passwords
  4. Implement rate limiting on auth endpoints
  5. Add CAPTCHA for signup (prevent bots)

API Security

  1. Validate all inputs (Zod schemas)
  2. Use CORS (restrict origins)
  3. Implement rate limiting (100 req/15min for public)
  4. Log authentication events (security monitoring)
  5. Monitor for suspicious activity

Error Handling

Common Errors

StatusErrorCauseSolution
400Bad RequestInvalid inputCheck request format
401UnauthorizedInvalid/expired tokenRefresh or re-login
401Invalid credentialsWrong email/passwordCheck credentials
401Account deactivatedUser.isActive = falseContact admin
403ForbiddenInsufficient permissionsCheck user role
409ConflictEmail already existsUse different email
429Too Many RequestsRate limit exceededWait and retry

Error Response Format

json
{
  "statusCode": 401,
  "message": "Invalid credentials",
  "error": "Unauthorized"
}

Handling Errors

javascript
try {
  const response = await fetch('/auth/signin', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });

  if (!response.ok) {
    const error = await response.json();
    
    switch (response.status) {
      case 401:
        console.error('Invalid credentials');
        break;
      case 409:
        console.error('Email already exists');
        break;
      case 429:
        console.error('Too many attempts, please wait');
        break;
      default:
        console.error('Authentication failed');
    }
    
    throw new Error(error.message);
  }

  const data = await response.json();
  // Store tokens
} catch (error) {
  console.error('Authentication error:', error);
}

Rate Limiting

Limits

EndpointLimitWindow
/auth/signup5 requests1 hour
/auth/signin5 requests15 minutes
/auth/refresh10 requests1 minute
/auth/signout10 requests1 minute

Rate Limit Headers

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200

Testing Authentication

Manual Testing

bash
# 1. Sign up
curl -X POST http://localhost:3000/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"name":"Test User","email":"test@example.com","password":"test123"}'

# 2. Extract tokens from response
ACCESS_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
REFRESH_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# 3. Access protected endpoint
curl -X GET http://localhost:3000/users/profile \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# 4. Refresh token
curl -X POST http://localhost:3000/auth/refresh \
  -H "Content-Type: application/json" \
  -d "{\"userId\":\"USER_ID\",\"refreshToken\":\"$REFRESH_TOKEN\"}"

# 5. Sign out
curl -X POST http://localhost:3000/auth/signout \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Automated Testing

See Auth Module for authentication implementation details.

Built with ❤️ for competitive programmers worldwide.