A community based topic aggregation platform built on atproto

fix(oauth): add fallback handler for Universal Link failures

When Universal Links don't intercept the redirect to /app/oauth/callback,
return a clear error instead of trying to process it as an OAuth callback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+24 -5
internal
api
routes
atproto
oauth
+6 -5
internal/api/routes/oauth.go
···
// Use login limiter since callback completes the authentication flow
r.With(corsMiddleware(allowedOrigins), loginLimiter.Middleware).Get("/oauth/callback", handler.HandleCallback)
-
// Mobile Universal Link callback route
-
// This route is used for iOS Universal Links and Android App Links
-
// Path must match the path in .well-known/apple-app-site-association
-
// Uses the same handler as web callback - the system routes it to the mobile app
-
r.With(loginLimiter.Middleware).Get("/app/oauth/callback", handler.HandleCallback)
+
// Mobile Universal Link callback route (fallback when app doesn't intercept)
+
// This route exists for iOS Universal Links and Android App Links.
+
// When properly configured, the mobile OS intercepts this URL and opens the app
+
// BEFORE the request reaches the server. If this handler is reached, it means
+
// Universal Links failed to intercept.
+
r.With(loginLimiter.Middleware).Get("/app/oauth/callback", handler.HandleMobileDeepLinkFallback)
// Session management - dedicated rate limits
r.With(logoutLimiter.Middleware).Post("/oauth/logout", handler.HandleLogout)
+18
internal/atproto/oauth/handlers.go
···
return
}
}
+
+
// HandleMobileDeepLinkFallback handles requests to /app/oauth/callback when
+
// Universal Links fail to intercept the redirect.
+
//
+
// If this handler is reached, it means the mobile app did NOT intercept the
+
// Universal Link redirect. The OAuth flow succeeded server-side, but the
+
// credentials couldn't be delivered to the app.
+
func (h *OAuthHandler) HandleMobileDeepLinkFallback(w http.ResponseWriter, r *http.Request) {
+
// Log the failure for debugging
+
slog.Warn("Universal Link not intercepted - mobile app did not handle redirect",
+
"path", r.URL.Path,
+
"has_token", r.URL.Query().Get("token") != "",
+
"has_did", r.URL.Query().Get("did") != "",
+
)
+
+
http.Error(w, "Universal Link not intercepted: The mobile app should have opened this URL. "+
+
"Check that Universal Links (iOS) or App Links (Android) are properly configured.", http.StatusBadRequest)
+
}