Framework-agnostic OAuth integration for AT Protocol (Bluesky) applications.
at main 321 lines 7.9 kB view raw
1/** 2 * Types for AT Protocol OAuth integration 3 * Framework-agnostic - works with standard Request/Response APIs 4 */ 5 6import type { OAuthStorage } from "@tijs/atproto-storage"; 7 8/** 9 * Logger interface for custom logging implementations. 10 * Compatible with oauth-client-deno's Logger interface. 11 */ 12export interface Logger { 13 debug(...args: unknown[]): void; 14 info(...args: unknown[]): void; 15 warn(...args: unknown[]): void; 16 error(...args: unknown[]): void; 17} 18 19/** 20 * No-op logger for production use 21 */ 22export const noopLogger: Logger = { 23 debug: () => {}, 24 info: () => {}, 25 warn: () => {}, 26 error: () => {}, 27}; 28 29/** 30 * Generic OAuth session interface 31 * 32 * Compatible with @tijs/oauth-client-deno Session class and similar implementations. 33 * For AT Protocol applications, makeRequest() provides automatic DPoP authentication. 34 */ 35export interface SessionInterface { 36 /** User's DID */ 37 did: string; 38 39 /** Access token for API calls */ 40 accessToken: string; 41 42 /** Refresh token (optional) */ 43 refreshToken?: string; 44 45 /** Handle/username (optional) */ 46 handle?: string; 47 48 /** User's PDS URL */ 49 pdsUrl: string; 50 51 /** Time until token expires in milliseconds (optional) */ 52 timeUntilExpiry?: number; 53 54 /** 55 * Make authenticated request with automatic DPoP handling. 56 */ 57 makeRequest( 58 method: string, 59 url: string, 60 options?: RequestInit, 61 ): Promise<Response>; 62 63 /** 64 * Refresh tokens (optional) 65 */ 66 refresh?(): Promise<SessionInterface>; 67 68 /** 69 * Serialize session data for storage 70 */ 71 toJSON(): unknown; 72} 73 74/** 75 * Generic OAuth client interface - bring your own client! 76 * Compatible with @tijs/oauth-client-deno v1.0.0+ 77 */ 78export interface OAuthClientInterface { 79 /** 80 * Start OAuth authorization flow 81 * @returns URL object for authorization redirect 82 */ 83 authorize( 84 handle: string, 85 options?: { state?: string; scope?: string; prompt?: string }, 86 ): Promise<URL>; 87 88 /** 89 * Handle OAuth callback and exchange code for tokens 90 * @param params URLSearchParams from OAuth callback 91 */ 92 callback(params: URLSearchParams): Promise<{ 93 session: SessionInterface; 94 state?: string | null; 95 }>; 96 97 /** 98 * Restore a session from storage by session ID. 99 * The OAuth client should handle automatic token refresh during restore if needed. 100 * @param sessionId - Session identifier to restore 101 * @returns Promise resolving to restored session, or null if not found 102 */ 103 restore(sessionId: string): Promise<SessionInterface | null>; 104} 105 106/** 107 * Configuration options for ATProto OAuth integration. 108 */ 109export interface ATProtoOAuthConfig { 110 /** Base URL of your application (e.g. "https://myapp.example.com") */ 111 baseUrl: string; 112 113 /** Display name for OAuth consent screen */ 114 appName: string; 115 116 /** URL to app logo for OAuth consent screen */ 117 logoUri?: string; 118 119 /** URL to privacy policy */ 120 policyUri?: string; 121 122 /** Cookie signing secret (required, at least 32 characters) */ 123 cookieSecret: string; 124 125 /** OAuth scope (default: "atproto transition:generic") */ 126 scope?: string; 127 128 /** 129 * Session TTL in seconds (default: 7 days). 130 * For AT Protocol OAuth public clients, max is 14 days per spec. 131 */ 132 sessionTtl?: number; 133 134 /** Storage implementation for OAuth sessions */ 135 storage: OAuthStorage; 136 137 /** 138 * Optional logger for debugging and monitoring OAuth flows. 139 * Defaults to a no-op logger (no console output). 140 * Pass console for standard logging. 141 */ 142 logger?: Logger; 143 144 /** 145 * URL scheme for mobile app OAuth callback. 146 * When mobile=true is passed to /login, the callback will redirect to this 147 * scheme with session_token and did as query params. 148 * Example: "myapp://auth-callback" or "anchor-app://auth-callback" 149 */ 150 mobileScheme?: string; 151} 152 153/** 154 * ATProto OAuth client metadata for /.well-known/oauth-client 155 */ 156export interface ClientMetadata { 157 client_name: string; 158 client_id: string; 159 client_uri: string; 160 redirect_uris: string[]; 161 scope: string; 162 grant_types: string[]; 163 response_types: string[]; 164 application_type: string; 165 token_endpoint_auth_method: string; 166 dpop_bound_access_tokens: boolean; 167 logo_uri?: string; 168 policy_uri?: string; 169} 170 171/** 172 * Session validation result. 173 */ 174export interface SessionValidationResult { 175 valid: boolean; 176 did?: string; 177 handle?: string; 178} 179 180/** 181 * Stored OAuth session data 182 */ 183export interface StoredOAuthSession { 184 did: string; 185 accessToken: string; 186 refreshToken?: string; 187 handle?: string; 188 pdsUrl: string; 189 expiresAt?: number; 190 createdAt: number; 191 updatedAt: number; 192} 193 194/** 195 * Iron Session data stored in encrypted cookie 196 */ 197export interface SessionData { 198 did: string; 199 createdAt: number; 200 lastAccessed: number; 201} 202 203/** 204 * OAuth sessions manager interface 205 */ 206export interface OAuthSessionsInterface { 207 /** 208 * Get an OAuth session for a specific DID 209 * @param did - User's DID 210 * @returns OAuth session or null if not found 211 */ 212 getOAuthSession(did: string): Promise<SessionInterface | null>; 213 214 /** 215 * Save OAuth session to storage 216 * @param session - Session to save 217 */ 218 saveOAuthSession(session: SessionInterface): Promise<void>; 219 220 /** 221 * Delete OAuth session from storage 222 * @param did - User's DID 223 */ 224 deleteOAuthSession(did: string): Promise<void>; 225} 226 227/** 228 * Result from getOAuthSessionFromRequest() 229 */ 230export interface OAuthSessionFromRequestResult { 231 /** The OAuth session, or null if not found/invalid */ 232 session: SessionInterface | null; 233 234 /** Set-Cookie header to refresh the session (set when session is valid) */ 235 setCookieHeader?: string; 236 237 /** Error information if session retrieval failed */ 238 error?: { 239 type: 240 | "NO_COOKIE" 241 | "INVALID_COOKIE" 242 | "SESSION_EXPIRED" 243 | "OAUTH_ERROR" 244 | "UNKNOWN"; 245 message: string; 246 details?: unknown; 247 }; 248} 249 250/** 251 * ATProto OAuth instance returned by createATProtoOAuth(). 252 */ 253export interface ATProtoOAuthInstance { 254 /** 255 * Handle /login route - start OAuth flow 256 * @param request - HTTP request with ?handle= query param 257 * @returns Response (redirect to OAuth provider) 258 */ 259 handleLogin(request: Request): Promise<Response>; 260 261 /** 262 * Handle /oauth/callback route - complete OAuth flow 263 * @param request - HTTP request from OAuth callback 264 * @returns Response (redirect to app) 265 */ 266 handleCallback(request: Request): Promise<Response>; 267 268 /** 269 * Handle /oauth-client-metadata.json route 270 * @returns Response with client metadata JSON 271 */ 272 handleClientMetadata(): Response; 273 274 /** 275 * Handle /api/auth/logout route 276 * @param request - HTTP request 277 * @returns Response 278 */ 279 handleLogout(request: Request): Promise<Response>; 280 281 /** 282 * Get OAuth session from request (cookie or Bearer token) 283 * @param request - HTTP request 284 * @returns Session result with optional Set-Cookie header 285 */ 286 getSessionFromRequest( 287 request: Request, 288 ): Promise<OAuthSessionFromRequestResult>; 289 290 /** 291 * Generate client metadata 292 */ 293 getClientMetadata(): ClientMetadata; 294 295 /** 296 * Get a Set-Cookie header to clear the session cookie. 297 * Useful for custom logout flows or error handling scenarios. 298 * @returns Set-Cookie header string 299 */ 300 getClearCookieHeader(): string; 301 302 /** Direct access to sessions interface for advanced usage */ 303 sessions: OAuthSessionsInterface; 304} 305 306/** 307 * OAuth state stored during authorization flow 308 */ 309export interface OAuthState { 310 handle: string; 311 timestamp: number; 312 /** Redirect path after successful web OAuth */ 313 redirectPath?: string; 314 /** Flag for mobile OAuth flow - redirects to mobileScheme instead of web */ 315 mobile?: boolean; 316 /** Flag for PWA OAuth flow - returns HTML page with postMessage instead of redirect */ 317 pwa?: boolean; 318} 319 320// Re-export OAuthStorage from atproto-storage for convenience 321export type { OAuthStorage };