{"record":{"id":"f0ac88921aa57afd","repo":"decolua/9router","slug":"xai-discovery-field-must-use-https-value","errorCode":null,"errorMessage":"`xai discovery ${field} must use https: ${value}`","messagePattern":"`xai discovery (.+?) must use https: (.+?)`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/lib/oauth/providerHelpers.js","lineNumber":10,"sourceCode":"const BASE64_BLOCK_SIZE = 4;\n\nfunction validateXaiOAuthEndpoint(rawUrl, field) {\n  const value = String(rawUrl || \"\").trim();\n  if (!value) throw new Error(`xai discovery ${field} is empty`);\n  let parsed;\n  try { parsed = new URL(value); } catch (err) {\n    throw new Error(`xai discovery ${field} is invalid: ${err.message}`);\n  }\n  if (parsed.protocol !== \"https:\") throw new Error(`xai discovery ${field} must use https: ${value}`);\n  const host = parsed.hostname.toLowerCase().trim();\n  if (host !== \"x.ai\" && !host.endsWith(\".x.ai\")) {\n    throw new Error(`xai discovery ${field} host ${host} is not on x.ai`);\n  }\n  return value;\n}\n\nfunction decodeXaiIdTokenEmail(idToken) {\n  if (!idToken || typeof idToken !== \"string\") return undefined;\n  const parts = idToken.split(\".\");\n  if (parts.length !== 3) return undefined;\n  try {\n    const base64 = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n    const padding = (BASE64_BLOCK_SIZE - (base64.length % BASE64_BLOCK_SIZE)) % BASE64_BLOCK_SIZE;\n    const json = Buffer.from(base64 + \"=\".repeat(padding), \"base64\").toString(\"utf8\");\n    const payload = JSON.parse(json);\n    return payload.email || payload.preferred_username || payload.sub || undefined;\n  } catch {","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/providerHelpers.js#L1-L28","documentation":"validateXaiOAuthEndpoint requires discovered xAI endpoints to use https; any http:, ftp:, or custom-scheme URL is rejected with this error naming the offending field and value. This prevents OAuth tokens from being sent over plaintext or to unexpected schemes. discoverXaiEndpoints catches it and falls back to the static https x.ai endpoints.","triggerScenarios":"Discovery document (or direct caller) supplies an endpoint with a non-https protocol, e.g. 'http://x.ai/oauth/token' or a localhost 'http://127.0.0.1:8080/token' endpoint from a test/mock discovery response.","commonSituations":"Local development mock discovery servers that advertise http endpoints, tampered/proxied discovery responses, or copied http URLs pasted into custom config.","solutions":["Use the https form of the endpoint (https://x.ai/...) — plain http is never accepted here.","Rely on discoverXaiEndpoints' static fallback (XAI_CONFIG endpoints are https).","If testing locally, point code at the real discovery URL or pre-validate yourself without the https check.","Check for a proxy stripping TLS / rewriting scheme in the discovery payload."],"exampleFix":"// before\nvalidateXaiOAuthEndpoint('http://x.ai/oauth/token', 'token_endpoint'); // throws\n// after\nvalidateXaiOAuthEndpoint('https://x.ai/oauth/token', 'token_endpoint');","handlingStrategy":"validation","validationCode":"function isHttpsUrl(v) {\n  try { return new URL(String(v).trim()).protocol === 'https:'; } catch { return false; }\n}\n// if (!isHttpsUrl(data.authorization_endpoint)) use static https fallback","typeGuard":"function isHttpsEndpoint(v) {\n  if (typeof v !== 'string') return false;\n  try { return new URL(v.trim()).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  const url = validateXaiOAuthEndpoint(raw, 'token_endpoint');\n  // use url\n} catch (err) {\n  if (/must use https:/.test(err.message)) {\n    // reject or fall back to the static https x.ai endpoint\n    return XAI_CONFIG.tokenUrl;\n  }\n  throw err;\n}","preventionTips":["Always advertise and consume https endpoints in dev mocks and production alike.","Check proxies/TLS-terminating middleboxes that rewrite schemes to http.","Keep local test discovery servers serving https or fall back to static endpoints.","Treat any non-https OAuth endpoint as a security signal, not just a validation nuisance."],"tags":["oauth","xai","https","url-validation"],"backgroundTag":"oauth-endpoint-not-https","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}