{"record":{"id":"b22ea2ee4fa24f65","repo":"ory/hydra","slug":"only-access-tokens-are-allowed-in-the-authorizatio","errorCode":null,"errorMessage":"Only access tokens are allowed in the authorization header.","messagePattern":"Only access tokens are allowed in the authorization header\\.","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"oauth2/handler.go","lineNumber":667,"sourceCode":"//\tExtensions:\n//\t  x-ory-ratelimit-bucket: hydra-public-medium\nfunc (h *Handler) getOidcUserInfo(w http.ResponseWriter, r *http.Request) {\n\tctx := r.Context()\n\tsession := NewSessionWithCustomClaims(ctx, h.c, \"\")\n\ttokenType, ar, err := h.r.OAuth2Provider().IntrospectToken(ctx, fosite.AccessTokenFromRequest(r), fosite.AccessToken, session)\n\tif err != nil {\n\t\trfcerr := fosite.ErrorToRFC6749Error(err)\n\t\tif rfcerr.StatusCode() == http.StatusUnauthorized {\n\t\t\tw.Header().Set(\"WWW-Authenticate\", fmt.Sprintf(`Bearer error=\"%s\",error_description=\"%s\"`, rfcerr.ErrorField, rfcerr.GetDescription()))\n\t\t}\n\t\th.r.Writer().WriteError(w, r, err)\n\t\treturn\n\t}\n\n\tif tokenType != fosite.AccessToken {\n\t\terrorDescription := \"Only access tokens are allowed in the authorization header.\"\n\t\tw.Header().Set(\"WWW-Authenticate\", fmt.Sprintf(`Bearer error=\"invalid_token\",error_description=\"%s\"`, errorDescription))\n\t\th.r.Writer().WriteErrorCode(w, r, http.StatusUnauthorized, errors.New(errorDescription))\n\t\treturn\n\t}\n\n\tc, ok := ar.GetClient().(*client.Client)\n\tif !ok {\n\t\th.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHint(\"Unable to type assert to *client.Client.\")))\n\t\treturn\n\t}\n\n\tinterim := ar.GetSession().(*Session).IDTokenClaims().ToMap()\n\tdelete(interim, \"nonce\")\n\tdelete(interim, \"at_hash\")\n\tdelete(interim, \"c_hash\")\n\tdelete(interim, \"exp\")\n\tdelete(interim, \"sid\")\n\tdelete(interim, \"jti\")\n\n\taud, ok := interim[\"aud\"].([]string)","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oauth2/handler.go#L649-L685","documentation":"Hydra's OIDC userinfo handler rejects requests whose bearer token is not a fosite access token. If the token type presented in the Authorization header is e.g. an ID token, refresh token, or some other token type, the handler responds 401 with `WWW-Authenticate: Bearer error=\"invalid_token\"` and this description. Only access tokens carry the userinfo authorization grant.","triggerScenarios":"GET/POST to /userinfo with `Authorization: Bearer <token>` where the token fails the `tokenType != fosite.AccessToken` check — classically an ID token pasted instead of an access token, or an opaque/refresh token from the token response's other fields.","commonSituations":"Client apps confusing `id_token` with `access_token` in the OIDC token response; frontends storing only the ID token; tests hitting /userinfo with a JWT from a different grant; using a token obtained from a non-token-endpoint flow.","solutions":["Send the `access_token` value from the token endpoint response, not the `id_token`.","Re-run the authorization code / client credentials flow and capture `access_token` from the JSON response.","Check client code that picks the token field from the response body and fix the key it uses.","If the token is expired/revoked, obtain a fresh access token."],"exampleFix":"// before\nconst token = tokenResponse.id_token\nfetch('https://hydra.example.com/userinfo', { headers: { Authorization: `Bearer ${token}` } })\n// after\nconst token = tokenResponse.access_token\nfetch('https://hydra.example.com/userinfo', { headers: { Authorization: `Bearer ${token}` } })","handlingStrategy":"validation","validationCode":"// client-side: only send the access_token to /userinfo\nif tokenResponse.access_token == \"\" {\n    return errors.New(\"no access_token in token response; cannot call userinfo\")\n}\nreq.Header.Set(\"Authorization\", \"Bearer \"+tokenResponse.access_token)","typeGuard":"function isAccessTokenResponse(r: { token_type?: string; access_token?: string }): r is { token_type: 'bearer'; access_token: string } {\n  return !!r.access_token && (r.token_type?.toLowerCase() === 'bearer');\n}","tryCatchPattern":"// handle 401 invalid_token by re-authenticating\nconst res = await fetch(userInfoURL, { headers: { Authorization: `Bearer ${accessToken}` } });\nif (res.status === 401) {\n  accessToken = await renewAccessToken(); // do NOT fall back to id_token\n  return fetchUserInfo(accessToken);\n}","preventionTips":["Store access_token and id_token as distinct fields and never interchange them","Read token_type from the token response and assert it is 'bearer'","Refresh access tokens before expiry instead of reusing ID tokens","Add integration tests hitting /userinfo with the real flow"],"tags":["oauth2","oidc","userinfo","access-token"],"backgroundTag":"wrong-token-type-userinfo","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}