# JWT Token Authentication ## Overview K-ArtSell Aegis uses JWT (JSON Web Token) for production authentication, replacing the Development-only header-based authentication. ## Architecture ### Backend (ASP.NET Core) **JwtAuthenticationHandler** (`src/KArtSell.Host/Security/JwtAuthenticationHandler.cs`) - Validates Bearer tokens from `Authorization` header - Verifies signature using HS256 algorithm - Validates issuer, audience, and expiration - Extracts claims: NameIdentifier, Name, Role, auth_mode **LoginEndpoint** (`src/KArtSell.Host/Endpoints/Auth/LoginEndpoint.cs`) - `POST /api/auth/login` - Issues JWT tokens - Request: `{ username, password, role? }` - Response: `{ accessToken, expiresIn, tokenType: "Bearer" }` ### Frontend (Vue 3) **useAuthApi** (`frontend/src/features/auth/composables/useAuthApi.ts`) - Token lifecycle: login, logout, getToken - Token persistence: localStorage - Expiration tracking and validation - Automatic cleanup on expiration **LoginPage** (`frontend/src/features/auth/pages/LoginPage.vue`) - Username/password form - Token acquisition on successful login - Redirect to home on auth success **Auth Interceptor** - Global fetch interceptor (setupAuthInterceptor) - Automatically adds `Authorization: Bearer {token}` to all requests - Initialized in `main.ts` ## Configuration ### Development Mode File: `appsettings.json` ```json { "Authentication": { "Mode": "DevelopmentHeader" }, "Jwt": { "Key": "KArtSell.Aegis.SecretKey.256Bits.v1.2026.Development.1234567890ABCDEF", "Issuer": "KArtSell.Aegis", "Audience": "KArtSell.Aegis", "ExpirationMinutes": 60 } } ``` **Backend**: Reads `X-KArtSell-User` and `X-KArtSell-Role` headers **Frontend**: Skips login, uses static headers in API requests ### Production Mode File: `appsettings.Release.json` ```json { "Authentication": { "Mode": "JWT" }, "Jwt": { "Key": "${JWT_KEY}", "Issuer": "KArtSell.Aegis", "Audience": "KArtSell.Aegis", "ExpirationMinutes": 60 } } ``` **Environment Variable**: Set `JWT_KEY` during deployment - Must be at least 256 bits (32 bytes) for HMAC SHA256 - Use cryptographically secure random string (e.g., `openssl rand -hex 32`) ## Usage ### Development 1. Backend starts with DevelopmentHeaderAuthenticationHandler 2. Frontend requests include static `X-KArtSell-User`/`X-KArtSell-Role` headers 3. No login required for testing ### Production 1. User navigates to application 2. Router redirects to `/login` 3. User enters credentials 4. Frontend calls `POST /api/auth/login` 5. Backend validates credentials and returns JWT token 6. Frontend stores token in localStorage 7. All subsequent requests include `Authorization: Bearer {token}` 8. Backend validates token in each request ## Security Considerations ### Token Storage - Tokens stored in localStorage (accessible to XSS attacks) - For sensitive applications, consider using httpOnly cookies ### Token Expiration - Default: 60 minutes - Configurable via `Jwt:ExpirationMinutes` - Frontend automatically detects expiration and logs out ### Credential Validation - Current implementation accepts any non-empty username/password - **TODO**: Integrate with identity database for real validation - Add rate limiting for login attempts - Hash passwords with bcrypt/argon2 ### HTTPS Only (Production) - Always use HTTPS in production - Set `Secure` flag on cookies if using cookie-based tokens - Implement token rotation/refresh mechanism ## Token Refresh (Optional Enhancement) For long-running applications, implement refresh token flow: 1. Add `RefreshTokenEndpoint` (`POST /api/auth/refresh`) 2. Issue longer-lived refresh tokens (1 week) 3. Implement automatic token refresh in frontend 4. Add refresh token rotation to prevent token reuse Example implementation: ```typescript // useAuthApi.ts - future enhancement async function refreshToken() { const response = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: getRefreshToken() }) }) // Store new token } ``` ## Testing ### Backend Unit Tests ```bash dotnet test KArtSell.sln -c Release ``` ### Frontend Unit Tests ```bash cd frontend pnpm test ``` ### Local Testing (Development Mode) ```bash # Terminal 1: SSH tunnel ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7 # Terminal 2: Backend cd src/KArtSell.Host dotnet run -c Debug # Terminal 3: Frontend cd frontend pnpm dev ``` Visit `http://localhost:5174` ### Local Testing (Production Mode - JWT) ```bash # Backend (Release mode) dotnet run -c Release --project src/KArtSell.Host # Frontend (will show login) pnpm dev # Login with any username/password # Will receive JWT token and be redirected to home ``` ## Troubleshooting ### 401 Unauthorized (Release Mode) - Missing `JWT_KEY` environment variable - Invalid/expired JWT token - Token not included in Authorization header ### Token Not Persisting - Check localStorage is enabled (not in private/incognito mode) - Check browser console for storage quota errors ### Clock Skew Issues - Server/client time out of sync - Default clock skew: 30 seconds (configurable) - Ensure server time is synchronized (NTP) ## API Contract ### POST /api/auth/login **Request** ```json { "username": "john_doe", "password": "secure_password", "role": "Admin" // optional } ``` **Success Response (200)** ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600, "tokenType": "Bearer" } ``` **Error Response (401)** ```json { "type": "about:blank", "title": "Unauthorized", "status": 401 } ``` ### Protected Endpoints **Header** ``` Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **Invalid Token (401)** ``` Authorization: Bearer invalid_token ``` ## Deployment Checklist - [ ] Set `JWT_KEY` environment variable (256+ bit secure random) - [ ] Configure `Jwt:Issuer` and `Jwt:Audience` to match environment - [ ] Update `Jwt:ExpirationMinutes` based on security requirements - [ ] Enable HTTPS only (redirect HTTP to HTTPS) - [ ] Set up database validation for credentials (not mock) - [ ] Implement token refresh mechanism (optional but recommended) - [ ] Configure rate limiting on `/api/auth/login` - [ ] Enable audit logging for authentication events - [ ] Test login flow end-to-end in staging environment ## References - [JWT.io](https://jwt.io) - JWT debugger and documentation - [Microsoft Identity Model Documentation](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) - [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)