Skip to main content

Overview

The inspir API uses JWT (JSON Web Token) authentication. Tokens are issued upon signup or login and remain valid for 7 days. Authentication flow:
  1. User signs up or logs in
  2. Server returns a JWT token
  3. Client includes token in Authorization header for subsequent requests
  4. Server validates token and identifies user

Authentication Types

The API supports three authentication patterns:

Public

No authentication required. Anyone can access.

Optional Auth

Works for guests, enhanced for authenticated users.

Required Auth

Must be authenticated. Returns 401 if not logged in.

Getting a Token

Sign Up

Create a new account and receive a JWT token:
POST /api/auth/signup
Request body:
username
string
required
Username (alphanumeric, underscores, hyphens only)
password
string
required
Password (minimum 6 characters)
confirmPassword
string
required
Password confirmation (must match password)
Example request:
curl -X POST https://api.inspir.uk/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "username": "student123",
    "password": "mySecurePassword",
    "confirmPassword": "mySecurePassword"
  }'
Success response (200):
user
object
User information
id
string
User UUID
username
string
Username
token
string
JWT authentication token (valid for 7 days)
{
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "student123"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJ1c2VybmFtZSI6InN0dWRlbnQxMjMiLCJpYXQiOjE3MDk1NjQwMDAsImV4cCI6MTcxMDE2ODgwMH0.abc123def456"
}
Error responses:
StatusErrorCause
400"Username and password are required"Missing required fields
400"Passwords do not match"Password confirmation doesn’t match
400"Password must be at least 6 characters"Password too short
400"Username already exists"Username taken
400"Username can only contain..."Invalid username format
429"Too many authentication attempts"Rate limit exceeded (5 attempts per 15 minutes)
Usernames are sanitized to allow only alphanumeric characters, underscores, and hyphens. The username is case-sensitive.

Log In

Authenticate with existing credentials:
POST /api/auth/login
Request body:
username
string
required
Your username
password
string
required
Your password
Example request:
curl -X POST https://api.inspir.uk/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "student123",
    "password": "mySecurePassword"
  }'
Success response (200):
user
object
User information
id
string
User UUID
username
string
Username
token
string
JWT authentication token (valid for 7 days)
{
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "student123"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Error responses:
StatusErrorCause
400"Username and password are required"Missing required fields
401"Invalid username or password"Incorrect credentials
429"Too many authentication attempts"Rate limit exceeded
For security, the API returns the same error message whether the username doesn’t exist or the password is incorrect.

Using Your Token

Authorization Header

Include the JWT token in the Authorization header using the Bearer scheme:
Authorization: Bearer YOUR_JWT_TOKEN
Example:
curl -X GET https://api.inspir.uk/api/quiz/history \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Structure

JWT tokens contain encoded user information:
{
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "username": "student123",
  "iat": 1709564000,
  "exp": 1710168800
}
userId
string
User’s unique identifier (UUID)
username
string
User’s username
iat
number
Issued at (Unix timestamp)
exp
number
Expiration time (Unix timestamp, 7 days from issuance)

Token Validation

The server validates tokens using the middleware in backend/middleware/auth.js:7:
  1. Checks for Authorization header with Bearer prefix
  2. Extracts and verifies JWT signature
  3. Decodes user ID from token payload
  4. Queries database to ensure user still exists
  5. Attaches user object to request: req.user
Authentication middleware implementation:
// From backend/middleware/auth.js
export async function authenticateUser(req, res, next) {
  const authHeader = req.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      error: 'No authorization token provided'
    });
  }
  
  const token = authHeader.substring(7);
  const decoded = jwt.verify(token, JWT_SECRET);
  
  // Fetch user from database
  const { data: users, error } = await supabase
    .from('users')
    .select('id, username, created_at')
    .eq('id', decoded.userId)
    .limit(1);
  
  if (error || !users || users.length === 0) {
    return res.status(401).json({
      error: 'Invalid or expired token'
    });
  }
  
  req.user = users[0];
  next();
}

Authentication Errors

When authentication fails, the API returns 401 Unauthorized:

Missing Token

Request without Authorization header:
curl -X GET https://api.inspir.uk/api/quiz/history
Response (401):
{
  "error": "No authorization token provided"
}

Invalid Token Format

Malformed or corrupted JWT:
curl -X GET https://api.inspir.uk/api/quiz/history \
  -H "Authorization: Bearer invalid.token.here"
Response (401):
{
  "error": "Invalid token"
}

Expired Token

Token older than 7 days:
curl -X GET https://api.inspir.uk/api/quiz/history \
  -H "Authorization: Bearer eyJhbGciOi...[expired]..."
Response (401):
{
  "error": "Token expired"
}

User Not Found

Valid token but user deleted from database: Response (401):
{
  "error": "Invalid or expired token"
}

Token Refresh

