# Authentication The B12 SIS API uses JWT (JSON Web Tokens) for authentication with access and refresh token rotation. ## Overview - **Access Token**: Short-lived token for API access (default: 24 hours) - **Refresh Token**: Long-lived token for obtaining new access tokens (default: 7 days) - **Token Rotation**: Each refresh generates a new token pair ## Endpoints ### Login Authenticate with email and password. **Endpoint:** `POST /api/auth/login` **Request:** ```json { "email": "user@school.edu", "password": "your-password" } ``` **Response:** ```json { "token": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 86400 }, "user": { "id": "user-uuid", "email": "user@school.edu", "name": "User Name", "avatar": "https://...", "teams": [ { "id": "team-uuid", "name": "Main Campus" } ], "roles": [ { "id": "role-uuid", "name": "Admin" } ] } } ``` **Error Responses:** === "Invalid Credentials" ```json { "error": "Invalid email or password" } ``` Status: 401 Unauthorized === "Account Disabled" ```json { "error": "Account is disabled" } ``` Status: 401 Unauthorized --- ### Refresh Token Get a new access token using a valid refresh token. **Endpoint:** `POST /api/auth/refresh` **Request:** ```json { "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Response:** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 86400 } ``` !!! warning "Token Rotation" Each refresh invalidates the old refresh token and issues a new pair. Store the new refresh token for future use. --- ### Get Current User Retrieve the authenticated user's profile and permissions. **Endpoint:** `GET /api/auth/me` **Headers:** ``` Authorization: Bearer ``` **Response:** ```json { "success": true, "message": "user", "data": { "user": { "id": "user-uuid", "email": "user@school.edu", "name": "User Name", "avatar": "https://...", "teams": [...], "roles": [...] }, "permissions": { "Student": ["list", "read", "create", "update", "delete"], "Teacher": ["list", "read"], "Class": ["list", "read", "create", "update"] } } } ``` --- ### Change Password Change the authenticated user's password. **Endpoint:** `POST /api/auth/change-password` **Headers:** ``` Authorization: Bearer ``` **Request:** ```json { "current_password": "old-password", "new_password": "new-password", "confirm_password": "new-password" } ``` **Response:** ```json { "success": true, "message": "Password changed successfully" } ``` **Validation Rules:** - New password must be at least 8 characters - New password must contain uppercase, lowercase, and numbers - New password must be different from current password --- ### Logout Invalidate the user's refresh token. **Endpoint:** `POST /api/auth/logout` **Headers:** ``` Authorization: Bearer ``` **Response:** ```json { "message": "Successfully logged out" } ``` --- ### Reset Password (Admin) Reset a user's password to the default. Requires admin role. **Endpoint:** `POST /api/admin/reset-password/:userid` **Headers:** ``` Authorization: Bearer ``` **Response:** ```json { "success": true, "message": "Password reset successfully" } ``` ## Using Tokens ### Authorization Header Include the access token in all protected requests: ```bash curl -H "Authorization: Bearer eyJhbGciOi..." \ https://api.yourschool.edu/api/students/list ``` ### Token Structure The JWT contains these claims: ```json { "sub": "user-uuid", "email": "user@school.edu", "exp": 1704067200, "iat": 1703980800, "type": "access" } ``` | Claim | Description | |-------|-------------| | `sub` | User ID | | `email` | User email | | `exp` | Expiration timestamp | | `iat` | Issued at timestamp | | `type` | Token type (`access` or `refresh`) | ## Security Features ### Token Blacklisting - Logout invalidates the refresh token - Changing password invalidates all tokens - Admin can revoke all user tokens ### Rate Limiting Login attempts are rate limited: - 5 failed attempts: 1-minute lockout - 10 failed attempts: 5-minute lockout - 20 failed attempts: Account temporary lock ### Security Audit All authentication events are logged: - Login attempts (success/failure) - Token refresh - Logout - Password changes ## Code Examples ### JavaScript/TypeScript ```typescript class AuthService { private accessToken: string | null = null; private refreshToken: string | null = null; async login(email: string, password: string): Promise { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (!response.ok) { throw new Error('Login failed'); } const data = await response.json(); this.accessToken = data.token.access_token; this.refreshToken = data.token.refresh_token; } async refreshTokens(): Promise { const response = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: this.refreshToken }) }); if (!response.ok) { // Refresh failed, need to re-login this.accessToken = null; this.refreshToken = null; throw new Error('Session expired'); } const data = await response.json(); this.accessToken = data.access_token; this.refreshToken = data.refresh_token; } getAuthHeaders(): Record { return { 'Authorization': `Bearer ${this.accessToken}` }; } } ``` ### Axios Interceptor ```typescript import axios from 'axios'; const api = axios.create({ baseURL: 'https://api.yourschool.edu/api' }); // Request interceptor - add token api.interceptors.request.use((config) => { const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Response interceptor - handle token refresh api.interceptors.response.use( (response) => response, async (error) => { if (error.response?.status === 401) { const refreshToken = localStorage.getItem('refresh_token'); if (refreshToken) { try { const response = await axios.post('/api/auth/refresh', { refresh_token: refreshToken }); localStorage.setItem('access_token', response.data.access_token); localStorage.setItem('refresh_token', response.data.refresh_token); // Retry original request error.config.headers.Authorization = `Bearer ${response.data.access_token}`; return api.request(error.config); } catch { // Refresh failed, redirect to login window.location.href = '/login'; } } } return Promise.reject(error); } ); ``` ## SSO / OIDC Login The system also supports Single Sign-On via OIDC providers. See [OIDC Setup Guide](../guides/oidc-setup.md) for configuration. ### OIDC Flow 1. Redirect to `/api/oidc/login?provider=` 2. User authenticates with identity provider 3. Callback to `/api/oidc/callback` with authorization code 4. System exchanges code for tokens and creates/updates user 5. Returns JWT tokens for API access