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

4.1 KiB

Microsoft OAuth Token Management - Fixes Applied

Issues Fixed

Fix #1: Refresh Tokens Now Persisted to Prisma Database

Problem: Refresh tokens were only stored in Redis (24-hour TTL), risking permanent loss.

Solution:

  • Refresh tokens are now saved to MailCredentials.refresh_token in Prisma
  • Access tokens and expiry also persisted to database
  • Database acts as source of truth for long-term token storage

Files Modified:

  • lib/services/email-service.ts - saveUserEmailCredentials() now saves OAuth tokens to Prisma

Fix #2: Database Updated on Token Refresh

Problem: When tokens were refreshed, only Redis was updated, leaving database stale.

Solution:

  • Token refresh now updates both Redis AND Prisma
  • New refresh tokens (if provided by Microsoft) are persisted
  • Token expiry timestamp updated in database

Files Modified:

  • lib/services/token-refresh.ts - ensureFreshToken() now updates Prisma after refresh

Fix #3: Fallback to Database if Redis Missing

Problem: If Redis cache was empty, system couldn't recover refresh tokens.

Solution:

  • If Redis cache miss, system checks Prisma database
  • Retrieves refresh token from database
  • Re-populates Redis cache for future use

Files Modified:

  • lib/services/token-refresh.ts - Added database fallback logic

Fix #4: OAuth Fields Retrieved from Database

Problem: When loading credentials from database, OAuth fields were ignored.

Solution:

  • Database queries now include OAuth fields (access_token, refresh_token, token_expiry, use_oauth)
  • Credentials object properly populated with OAuth data from database

Files Modified:

  • lib/services/email-service.ts - getImapConnection() now includes OAuth fields from database

Token Storage Strategy (Current)

Access Tokens

  • Primary: Redis (fast access, 24-hour TTL)
  • Backup: Prisma Database (persisted)
  • Lifespan: ~1 hour (Microsoft default)

Refresh Tokens

  • Primary: Prisma Database (persistent, long-term)
  • Cache: Redis (24-hour TTL, for fast access)
  • Lifespan: Up to 90 days (with offline_access scope)

Token Expiry

  • Storage: Both Redis and Prisma
  • Purpose: Determine when to refresh tokens

Long-Term Viability

NOW VIABLE for Production

Improvements:

  1. Refresh tokens persisted to database
  2. Database updated on token refresh
  3. Fallback mechanism if Redis fails
  4. No data loss on Redis restart
  5. Recovery mechanism in place

What Happens Now

When Adding Microsoft Account:

  1. OAuth tokens saved to both Redis and Prisma
  2. Refresh token stored in database for long-term access
  3. Access token cached in Redis for fast retrieval

When Token Expires:

  1. System checks Redis first (fast path)
  2. If Redis miss, checks Prisma database (fallback)
  3. Uses refresh token to get new access token
  4. Updates both Redis and Prisma with new tokens
  5. Continues normal operation

If Redis is Cleared:

  1. System detects Redis cache miss
  2. Retrieves refresh token from Prisma database
  3. Gets new access token using refresh token
  4. Re-populates Redis cache
  5. No user action required

Testing Recommendations

  1. Test Token Refresh:

    • Wait for access token to expire (~1 hour)
    • Verify system automatically refreshes
    • Check both Redis and Prisma are updated
  2. Test Redis Failure:

    • Clear Redis cache
    • Try to access email
    • Verify system recovers from database
  3. Test Long-Term Access:

    • Wait several days
    • Verify refresh token still works
    • Check no re-authentication required

Monitoring

Watch for these log messages:

  • Token for ${email} persisted to Prisma database - Token saved successfully
  • Recovered credentials from Prisma and cached in Redis - Fallback working
  • ⚠️ Error persisting tokens to database - Database update failed (check logs)

Next Steps

  1. Monitor: Watch logs for token refresh operations
  2. Verify: Check Prisma database has refresh_token values
  3. Test: Verify email access works after Redis restart
  4. Optional: Consider encrypting tokens at rest (if compliance requires)