A community based topic aggregation platform built on atproto
1package routes
2
3import (
4 "Coves/internal/api/middleware"
5 "Coves/internal/atproto/oauth"
6 "net/http"
7 "time"
8
9 "github.com/go-chi/chi/v5"
10 "github.com/go-chi/cors"
11)
12
13// RegisterOAuthRoutes registers OAuth-related endpoints on the router with dedicated rate limiting
14// OAuth endpoints have stricter rate limits to prevent:
15// - Credential stuffing attacks on login endpoints
16// - OAuth state exhaustion
17// - Refresh token abuse
18func RegisterOAuthRoutes(r chi.Router, handler *oauth.OAuthHandler, allowedOrigins []string) {
19 // Create stricter rate limiters for OAuth endpoints
20 // Login endpoints: 10 req/min per IP (credential stuffing protection)
21 loginLimiter := middleware.NewRateLimiter(10, 1*time.Minute)
22
23 // Refresh endpoint: 20 req/min per IP (slightly higher for legitimate token refresh)
24 refreshLimiter := middleware.NewRateLimiter(20, 1*time.Minute)
25
26 // Logout endpoint: 10 req/min per IP
27 logoutLimiter := middleware.NewRateLimiter(10, 1*time.Minute)
28
29 // OAuth metadata endpoints - public, no extra rate limiting (use global limit)
30 // Serve at root /oauth-client-metadata.json so OAuth screens show clean brand domain
31 r.Get("/oauth-client-metadata.json", handler.HandleClientMetadata)
32 r.Get("/oauth-client-keys.json", handler.HandleClientJWKS)
33 r.Get("/.well-known/oauth-protected-resource", handler.HandleProtectedResourceMetadata)
34
35 // OAuth flow endpoints - stricter rate limiting for authentication attempts
36 r.With(loginLimiter.Middleware).Get("/oauth/login", handler.HandleLogin)
37 r.With(loginLimiter.Middleware).Get("/oauth/mobile/login", handler.HandleMobileLogin)
38
39 // OAuth callback - needs CORS for potential cross-origin redirects from PDS
40 // Use login limiter since callback completes the authentication flow
41 r.With(corsMiddleware(allowedOrigins), loginLimiter.Middleware).Get("/oauth/callback", handler.HandleCallback)
42
43 // Mobile Universal Link callback route (fallback when app doesn't intercept)
44 // This route exists for iOS Universal Links and Android App Links.
45 // When properly configured, the mobile OS intercepts this URL and opens the app
46 // BEFORE the request reaches the server. If this handler is reached, it means
47 // Universal Links failed to intercept.
48 r.With(loginLimiter.Middleware).Get("/app/oauth/callback", handler.HandleMobileDeepLinkFallback)
49
50 // Session management - dedicated rate limits
51 r.With(logoutLimiter.Middleware).Post("/oauth/logout", handler.HandleLogout)
52 r.With(refreshLimiter.Middleware).Post("/oauth/refresh", handler.HandleRefresh)
53}
54
55// corsMiddleware creates a CORS middleware for OAuth callback with specific allowed origins
56func corsMiddleware(allowedOrigins []string) func(next http.Handler) http.Handler {
57 return cors.Handler(cors.Options{
58 AllowedOrigins: allowedOrigins, // Only allow specific origins for OAuth callback
59 AllowedMethods: []string{"GET", "POST", "OPTIONS"},
60 AllowedHeaders: []string{
61 "Accept",
62 "Authorization",
63 "Content-Type",
64 "X-CSRF-Token",
65 },
66 ExposedHeaders: []string{"Link"},
67 AllowCredentials: true,
68 MaxAge: 300, // 5 minutes
69 })
70}