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