fix: discover OIDC metadata server-side to avoid CORS failures #382

This commit is contained in:
Linus Rath
2026-06-02 00:15:50 +02:00
parent 152ec99262
commit 31100b8f87
3 changed files with 169 additions and 4 deletions
+15 -4
View File
@@ -14,7 +14,7 @@ import { useConfig } from "@/hooks/use-config";
import { apiFetch, getPathPrefix, withBasePath } from "@/lib/browser-navigation";
import { cn } from "@/lib/utils";
import { AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor, Check, Shield, Play, Copy } from "lucide-react";
import { discoverOAuth, type OAuthMetadata } from "@/lib/oauth/discovery";
import { type OAuthMetadata } from "@/lib/oauth/discovery";
import { generateCodeVerifier, generateCodeChallenge, generateState } from "@/lib/oauth/pkce";
import { useUpdateStore, selectBanner } from "@/stores/update-store";
import type { PublicJmapServerEntry } from "@/lib/admin/jmap-servers";
@@ -329,16 +329,27 @@ export default function LoginPage() {
if (!oauthEnabled || !serverUrl) return;
setOauthDiscoveryDone(false);
setOauthMetadata(null);
discoverOAuth(effectiveOauthIssuerUrl || serverUrl)
const controller = new AbortController();
// Discover via our own origin rather than fetching the IdP's /.well-known/*
// documents directly from the browser. A direct cross-origin discovery
// fetch is subject to CORS, and providers like Authentik serve those
// documents without Access-Control-Allow-Origin, so the browser blocks the
// response and login breaks (issue #382). The proxy runs discovery server
// side where CORS does not apply.
const query = selectedServer?.id ? `?server_id=${encodeURIComponent(selectedServer.id)}` : "";
apiFetch(`/api/auth/oauth/metadata${query}`, { signal: controller.signal })
.then(async (res) => (res.ok ? ((await res.json()) as OAuthMetadata) : null))
.then((metadata) => {
setOauthMetadata(metadata);
setOauthDiscoveryDone(true);
})
.catch(() => {
.catch((err) => {
if (err?.name === "AbortError") return;
setOauthMetadata(null);
setOauthDiscoveryDone(true);
});
}, [oauthEnabled, serverUrl, effectiveOauthIssuerUrl]);
return () => controller.abort();
}, [oauthEnabled, serverUrl, effectiveOauthIssuerUrl, selectedServer?.id]);
// Auto-SSO: when enabled with OAUTH_ONLY, skip the login page entirely
const ssoError = searchParams.get("sso_error");