From 162c8ed2570a1ebbbf8062f99d2c07d1fc9a2567 Mon Sep 17 00:00:00 2001 From: Claude VM Date: Tue, 19 May 2026 08:23:43 +0300 Subject: [PATCH] fix(auth): Authentik token endpoint is /application/o/token/ (shared) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit THE bug behind every "data nu raman" / invalid_token incident this session: refresh POSTed to `{issuer}/token/` = /application/o/architools/token/ which returns HTTP 405 + empty body. JSON.parse on the empty body threw "Unexpected end of JSON input" → catch fired → token marked RefreshAccessTokenError → 60s cooldown later, retry hit the same broken URL → loop. OIDC discovery at {issuer}/.well-known/openid-configuration confirms: "token_endpoint": "https://auth.beletage.ro/application/o/token/" This is the SHARED endpoint, not per-provider. Hard-fix the URL by constructing it from the issuer's origin. Marius's currently-stuck session will auto-recover on next request (cooldown expires, refresh fires against the corrected URL, refresh_token still valid 30d). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/core/auth/auth-options.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/auth/auth-options.ts b/src/core/auth/auth-options.ts index b5b4898..14ab33d 100644 --- a/src/core/auth/auth-options.ts +++ b/src/core/auth/auth-options.ts @@ -43,7 +43,14 @@ async function refreshAuthentikToken(token: JWT): Promise { if (!issuer || !clientId || !clientSecret) { throw new Error("refresh_prerequisites_missing"); } - const url = `${issuer.replace(/\/$/, "")}/token/`; + // Authentik exposes the token endpoint at the SHARED path, not per + // provider. The per-provider `{issuer}/token/` returns HTTP 405 with + // an empty body — which then explodes our JSON.parse with + // "Unexpected end of JSON input". The OIDC discovery doc at + // {issuer}/.well-known/openid-configuration declares the correct + // endpoint as `https://auth.beletage.ro/application/o/token/`. + const issuerOrigin = new URL(issuer).origin; + const url = `${issuerOrigin}/application/o/token/`; const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" },