How Auth works
This document explains how authentication works in the application.
Overview: Refresh Token + Access Token Approach
Our authentication system uses two types of tokens:
Access Token: Short-lived token (valid for 15 minutes) used to authenticate users for protected API routes. These tokens are kept only in memory and never stored in persistent storage.
Refresh Token: Long-lived token (valid for 90 days) used only to obtain a new pair of access and refresh tokens when the access token expires. This token is stored in browser's local storage.
This approach follows security best practices by minimizing the exposure of the more powerful access token while providing seamless user experience.
Authentication Flow
Initial Login:
User logs in with credentials (email/password) or via OAuth provider
Server validates credentials and creates a token pair
Access token is kept in memory (JavaScript variable)
Refresh token is stored in local storage
Authenticated Requests:
For each API request, the access token is attached in the Authorization header
Server validates the token and processes the request
Token Refresh Flow:
When access token expires (or is about to expire), client uses the refresh token to get a new token pair
If refresh is successful, new access token is kept in memory and new refresh token replaces the old one in local storage
This creates a persistent login experience across page refreshes
Token Rotation:
For security, we implement refresh token rotation - each time a refresh token is used, it's replaced with a new one
This helps prevent token replay attacks
If a refresh token is used more than once (potentially indicating it was leaked), the system invalidates the entire token family
Key Files
Client-side (ClojureScript)
src/cljs/saas/auth.cljs: Contains the core authentication logic for the front-end including:
Token management
Login/logout functions
Token refresh logic
OAuth authentication
Server-side (Clojure)
src/clj/saas/web/controllers/auth/handlers.clj: Contains server-side authentication handlers:
User registration
Login
Token generation
Refresh token rotation logic
OAuth providers integration
Protecting API Routes
API routes that require authentication are protected using middleware:
The application uses two middleware functions:
wrap-jwt-auth: Decodes the JWT token and adds
:claims
to the request objectwrap-authenticated: Ensures that a user is authenticated by checking for the existence of
:sub
in claims
Accessing User Information in Route Handlers
When handling requests for authenticated routes, you can access the user information from the token claims:
Common claims available in the token:
:sub
: The subject (user ID):exp
: Expiration time:iat
: Issued at timeOther custom claims depending on your application
Refresh Token Persistence
Login persistence works because:
On page load/refresh, the system checks for a refresh token in local storage
If found, it uses this token to request a new token pair from the server
This happens automatically before the user interacts with the application
As long as the refresh token is valid, the user remains logged in without having to re-enter credentials
This approach balances security (access tokens are ephemeral and never stored) with user experience (seamless login persistence through refresh tokens).
Last updated