# Apple OAuth Maintenance Guide

## Overview

AMSOIL Dealer Pages uses Apple Sign In as one of two OAuth authentication providers (alongside Google). Apple's OAuth implementation requires a **JWT client secret** that must be **regenerated periodically** due to its 180-day expiration limit.

### Why Apple Requires JWT Client Secrets

Unlike Google OAuth (which uses a static client secret), Apple Sign In requires:
- **ES256-signed JWT** (Elliptic Curve Digital Signature Algorithm)
- **Private key** from Apple Developer Portal
- **Maximum expiration:** 180 days (6 months)
- **Regeneration required:** Before expiration to maintain service

### Current Implementation Strategy

**Generation:** Server startup (module load time)
**Caching:** JWT stored in memory and reused for all requests
**Expiration:** 180 days from server start
**Regeneration:** Automatic on server restart/redeploy

**Key Insight:** The JWT is generated once when the Node.js process starts and remains valid for 180 days. Simply restarting the server regenerates the JWT automatically.

---

## Required Credentials

Apple OAuth requires **4 environment variables** from the Apple Developer Portal:

| Variable | Description | Example | Where to Find |
|----------|-------------|---------|---------------|
| `APPLE_ID` | Service ID (identifier) | `com.amsoil.dealerpages.signin` | [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list/serviceId) |
| `APPLE_TEAM_ID` | 10-character Team ID | `A1B2C3D4E5` | [Membership Details](https://developer.apple.com/account/#/membership/) |
| `APPLE_PRIVATE_KEY` | ES256 private key (PKCS8 PEM format) | `-----BEGIN PRIVATE KEY-----\n...` | [Keys section](https://developer.apple.com/account/resources/authkeys/list) - download once |
| `APPLE_KEY_ID` | 10-character Key ID | `F6G7H8I9J0` | [Keys section](https://developer.apple.com/account/resources/authkeys/list) |

**Important:** The private key can only be downloaded **once** when created. Store it securely!

---

## JWT Expiration Timeline

```
Day 0:    Server starts → JWT generated (180-day expiration)
Day 150:  ⚠️ SET CALENDAR REMINDER (30 days before expiration)
Day 175:  ⚠️ URGENT: Schedule restart within 5 days
Day 180:  ❌ JWT EXPIRES - Apple sign-in will fail silently
Day 181:  Server restart → New JWT generated → Service restored
```

### What Happens When JWT Expires?

- ✅ Google sign-in continues to work
- ❌ Apple sign-in fails **silently** (no error shown to users)
- ❌ Users with Apple accounts cannot sign in
- ⚠️ No automatic alerts (manual monitoring required)

**Silent Failure:** Apple's OAuth implementation doesn't return clear error messages for expired JWTs. Users will see a generic authentication error.

---

## Monitoring

### Manual Monitoring (Current Implementation)

**When JWT was generated:** Check application startup logs for:
```
✓ Apple OAuth provider initialized successfully
```

**When JWT will expire:** Add 180 days to the server start date.

### Setting Up Monitoring

#### Option 1: Calendar Reminder (Minimum Requirement)
1. Note the last production deployment date
2. Add 150 days → Set "Check Apple JWT" reminder
3. Add 175 days → Set "URGENT: Restart server" reminder

**Example:**
- Deployed: January 15, 2025
- First reminder: June 14, 2025 (150 days)
- Urgent reminder: July 9, 2025 (175 days)
- Expiration: July 14, 2025 (180 days)

#### Option 2: Automated Monitoring (Future Enhancement)

Add to application logs:
```typescript
// Log JWT expiration date at startup
const jwtExpiresAt = new Date(Date.now() + 180 * 24 * 60 * 60 * 1000);
authLogger.info({ jwtExpiresAt }, 'Apple JWT will expire on');
```

Set up alerts:
- **Datadog/Sentry:** Create monitor for JWT expiration date
- **Cron job:** Check expiration date and send alert 30 days before
- **Application metric:** Expose JWT expiration as Prometheus metric

---

## Regeneration Procedure

### Production Environment

**Prerequisites:**
- Maintenance window scheduled (5-10 minutes)
- Team notified
- Rollback plan ready

**Steps:**

1. **Verify Current JWT Status**
   ```bash
   # Check application logs for last JWT generation
   grep "Apple OAuth provider initialized" /var/log/app.log
   ```

2. **Schedule Server Restart**
   - Coordinate with team
   - Choose low-traffic time window
   - Notify users if necessary (though restart should be quick)

3. **Restart Application Server**
   ```bash
   # Example for PM2
   pm2 restart amsoil-dlp

   # Example for Kubernetes
   kubectl rollout restart deployment/amsoil-dlp

   # Example for systemd
   sudo systemctl restart amsoil-dlp

   # Example for Docker
   docker restart amsoil-dlp
   ```

4. **Verify Apple OAuth Functionality**
   ```bash
   # Check logs for successful JWT generation
   grep "Apple OAuth provider initialized" /var/log/app.log | tail -1

   # Expected output:
   # ✓ Apple OAuth provider initialized successfully
   ```

5. **Test Apple Sign In**
   - Visit sign-in page: https://yourdomain.com/auth/signin
   - Click "Continue with Apple"
   - Complete sign-in flow
   - Verify successful authentication

6. **Update Calendar Reminder**
   - Calculate new expiration date (today + 180 days)
   - Update calendar reminders accordingly

### Staging/Development Environment

**Simpler procedure for non-production:**

```bash
# Just restart the dev server
npm run dev

# Or restart with PM2
pm2 restart amsoil-dlp-dev
```

**Verification:**
- Check terminal output for: `✓ Apple OAuth provider initialized successfully`
- Test Apple sign-in on staging environment

---

## Troubleshooting

### Symptom: Apple Sign In Fails Silently

**Possible Causes:**

1. **Expired JWT (Most Common)**
   - **Check:** Server uptime > 180 days?
   - **Fix:** Restart server immediately
   - **Prevention:** Set calendar reminders

2. **Missing HTTPS in Development**
   - **Check:** Is `NEXTAUTH_URL` using `http://`?
   - **Error Log:**
     ```
     ⚠️  APPLE OAUTH CONFIGURATION ERROR ⚠️
     Apple OAuth requires HTTPS for all URLs, including development.
     ```
   - **Fix:** Use ngrok (see Development Setup below)

3. **Incorrect Private Key Format**
   - **Check:** Does key include `-----BEGIN PRIVATE KEY-----` header?
   - **Error Log:**
     ```
     APPLE_PRIVATE_KEY must be in PKCS8 PEM format
     ```
   - **Fix:** Re-download key from Apple Developer Portal

4. **Invalid Credentials**
   - **Check:** All 4 environment variables set correctly?
   - **Error Log:**
     ```
     ✗ Apple OAuth initialization failed
     ```
   - **Fix:** Verify credentials match Apple Developer Portal

### Debugging Commands

```bash
# Check if Apple OAuth is configured
grep "Apple OAuth" /var/log/app.log

# Check environment variables (production)
printenv | grep APPLE_

# Test JWT generation locally (requires credentials)
node -e "
const { SignJWT } = require('jose');
const { createPrivateKey } = require('crypto');

const privateKey = process.env.APPLE_PRIVATE_KEY.replace(/\\\\n/g, '\\n');
const key = createPrivateKey(privateKey);

new SignJWT({})
  .setProtectedHeader({ alg: 'ES256', kid: process.env.APPLE_KEY_ID })
  .setAudience('https://appleid.apple.com')
  .setIssuer(process.env.APPLE_TEAM_ID)
  .setIssuedAt()
  .setExpirationTime('180d')
  .setSubject(process.env.APPLE_ID)
  .sign(key)
  .then(jwt => console.log('JWT generated successfully:', jwt.substring(0, 50) + '...'))
  .catch(err => console.error('JWT generation failed:', err.message));
"
```

### Emergency Recovery

**If JWT expires and users cannot sign in:**

1. **Immediate Action:** Restart production server
2. **Verify Fix:** Test Apple sign-in works
3. **Communication:**
   - If downtime was significant (>1 hour), notify affected users
   - Explain that Google sign-in remained available
4. **Post-Mortem:**
   - Document when/why expiration was missed
   - Improve monitoring and alerts
   - Update procedures

---

## Development Setup

### The HTTPS Requirement

Apple OAuth **requires HTTPS** even for development environments. HTTP URLs (like `http://localhost:3000`) will cause silent authentication failures.

### Option 1: Using ngrok (Recommended)

**ngrok** creates a secure HTTPS tunnel to your local development server.

#### Installation

```bash
# macOS (Homebrew)
brew install ngrok

# Or download from https://ngrok.com/download
```

#### Setup Steps

1. **Start your development server**
   ```bash
   npm run dev
   # Server running on http://localhost:3000
   ```

2. **Start ngrok tunnel**
   ```bash
   ngrok http 3000
   ```

3. **Copy the HTTPS URL from ngrok output**
   ```
   Forwarding   https://abc123.ngrok.io -> http://localhost:3000
   ```

4. **Update `.env.local`**
   ```bash
   NEXTAUTH_URL=https://abc123.ngrok.io
   ```

5. **Update Apple Developer Portal**
   - Go to [Services IDs](https://developer.apple.com/account/resources/identifiers/list/serviceId)
   - Edit your Service ID
   - Add redirect URI: `https://abc123.ngrok.io/api/auth/callback/apple`
   - Save changes

6. **Restart development server**
   ```bash
   npm run dev
   ```

7. **Test Apple Sign In**
   - Visit: `https://abc123.ngrok.io/auth/signin`
   - Click "Continue with Apple"
   - Complete authentication

#### ngrok Tips

- **Free tier:** URL changes every time you restart ngrok
- **Paid tier:** Get static subdomain (recommended for frequent dev)
- **Security:** ngrok URLs are publicly accessible
- **Alternative:** VS Code port forwarding (GitHub Codespaces)

### Option 2: Temporarily Disable Apple OAuth

For quick local testing without HTTPS setup:

```bash
# .env.local - Comment out Apple credentials
# APPLE_ID=...
# APPLE_TEAM_ID=...
# APPLE_PRIVATE_KEY=...
# APPLE_KEY_ID=...
```

**Result:**
- Google OAuth remains functional
- Apple sign-in button hidden
- No errors in logs

**When to use:** Quick local testing, focus on non-auth features

---

## Production Deployment

### Pre-Deployment Checklist

- [ ] **Environment Variables Set**
  - [ ] `APPLE_ID` configured
  - [ ] `APPLE_TEAM_ID` configured
  - [ ] `APPLE_PRIVATE_KEY` configured (with escaped newlines)
  - [ ] `APPLE_KEY_ID` configured

- [ ] **NEXTAUTH_URL Configuration**
  - [ ] Uses `https://` protocol ✅
  - [ ] NOT using `http://` ❌
  - [ ] Matches production domain
  - [ ] Example: `https://amsoildealerpages.com`

- [ ] **Apple Developer Portal Configuration**
  - [ ] Service ID exists
  - [ ] Redirect URIs include: `https://yourdomain.com/api/auth/callback/apple`
  - [ ] Service enabled for Sign In with Apple
  - [ ] Domain verification completed

- [ ] **Testing**
  - [ ] Apple sign-in tested on staging environment
  - [ ] JWT generates successfully (check startup logs)
  - [ ] No HTTPS warnings in logs
  - [ ] End-to-end authentication flow works

### Post-Deployment

1. **Verify JWT Generation**
   ```bash
   # Check production logs
   tail -f /var/log/app.log | grep "Apple OAuth"

   # Expected output:
   # ✓ Apple OAuth provider initialized successfully
   ```

2. **Test Production Apple Sign In**
   - Visit: `https://yourdomain.com/auth/signin`
   - Click "Continue with Apple"
   - Verify successful authentication

3. **Set Calendar Reminders**
   - Deployment date: [TODAY]
   - First reminder: [TODAY + 150 days] - "Check Apple JWT"
   - Second reminder: [TODAY + 175 days] - "URGENT: Restart server"
   - Expiration date: [TODAY + 180 days] - "JWT expires"

4. **Document Deployment**
   ```
   Production Deployment Log
   Date: 2025-01-15
   Apple JWT Generated: 2025-01-15 14:30 UTC
   Apple JWT Expires: 2025-07-14 14:30 UTC
   Next Action Required: 2025-06-14 (JWT check)
   ```

---

## Operational Checklist

Copy this checklist to your operations documentation:

### Quarterly (Every 90 Days)

- [ ] Check Apple JWT expiration date
- [ ] Verify calendar reminders are set
- [ ] Test Apple sign-in on production
- [ ] Review application logs for Apple OAuth errors

### Before Expiration (30 Days)

- [ ] Verify server restart procedure
- [ ] Schedule maintenance window
- [ ] Notify team of upcoming restart
- [ ] Prepare rollback plan

### At Expiration (Before Day 180)

- [ ] Execute server restart procedure
- [ ] Verify new JWT generated
- [ ] Test Apple sign-in functionality
- [ ] Update calendar reminders for next cycle
- [ ] Document restart in operations log

### Emergency (JWT Expired)

- [ ] Restart server immediately
- [ ] Verify Apple sign-in restored
- [ ] Notify affected users (if significant downtime)
- [ ] Post-mortem: Why was expiration missed?
- [ ] Implement better monitoring

---

## Future Enhancements

Consider implementing these improvements for better maintainability:

### 1. Automated JWT Rotation

Add runtime JWT regeneration:
```typescript
// Check JWT expiration daily
setInterval(() => {
  const daysUntilExpiration = calculateDaysUntilExpiration();
  if (daysUntilExpiration < 30) {
    webhookLogger.warn({ daysUntilExpiration }, 'Apple JWT expires soon');
    // Optionally: Trigger automated restart or JWT regeneration
  }
}, 24 * 60 * 60 * 1000); // Daily check
```

### 2. Monitoring Dashboard

Expose JWT status as metric:
```typescript
// Prometheus metric
apple_jwt_expiration_seconds{provider="apple"} = expirationTimestamp
```

### 3. Automated Alerts

Integrate with monitoring services:
- **Datadog:** Create JWT expiration monitor
- **PagerDuty:** Alert on-call when <30 days remaining
- **Slack:** Weekly JWT status report

### 4. Graceful Degradation

Handle expired JWT without service interruption:
```typescript
// Attempt to regenerate JWT if expired
if (isJWTExpired(appleClientSecret)) {
  authLogger.warn('Apple JWT expired - attempting regeneration');
  appleClientSecret = await generateAppleClientSecret();
}
```

---

## Additional Resources

### Official Documentation

- [Apple Sign In Overview](https://developer.apple.com/sign-in-with-apple/)
- [Generate Client Secret JWT](https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens)
- [NextAuth Apple Provider](https://next-auth.js.org/providers/apple)

### Related Files

- `lib/auth.ts` - JWT generation implementation (lines 53-117)
- `.env.example` - Environment variable template
- `CLAUDE.md` - Project documentation

### Support

For questions or issues:
- **Internal:** Check with engineering team
- **Apple:** [Apple Developer Forums](https://developer.apple.com/forums/tags/sign-in-with-apple)
- **NextAuth:** [GitHub Discussions](https://github.com/nextauthjs/next-auth/discussions)

---

## Revision History

| Date | Version | Changes |
|------|---------|---------|
| 2025-01-11 | 1.0 | Initial documentation created |

---

**Last Updated:** 2025-01-11
**Next Review:** 2025-04-11 (Quarterly review)
