NeahNew/MICROSOFT_OAUTH_ANALYSIS.md
2026-01-01 19:31:48 +01:00

154 lines
4.8 KiB
Markdown

# Microsoft OAuth Token Management Analysis
## Current Implementation
### Token Storage Locations
1. **Redis Cache** (Primary for OAuth tokens)
- **Location**: `lib/redis.ts``cacheEmailCredentials()`
- **TTL**: 24 hours (`TTL.CREDENTIALS = 60 * 60 * 24`)
- **Stored**: `accessToken`, `refreshToken`, `tokenExpiry`, `useOAuth`
- **Key Format**: `email:credentials:${userId}:${accountId}`
2. **Prisma Database** (Schema has fields but NOT used for OAuth tokens)
- **Location**: `prisma/schema.prisma``MailCredentials` model
- **Fields Available**: `refresh_token`, `access_token`, `token_expiry`, `use_oauth`
- **Current Status**: ❌ **Tokens are NOT saved to Prisma** (only Redis)
- **Code Comment**: "OAuth fields don't exist" (but they DO exist in schema!)
### Token Refresh Flow
**Location**: `lib/services/token-refresh.ts``ensureFreshToken()`
1. Checks Redis for credentials
2. Validates token expiry (5-minute buffer)
3. Refreshes token if needed via Microsoft API
4. **Updates Redis only** (not Prisma)
5. Returns new access token
### Issues Identified
#### 🔴 Critical Issue #1: Refresh Tokens Not Persisted to Database
**Problem**:
- Refresh tokens are only stored in Redis with 24-hour TTL
- If Redis is cleared, restarted, or TTL expires, refresh tokens are **permanently lost**
- Microsoft refresh tokens can last up to **90 days** (or indefinitely with `offline_access` scope)
- Users would need to re-authenticate if Redis data is lost
**Impact**:
- ❌ Not viable for long-term production use
- ❌ Data loss risk on Redis restarts
- ❌ No backup/recovery mechanism
#### 🟡 Issue #2: Token Refresh Doesn't Update Database
**Problem**:
- When tokens are refreshed, only Redis is updated
- Prisma database still has old/expired tokens (if any)
- Schema has the fields but they're never populated
**Impact**:
- ⚠️ Inconsistency between Redis and Database
- ⚠️ Can't recover from Redis cache loss
#### 🟡 Issue #3: Missing Refresh Token in Logs
From your logs:
```
hasRefreshToken: false
```
This suggests the refresh token might not be properly saved or retrieved.
### Microsoft OAuth Token Lifespan
- **Access Token**: ~1 hour (3600 seconds)
- **Refresh Token**: Up to 90 days (with `offline_access` scope)
- **Token Refresh**: Returns new access token, may return new refresh token
### Required Scopes
Current implementation uses:
```typescript
const REQUIRED_SCOPES = [
'offline_access', // ✅ Required for long-lived refresh tokens
'https://outlook.office.com/IMAP.AccessAsUser.All',
'https://outlook.office.com/SMTP.Send'
].join(' ');
```
`offline_access` is included - this is correct for long-term use.
## Recommendations
### ✅ Fix #1: Persist Refresh Tokens to Prisma
**Why**: Refresh tokens are critical for long-term access and should be persisted to database.
**Implementation**:
1. Save `refresh_token` to Prisma `MailCredentials.refresh_token` field
2. Update `token_expiry` when tokens are refreshed
3. Keep access tokens in Redis (short-lived, can be regenerated)
4. Use Prisma as source of truth for refresh tokens
### ✅ Fix #2: Update Database on Token Refresh
**Why**: Keep database in sync with refreshed tokens.
**Implementation**:
1. After refreshing tokens, update Prisma `MailCredentials` record
2. Update `access_token` and `token_expiry` fields
3. Update `refresh_token` if Microsoft returns a new one
### ✅ Fix #3: Fallback to Database if Redis Missing
**Why**: Recover from Redis cache loss.
**Implementation**:
1. If Redis cache is empty, check Prisma for refresh token
2. Use Prisma refresh token to get new access token
3. Re-populate Redis cache
## Long-Term Viability Assessment
### Current State: ⚠️ **NOT VIABLE** for long-term production
**Reasons**:
1. ❌ Refresh tokens only in volatile Redis cache
2. ❌ No persistence mechanism
3. ❌ Risk of data loss on Redis restart
4. ❌ No recovery mechanism
### After Fixes: ✅ **VIABLE** for long-term production
**With recommended fixes**:
1. ✅ Refresh tokens persisted to database
2. ✅ Redis used for fast access token retrieval
3. ✅ Database as source of truth
4. ✅ Recovery mechanism in place
## Token Storage Strategy (Recommended)
### Access Tokens
- **Storage**: Redis (fast, short-lived)
- **TTL**: 1 hour (matches Microsoft token expiry)
- **Purpose**: Fast IMAP/SMTP authentication
### Refresh Tokens
- **Storage**: Prisma Database (persistent, long-term)
- **TTL**: None (stored indefinitely until revoked)
- **Purpose**: Long-term access, token renewal
### Token Expiry
- **Storage**: Both Redis and Prisma
- **Purpose**: Know when to refresh tokens
## Implementation Priority
1. **HIGH**: Persist refresh tokens to Prisma
2. **HIGH**: Update Prisma on token refresh
3. **MEDIUM**: Add fallback to database if Redis missing
4. **LOW**: Add token encryption at rest (if required by compliance)