| 90 | private readonly pendingStates = new Map<string, number>(); |
| 91 | |
| 92 | constructor(store: SessionStore) { |
| 93 | this.store = store; |
| 94 | this.clientId = process.env.OAUTH_CLIENT_ID ?? ""; |
| 95 | this.clientSecret = process.env.OAUTH_CLIENT_SECRET ?? ""; |
| 96 | this.issuer = (process.env.OAUTH_ISSUER ?? "").replace(/\/$/, ""); |
| 97 | this.callbackUrl = |
| 98 | process.env.OAUTH_CALLBACK_URL ?? "http://localhost:3000/auth/callback"; |
| 99 | this.scopes = process.env.OAUTH_SCOPES ?? "openid email profile"; |
| 100 | this.adminUsers = new Set( |
| 101 | (process.env.ADMIN_USERS ?? "") |
| 102 | .split(",") |
| 103 | .map((s) => s.trim()) |
| 104 | .filter(Boolean), |
| 105 | ); |
| 106 | |
| 107 | // Periodically prune stale state tokens (>10 min old). |
| 108 | setInterval(() => { |
| 109 | const cutoff = Date.now() - 10 * 60_000; |
| 110 | for (const [s, t] of this.pendingStates) { |
| 111 | if (t < cutoff) this.pendingStates.delete(s); |
| 112 | } |
| 113 | }, 5 * 60_000).unref(); |
| 114 | } |
| 115 | |
| 116 | authenticate(req: IncomingMessage): AuthUser | null { |
| 117 | const session = this.store.getFromRequest(req); |