ory/hydra · error

Only access tokens are allowed in the authorization header.

Error message

Only access tokens are allowed in the authorization header.

What it means

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.

Source

Thrown at oauth2/handler.go:667

//	Extensions:
//	  x-ory-ratelimit-bucket: hydra-public-medium
func (h *Handler) getOidcUserInfo(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	session := NewSessionWithCustomClaims(ctx, h.c, "")
	tokenType, ar, err := h.r.OAuth2Provider().IntrospectToken(ctx, fosite.AccessTokenFromRequest(r), fosite.AccessToken, session)
	if err != nil {
		rfcerr := fosite.ErrorToRFC6749Error(err)
		if rfcerr.StatusCode() == http.StatusUnauthorized {
			w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfcerr.ErrorField, rfcerr.GetDescription()))
		}
		h.r.Writer().WriteError(w, r, err)
		return
	}

	if tokenType != fosite.AccessToken {
		errorDescription := "Only access tokens are allowed in the authorization header."
		w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="invalid_token",error_description="%s"`, errorDescription))
		h.r.Writer().WriteErrorCode(w, r, http.StatusUnauthorized, errors.New(errorDescription))
		return
	}

	c, ok := ar.GetClient().(*client.Client)
	if !ok {
		h.r.Writer().WriteError(w, r, errors.WithStack(fosite.ErrServerError.WithHint("Unable to type assert to *client.Client.")))
		return
	}

	interim := ar.GetSession().(*Session).IDTokenClaims().ToMap()
	delete(interim, "nonce")
	delete(interim, "at_hash")
	delete(interim, "c_hash")
	delete(interim, "exp")
	delete(interim, "sid")
	delete(interim, "jti")

	aud, ok := interim["aud"].([]string)

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Send the `access_token` value from the token endpoint response, not the `id_token`.
  2. Re-run the authorization code / client credentials flow and capture `access_token` from the JSON response.
  3. Check client code that picks the token field from the response body and fix the key it uses.
  4. If the token is expired/revoked, obtain a fresh access token.

Example fix

// before
const token = tokenResponse.id_token
fetch('https://hydra.example.com/userinfo', { headers: { Authorization: `Bearer ${token}` } })
// after
const token = tokenResponse.access_token
fetch('https://hydra.example.com/userinfo', { headers: { Authorization: `Bearer ${token}` } })
Defensive patterns

Strategy: validation

Validate before calling

// client-side: only send the access_token to /userinfo
if tokenResponse.access_token == "" {
    return errors.New("no access_token in token response; cannot call userinfo")
}
req.Header.Set("Authorization", "Bearer "+tokenResponse.access_token)

Type guard

function isAccessTokenResponse(r: { token_type?: string; access_token?: string }): r is { token_type: 'bearer'; access_token: string } {
  return !!r.access_token && (r.token_type?.toLowerCase() === 'bearer');
}

Try / catch

// handle 401 invalid_token by re-authenticating
const res = await fetch(userInfoURL, { headers: { Authorization: `Bearer ${accessToken}` } });
if (res.status === 401) {
  accessToken = await renewAccessToken(); // do NOT fall back to id_token
  return fetchUserInfo(accessToken);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/b22ea2ee4fa24f65. Report an issue: GitHub.