Something went wrong. Try again.
FlockOff is a mass-block tool for the Atmosphere flockoff.app
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667import 'server-only'
/** * Auth bridge for local development. * * The AT Protocol OAuth loopback spec (RFC 8252) requires 127.0.0.1 as the * redirect hostname. But our dev server runs on localhost. The browser treats * these as different origins, so a cookie set on 127.0.0.1 is never sent to * localhost. * * Fix: after the callback (on 127.0.0.1), store auth data in server-side memory * with a one-time token, then redirect to localhost/api/oauth/bridge?token=... * The bridge runs on localhost, consumes the token, and sets the session cookie * on the correct domain. * * In production (HTTPS, single domain) this is just an extra redirect that's * transparent to the user. */
interface BridgeEntry { did: string handle?: string avatar?: string // Validated same-origin relative path to land on after the session cookie is // set (scope upgrades return to where the user was). Stored inside the // one-time token entry — never accepted as a query param on the bridge URL. returnTo?: string expiresAt: number}
declare global { // eslint-disable-next-line no-var var __authBridgePending: Map<string, BridgeEntry> | undefined // eslint-disable-next-line no-var var __authBridgeCleanupStarted: boolean | undefined}
const pending: Map<string, BridgeEntry> = (globalThis.__authBridgePending ??= new Map())
const TTL_MS = 60_000 // 60 second window — plenty for a redirect chain
function cleanupExpiredBridgeTokens(): void { const now = Date.now() for (const [token, entry] of pending) { if (entry.expiresAt <= now) pending.delete(token) }}
if (!globalThis.__authBridgeCleanupStarted) { globalThis.__authBridgeCleanupStarted = true setInterval(cleanupExpiredBridgeTokens, TTL_MS).unref?.()}
export function createBridgeToken(did: string, handle?: string, avatar?: string, returnTo?: string): string { cleanupExpiredBridgeTokens() const token = crypto.randomUUID() pending.set(token, { did, handle, avatar, returnTo, expiresAt: Date.now() + TTL_MS }) return token}
export function consumeBridgeToken(token: string): { did: string; handle?: string; avatar?: string; returnTo?: string } | null { const entry = pending.get(token) pending.delete(token) // always delete — single use regardless of expiry if (!entry || Date.now() > entry.expiresAt) return null return { did: entry.did, handle: entry.handle, avatar: entry.avatar, returnTo: entry.returnTo }}