# Refresh Token Implementation

## Overview

This document describes the refresh token functionality implemented for StoryCove, allowing users to stay authenticated for up to 2 weeks with automatic token refresh.

## Architecture

### Token Types

1. **Access Token (JWT)**
   - Lifetime: 24 hours
   - Stored in: httpOnly cookie + localStorage
   - Used for: API authentication
   - Format: JWT with subject and libraryId claims

2. **Refresh Token**
   - Lifetime: 14 days (2 weeks)
   - Stored in: httpOnly cookie + database
   - Used for: Generating new access tokens
   - Format: Secure random 256-bit token (Base64 encoded)

### Token Flow

1. **Login**
   - User provides password
   - Backend validates password
   - Backend generates both access token and refresh token
   - Both tokens sent as httpOnly cookies
   - Access token also returned in response body for localStorage

2. **API Request**
   - Frontend sends access token via Authorization header and cookie
   - Backend validates access token
   - If valid: Request proceeds
   - If expired: Frontend attempts token refresh

3. **Token Refresh**
   - Frontend detects 401/403 response
   - Frontend automatically calls `/api/auth/refresh`
   - Backend validates refresh token from cookie
   - If valid: New access token generated and returned
   - If invalid/expired: User redirected to login

4. **Logout**
   - Frontend calls `/api/auth/logout`
   - Backend revokes refresh token in database
   - Both cookies cleared
   - User redirected to login page

## Backend Implementation

### New Files

1. **`RefreshToken.java`** - Entity class
   - Fields: id, token, expiresAt, createdAt, revokedAt, libraryId, userAgent, ipAddress
   - Helper methods: isExpired(), isRevoked(), isValid()

2. **`RefreshTokenRepository.java`** - Repository interface
   - findByToken(String)
   - deleteExpiredTokens(LocalDateTime)
   - revokeAllByLibraryId(String, LocalDateTime)
   - revokeAll(LocalDateTime)

3. **`RefreshTokenService.java`** - Service class
   - createRefreshToken(libraryId, userAgent, ipAddress)
   - verifyRefreshToken(token)
   - revokeToken(token)
   - revokeAllByLibraryId(libraryId)
   - cleanupExpiredTokens() - Scheduled daily at 3 AM

### Modified Files

1. **`JwtUtil.java`**
   - Added `refreshExpiration` property (14 days)
   - Added `generateRefreshToken()` method
   - Added `getRefreshExpirationMs()` method

2. **`AuthController.java`**
   - Updated `/login` endpoint to create and return refresh token
   - Added `/refresh` endpoint to handle token refresh
   - Updated `/logout` endpoint to revoke refresh token
   - Added helper methods: `getRefreshTokenFromCookies()`, `getClientIpAddress()`

3. **`SecurityConfig.java`**
   - Added `/api/auth/refresh` to public endpoints

4. **`application.yml`**
   - Added `storycove.jwt.refresh-expiration: 1209600000` (14 days)

## Frontend Implementation

### Modified Files

1. **`api.ts`**
   - Added automatic token refresh logic in response interceptor
   - Added request queuing during token refresh
   - Prevents multiple simultaneous refresh attempts
   - Automatically retries failed requests after refresh

### Token Refresh Logic

```typescript
// On 401/403 response:
1. Check if already retrying -> if yes, queue request
2. Check if refresh/login endpoint -> if yes, logout
3. Attempt token refresh via /api/auth/refresh
4. If successful:
   - Update localStorage with new token
   - Retry original request
   - Process queued requests
5. If failed:
   - Clear token
   - Redirect to login
   - Reject queued requests
```

## Security Features

1. **httpOnly Cookies**: Prevents XSS attacks
2. **Token Revocation**: Refresh tokens can be revoked
3. **Database Storage**: Refresh tokens stored server-side
4. **Expiration Tracking**: Tokens have strict expiration dates
5. **IP & User Agent Tracking**: Stored for security auditing
6. **Library Isolation**: Tokens scoped to specific library

## Database Schema