Tokens are valid for 7 days. There is currently no token refresh endpoint. To get a new token:
  1. Log in again with username and password
  2. Replace the old token with the new one in your client
Best practice: Store tokens securely (e.g., in httpOnly cookies or secure storage). Never expose tokens in URLs or client-side JavaScript logs.

Optional Authentication

Some endpoints work for both guests and authenticated users. These use the optionalAuth middleware (backend/middleware/auth.js:59): Endpoints with optional authentication:
  • POST /api/quiz/generate - Generate quizzes
  • POST /api/quiz/submit - Submit quiz answers
  • GET /api/quiz/:id - View specific quiz
  • POST /api/quiz/shared/:shareToken/submit - Submit shared quiz
Behavior:
  • Without token: Request proceeds as guest user
  • With valid token: req.user is populated with user data
  • With invalid token: Request proceeds as guest (no error)
Example - Generate quiz as guest:
curl -X POST https://api.inspir.uk/api/quiz/generate \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Photosynthesis is the process...",
    "sourceName": "Biology Notes"
  }'
Quiz generates successfully but won’t be saved to history. Example - Generate quiz as authenticated user:
curl -X POST https://api.inspir.uk/api/quiz/generate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGci..." \
  -d '{
    "content": "Photosynthesis is the process...",
    "sourceName": "Biology Notes"
  }'
Quiz generates and saves to user’s history.

Required Authentication

Endpoints that require authentication will return 401 if no valid token is provided: Protected endpoints:
  • GET /api/auth/me - Get current user
  • GET /api/quiz/history - Get quiz history
  • POST /api/quiz/:quizId/share - Share a quiz
  • GET /api/quiz/:quizId/attempts - View quiz statistics
  • GET /api/streaks - View study streaks
  • User profile and settings endpoints
Example - Access protected endpoint without token:
curl -X GET https://api.inspir.uk/api/quiz/history
Response (401):
{
  "error": "No authorization token provided"
}

Get Current User

Verify authentication and retrieve current user information:
GET /api/auth/me
Headers:
Authorization
string
required
Bearer token
Example request:
curl -X GET https://api.inspir.uk/api/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Success response (200):
user
object
Current user information
id
string
User UUID
username
string
Username
created_at
string
Account creation timestamp (ISO 8601)
{
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "student123",
    "created_at": "2024-03-01T10:30:00.000Z"
  }
}
Error responses:
StatusErrorCause
401"No authorization token provided"Missing Authorization header
401"Invalid token"Malformed JWT
401"Token expired"Token older than 7 days
401"Invalid or expired token"User not found in database
401"Not authenticated"Generic authentication failure

Logout

Log out from the current session:
POST /api/auth/logout
Example request:
curl -X POST https://api.inspir.uk/api/auth/logout
Success response (200):
{
  "message": "Logged out successfully"
}
Since authentication is JWT-based and stateless, logout is primarily handled client-side by removing the stored token. The server endpoint exists for consistency but doesn’t invalidate the token server-side. The token remains technically valid until expiration (7 days).

Security Best Practices

  • Use httpOnly cookies for web applications
  • Use secure storage mechanisms in mobile apps
  • Never store tokens in localStorage if XSS is a concern
  • Never expose tokens in URLs or logs
  • Monitor for 401 responses indicating expired tokens
  • Prompt users to re-authenticate when tokens expire
  • Consider implementing token refresh before implementing auto-re-login
  • Always use HTTPS to prevent token interception
  • The production API enforces HTTPS
  • Development environments (localhost) can use HTTP
  • Authentication endpoints are limited to 5 attempts per 15 minutes
  • Implement exponential backoff for retry logic
  • Cache tokens instead of requesting new ones on every API call

Example: Complete Authentication Flow

Here’s a complete example of signing up, authenticating, and making authenticated requests:
// 1. Sign up
const signupResponse = await fetch('https://api.inspir.uk/api/auth/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'student123',
    password: 'mySecurePassword',
    confirmPassword: 'mySecurePassword'
  })
});

const { user, token } = await signupResponse.json();

// 2. Store token securely
localStorage.setItem('auth_token', token); // Or use httpOnly cookie

// 3. Make authenticated requests
const quizHistoryResponse = await fetch('https://api.inspir.uk/api/quiz/history', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

const quizHistory = await quizHistoryResponse.json();

// 4. Handle authentication errors
if (quizHistoryResponse.status === 401) {
  // Token expired or invalid - redirect to login
  localStorage.removeItem('auth_token');
  window.location.href = '/login';
}

// 5. Logout
await fetch('https://api.inspir.uk/api/auth/logout', { method: 'POST' });
localStorage.removeItem('auth_token');

Next Steps

Quiz API

Generate and manage quizzes with authentication

Study Tools

Access AI-powered study tools

Build docs developers (and LLMs) love