Files
KArtSell.Aegis/docs/JWT_AUTHENTICATION.md
T
kjh2064 f38581ff4a
deploy / deploy (push) Successful in 1m51s
deploy / notify (push) Successful in 1s
docs: JWT authentication implementation guide and production config
- Added comprehensive JWT_AUTHENTICATION.md documentation
- Covers backend (JwtAuthenticationHandler, LoginEndpoint) and frontend (useAuthApi, LoginPage)
- Includes configuration for development and production modes
- Security considerations and best practices
- Token refresh enhancement recommendations
- Testing guide and troubleshooting
- API contract documentation
- Deployment checklist

- Added appsettings.Release.json for production JWT configuration
- Placeholders for environment-specific values (JWT_KEY)
- Proper listen address (0.0.0.0:5002) for containerized deployments

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 00:18:48 +09:00

6.6 KiB

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

{
  "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

{
  "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:

// 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

dotnet test KArtSell.sln -c Release

Frontend Unit Tests

cd frontend
pnpm test

Local Testing (Development Mode)

# 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)

# 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

{
  "username": "john_doe",
  "password": "secure_password",
  "role": "Admin"  // optional
}

Success Response (200)

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 3600,
  "tokenType": "Bearer"
}

Error Response (401)

{
  "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