```sql
CREATE TABLE refresh_tokens (
    id UUID PRIMARY KEY,
    token VARCHAR(255) UNIQUE NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP NOT NULL,
    revoked_at TIMESTAMP,
    library_id VARCHAR(255),
    user_agent VARCHAR(255) NOT NULL,
    ip_address VARCHAR(255) NOT NULL
);

CREATE INDEX idx_refresh_token ON refresh_tokens(token);
CREATE INDEX idx_expires_at ON refresh_tokens(expires_at);
```

## Configuration

### Backend (`application.yml`)

```yaml
storycove:
  jwt:
    expiration: 86400000        # 24 hours (access token)
    refresh-expiration: 1209600000  # 14 days (refresh token)
```

### Environment Variables

No new environment variables required. Existing `JWT_SECRET` is used.

## Testing

Comprehensive test suite in `RefreshTokenServiceTest.java`:
- Token creation
- Token validation
- Expired token handling
- Revoked token handling
- Token revocation
- Cleanup operations

Run tests:
```bash
cd backend
mvn test -Dtest=RefreshTokenServiceTest
```

## Maintenance

### Automated Cleanup

Expired tokens are automatically cleaned up daily at 3 AM via scheduled task in `RefreshTokenService.cleanupExpiredTokens()`.

### Manual Revocation

```java
// Revoke all tokens for a library
refreshTokenService.revokeAllByLibraryId("library-id");

// Revoke all tokens (logout all users)
refreshTokenService.revokeAll();
```

## User Experience

1. **Seamless Authentication**: Users stay logged in for 2 weeks
2. **Automatic Refresh**: Token refresh happens transparently
3. **No Interruptions**: API calls succeed even when access token expires
4. **Backend Restart**: Users must re-login (JWT secret rotates on startup)
5. **Cross-Device Library Switching**: Automatic library switching when using different devices with different libraries

## Cross-Device Library Switching

### Feature Overview

The system automatically detects and switches libraries when you use different devices authenticated to different libraries. This ensures you always see the correct library's data.

### How It Works

**Scenario 1: Active Access Token (within 24 hours)**
1. Request comes in with valid JWT access token
2. `JwtAuthenticationFilter` extracts `libraryId` from token
3. Compares with `currentLibraryId` in backend
4. **If different**: Automatically switches to token's library
5. **If same**: Early return (no overhead, just string comparison)
6. Request proceeds with correct library

**Scenario 2: Token Refresh (after 24 hours)**
1. Access token expired, refresh token still valid
2. `/api/auth/refresh` endpoint validates refresh token
3. Extracts `libraryId` from refresh token
4. Compares with `currentLibraryId` in backend
5. **If different**: Automatically switches to token's library
6. **If same**: Early return (no overhead)
7. Generates new access token with correct `libraryId`

**Scenario 3: After Backend Restart**
1. `currentLibraryId` is null (no active library)
2. First request with any token automatically switches to that token's library
3. Subsequent requests use early return optimization

### Performance

**When libraries match** (most common case):
- Simple string comparison: `libraryId.equals(currentLibraryId)`
- Immediate return - zero overhead
- No datasource changes, no reindexing

**When libraries differ** (switching devices):
- Synchronized library switch
- Datasource routing updated instantly
- Solr reindex runs asynchronously (doesn't block request)
- Takes 2-3 seconds in background

### Edge Cases

**Multi-device simultaneous use:**
- If two devices with different libraries are used simultaneously
- Last request "wins" and switches backend to its library
- Not recommended but handled gracefully
- Each device corrects itself on next request

**Library doesn't exist:**
- If token contains invalid `libraryId`
- Library switch fails with error
- Request is rejected with 500 error
- User must re-login with valid credentials

## Future Enhancements

Potential improvements:
1. Persistent JWT secret (survive backend restarts)
2. Sliding refresh token expiration (extend on use)
3. Multiple device management (view/revoke sessions)
4. Configurable token lifetimes via environment variables
5. Token rotation (new refresh token on each use)
6. Thread-local library context for true stateless operation

## Summary

The refresh token implementation provides a robust, secure authentication system that balances user convenience (2-week sessions) with security (short-lived access tokens, automatic refresh). The implementation follows industry best practices and provides a solid foundation for future enhancements.
