{"record":{"id":"2b07236fec8eeabb","repo":"decolua/9router","slug":"token-endpoint-must-be-a-microsoft-login-endpoint","errorCode":null,"errorMessage":"token_endpoint must be a Microsoft login endpoint","messagePattern":"token_endpoint must be a Microsoft login endpoint","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/kiroExternalIdp.js","lineNumber":31,"sourceCode":"\nexport function validateMicrosoftTokenEndpoint(rawEndpoint) {\n  const tokenEndpoint = normalizeString(rawEndpoint);\n  if (!tokenEndpoint) throw new Error(\"token_endpoint is required\");\n\n  let parsed;\n  try {\n    parsed = new URL(tokenEndpoint);\n  } catch {\n    throw new Error(\"token_endpoint must be a valid URL\");\n  }\n\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(\"token_endpoint must use https\");\n  }\n\n  const host = parsed.hostname.toLowerCase();\n  if (!MICROSOFT_TOKEN_ENDPOINT_HOSTS.has(host)) {\n    throw new Error(\"token_endpoint must be a Microsoft login endpoint\");\n  }\n\n  return parsed.toString();\n}\n\nexport function normalizeScope(scopes) {\n  if (Array.isArray(scopes)) {\n    return scopes.map(normalizeString).filter(Boolean).join(\" \");\n  }\n  return normalizeString(scopes);\n}\n\nexport function decodeJwtPayload(jwt) {\n  try {\n    if (!jwt || typeof jwt !== \"string\") return null;\n    const parts = jwt.split(\".\");\n    if (parts.length !== 3) return null;\n    const base64 = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/kiroExternalIdp.js#L13-L49","documentation":"Even a valid https URL is rejected unless its hostname is in MICROSOFT_TOKEN_ENDPOINT_HOSTS (login.microsoftonline.com and related Microsoft login hosts). This prevents token leakage to attacker-controlled https endpoints via a tampered token_endpoint.","triggerScenarios":"token_endpoint pointing at https://evil.example.com/token, a personal proxy/relay host, or a regional/alternate host not in the allowlist (e.g. login.partner.microsoftonline.cn if it isn't allowlisted), or a typo'd host like login.micrsoftonline.com.","commonSituations":"User routed Microsoft auth through a self-hosted proxy for network reasons; using a sovereign-cloud tenant endpoint that the allowlist doesn't cover; typo in the hostname; enterprise gateway re-hosting the login endpoint.","solutions":["Use the standard endpoint host: https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token.","If you must use a sovereign/alternative Microsoft host, check MICROSOFT_TOKEN_ENDPOINT_HOSTS in src/lib/oauth/constants or kiroExternalIdp.js and add it deliberately.","Remove any custom proxy host from the token_endpoint; if TLS interception is needed, configure it at the network layer on the standard host instead.","Fix hostname typos by pasting token_endpoint directly from the tenant's openid-configuration document."],"exampleFix":"// before\nvalidateMicrosoftTokenEndpoint(\"https://my-relay.example.com/microsoft/token\");\n// after\nvalidateMicrosoftTokenEndpoint(\"https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token\");","handlingStrategy":"validation","validationCode":"const MICROSOFT_TOKEN_ENDPOINT_HOSTS = new Set([\"login.microsoftonline.com\", \"login.microsoft.com\", \"login.windows.net\"]);\nconst host = new URL(raw).hostname.toLowerCase();\nif (!MICROSOFT_TOKEN_ENDPOINT_HOSTS.has(host)) {\n  throw new Error(`token_endpoint host \"${host}\" is not a Microsoft login endpoint`);\n}\nvalidateMicrosoftTokenEndpoint(raw);","typeGuard":null,"tryCatchPattern":"try {\n  endpoint = validateMicrosoftTokenEndpoint(raw);\n} catch (err) {\n  if (err.message === \"token_endpoint must be a Microsoft login endpoint\") {\n    throw new Error(`token_endpoint must point at login.microsoftonline.com (got \"${new URL(raw).hostname}\")`);\n  }\n  throw err;\n}","preventionTips":["Use the canonical host login.microsoftonline.com; don't route OAuth through custom proxies or relays.","If you need a sovereign-cloud tenant, verify its host is in MICROSOFT_TOKEN_ENDPOINT_HOSTS and extend the allowlist deliberately.","Copy token_endpoint from the tenant's openid-configuration document to avoid hostname typos.","Treat any non-Microsoft token endpoint as a credential-theft risk — the allowlist exists to block it."],"tags":["oauth","allowlist","security","microsoft"],"backgroundTag":"untrusted-token-endpoint","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}