{"record":{"id":"e7d6148918d8372a","repo":"JuliusBrussee/caveman","slug":"azure-bearer-credentials-are-unsupported-use-an-a","errorCode":null,"errorMessage":"azure bearer credentials are unsupported; use an API key","messagePattern":"azure bearer credentials are unsupported; use an API key","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/adapter.go","lineNumber":504,"sourceCode":"\t\t\tout.Set(\"authorization\", \"Bearer \"+credential.Key)\n\t\t}\n\t\tcopyIfPresent(out, req.Header, \"x-vertex-ai-llm-request-type\")\n\t\tcopyIfPresent(out, req.Header, \"x-goog-user-project\")\n\tcase \"azure_openai\":\n\t\tif credential.Key != \"\" {\n\t\t\t// The standalone placeholder is a synthetic no-key marker used by\n\t\t\t// callers that explicitly allow env-backed API-key fallback. It is\n\t\t\t// not an Entra token and must not be forwarded as Authorization;\n\t\t\t// gateway fallback may replace it with the Azure api-key header.\n\t\t\tif credential.Scheme == \"bearer\" && credential.Key == \"no-key-required\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t// Real bearer/JWT credentials remain fail-closed until an auth-kind is\n\t\t\t// persisted and mapped end-to-end. They must never be silently\n\t\t\t// relabeled as an api-key.\n\t\t\tfields := strings.Fields(strings.TrimSpace(credential.Key))\n\t\t\tif credential.Scheme == \"bearer\" || len(fields) > 1 && strings.EqualFold(fields[0], \"bearer\") || strings.HasPrefix(strings.TrimSpace(credential.Key), \"eyJ\") {\n\t\t\t\treturn nil, fmt.Errorf(\"azure bearer credentials are unsupported; use an API key\")\n\t\t\t}\n\t\t\tout.Set(\"api-key\", credential.Key)\n\t\t}\n\tdefault:\n\t\tif credential.Key != \"\" {\n\t\t\tout.Set(\"authorization\", \"Bearer \"+credential.Key)\n\t\t}\n\t}\n\treturn out, nil\n}\n\nfunc validGoogleQuotaProject(value string) bool {\n\tif value == \"\" || len(value) > 128 {\n\t\treturn false\n\t}\n\tfor _, r := range value {\n\t\tif r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '.' || r == '_' || r == ':' {\n\t\t\tcontinue","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/adapter.go#L486-L522","documentation":"Azure OpenAI auth in this adapter supports only API keys (mapped to the api-key header). A credential whose scheme is bearer, whose value starts with 'Bearer ' or looks like a JWT ('eyJ...') is rejected outright: relabeling an Entra token as an api-key would silently fail auth upstream, so the adapter fails closed until bearer auth is properly supported. The synthetic 'no-key-required' marker is explicitly exempted.","triggerScenarios":"Configuring the Azure provider with credential_scheme: bearer; pasting an Entra ID / Azure AD token (they start with eyJ) into the api-key field; injecting OAuth credentials from a vault that prefixes values with 'Bearer '.","commonSituations":"Porting config from a tool that uses Azure AD auth (azure-cli login, managed identity) and assuming the same token works here; secrets managers that store tokens with their scheme prefix; copy-pasting a JWT from an Entra token dump.","solutions":["Use an Azure OpenAI API key for this provider: generate one in the Azure OpenAI resource (Keys and Endpoint) and configure it as the api-key credential.","Strip any 'Bearer ' prefix or scheme metadata so the stored secret is the bare key value.","Do not feed az account get-access-token output here — Entra bearer auth is deliberately unsupported."],"exampleFix":"# before\nproviders:\n  azure:\n    credential_scheme: bearer\n    key: eyJhbGciOiJSUzI1NiIs...   # Entra JWT -> error\n\n# after\nproviders:\n  azure:\n    key: 32charAzureOpenAIResourceKey  # from Keys and Endpoint blade","handlingStrategy":"validation","validationCode":"func isAzureAPIKeyCredential(scheme, key string) bool {\n    if scheme == \"bearer\" {\n        return false\n    }\n    fields := strings.Fields(strings.TrimSpace(key))\n    if len(fields) > 1 && strings.EqualFold(fields[0], \"bearer\") {\n        return false\n    }\n    return !strings.HasPrefix(strings.TrimSpace(key), \"eyJ\")\n}\n\nif !isAzureAPIKeyCredential(cred.Scheme, cred.Key) {\n    return errors.New(\"Azure provider requires an API key, not an Entra/JWT token\")\n}","typeGuard":"func isAzureAPIKeyCredential(scheme, key string) bool {\n    if scheme == \"bearer\" {\n        return false\n    }\n    fields := strings.Fields(strings.TrimSpace(key))\n    if len(fields) > 1 && strings.EqualFold(fields[0], \"bearer\") {\n        return false\n    }\n    return !strings.HasPrefix(strings.TrimSpace(key), \"eyJ\")\n}","tryCatchPattern":"if _, err := adapter.BuildUpstreamHeaders(credential, req); err != nil {\n    if strings.Contains(err.Error(), \"azure bearer credentials are unsupported\") {\n        http.Error(w, \"configure an Azure OpenAI API key for this provider\", http.StatusBadRequest)\n        return\n    }\n    http.Error(w, err.Error(), http.StatusBadRequest)\n}","preventionTips":["Provision Azure OpenAI resource keys and store the bare key value.","Never feed az account get-access-token output to this provider.","Secrets managers should store the raw key without scheme prefixes."],"tags":["go","auth","azure","fail-closed","configuration"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}