diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index bbdd146c..6be3c3a5 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -63,7 +63,12 @@ instance.interceptors.request.use( let isRefreshing = false; let failedQueue: Array<{ resolve: Function; reject: Function }> = []; -const PUBLIC_AUTH_PATHS = ['/auth/auto-setup', '/auth/login', '/auth/register', '/auth/oidc/']; +// Share-link endpoints (/auth/invitations/lookup, /auth/register-by-invite) +// are reachable by anonymous users opening an invite link. A 401 from these +// must surface to the page (e.g. expired token), not trigger the +// refresh-then-redirect-to-login flow (issue #1617). '/auth/register' already +// covers '/auth/register-by-invite' via substring match. +const PUBLIC_AUTH_PATHS = ['/auth/auto-setup', '/auth/login', '/auth/register', '/auth/oidc/', '/auth/invitations/lookup']; function isPublicAuthRequest(url?: string): boolean { if (!url) return false; diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 490adf19..579246bb 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -20,15 +20,23 @@ import ( // 无需认证的API列表 var noAuthAPI = map[string][]string{ - "/health": {"GET"}, - "/api/v1/auth/register": {"POST"}, - "/api/v1/auth/login": {"POST"}, - "/api/v1/auth/auto-setup": {"POST"}, - "/api/v1/auth/config": {"GET"}, - "/api/v1/auth/oidc/config": {"GET"}, - "/api/v1/auth/oidc/url": {"GET"}, - "/api/v1/auth/oidc/callback": {"GET"}, - "/api/v1/auth/refresh": {"POST"}, + "/health": {"GET"}, + "/api/v1/auth/register": {"POST"}, + "/api/v1/auth/login": {"POST"}, + "/api/v1/auth/auto-setup": {"POST"}, + // Share-link surfaces accept a plaintext invite token from anonymous + // callers (an invitee who hasn't registered yet). They are registered + // as public routes in RegisterAuthRoutes and rate-limited by IP, so the + // global Auth middleware must let them through — otherwise opening a + // share link while logged out 401s and the frontend bounces the user to + // /login instead of the register page (issue #1617). + "/api/v1/auth/invitations/lookup": {"POST"}, + "/api/v1/auth/register-by-invite": {"POST"}, + "/api/v1/auth/config": {"GET"}, + "/api/v1/auth/oidc/config": {"GET"}, + "/api/v1/auth/oidc/url": {"GET"}, + "/api/v1/auth/oidc/callback": {"GET"}, + "/api/v1/auth/refresh": {"POST"}, // IM platforms (Feishu, Slack, etc.) commonly issue a HEAD request // before GET to validate Content-Type / Content-Length when rendering // image previews — both verbs must be allowed for image links to work.