siyuan-note/siyuan · error
OIDC response does not contain an ID token
Error message
OIDC response does not contain an ID token
What it means
After a successful token exchange, Exchange expects the token response to include an id_token extra (per OIDC); if it is absent or empty the error is thrown. GitHub's OAuth2 flow is not OIDC and returns no ID token, which is why GitHub is special-cased before this check — reaching this error means a non-GitHub provider's token endpoint did not return an ID token.
Solutions
- Ensure the openid scope is included in config.Scopes; the Provider prepends it if missing, so check that scopes were not overridden downstream or that the IdP honors them.
- Confirm the IdP actually implements OIDC (discovery document lists id_token signing algos / supports id_token); if it is pure OAuth2, it cannot be used with this flow.
- Verify the token endpoint response (via the IdP logs) actually contains id_token; check for intermediaries modifying the response.
- If you intended GitHub, set config.Provider to conf.OIDCProviderGitHub so the GitHub code path is used instead.
Example fix
// before
cfg := &conf.OIDC{Provider: conf.OIDCProviderCustom, IssuerURL: issuer, Scopes: []string{"email", "profile"}} // plain OAuth2 IdP, no id_token
provider, err := New(cfg, redirectURL)
// after
// use an IdP that supports OIDC, or keep scopes such that openid is honored
cfg := &conf.OIDC{Provider: conf.OIDCProviderCustom, IssuerURL: issuer, Scopes: []string{"openid", "email", "profile"}}
provider, err := New(cfg, redirectURL) Defensive patterns
Strategy: validation
Validate before calling
hasOpenID := false
for _, s := range cfg.Scopes {
if s == oidc.ScopeOpenID {
hasOpenID = true
}
}
if !hasOpenID {
return errors.New("openid scope is required for OIDC sign-in")
} Try / catch
claims, err := provider.Exchange(ctx, code, verifier, nonce)
if err != nil {
if err.Error() == "OIDC response does not contain an ID token" {
// the IdP is likely pure OAuth2; verify OIDC support before reconfiguring
}
return err
} Prevention
- Confirm the IdP implements OIDC (discovery lists id_token support) before wiring it up
- Never override scopes in a way that drops openid
- Test sign-in with a token introspection tool to confirm id_token is returned
When it happens
Trigger: Calling Exchange with a custom/standard provider whose token response lacks the id_token field: the IdP is pure OAuth2 (not OIDC), the scopes did not include openid (scopes misconfigured/overridden), or a proxy stripped the response field.
Common situations: Configuring a plain OAuth2 server (e.g. an old GitLab or a custom OAuth service) as an OIDC provider; config.Scopes replaced the default openid scope with only email/profile; response_type or flow variant returning only an access token.
Related errors
- exchange OIDC authorization code failed
- GitHub OAuth client secret is required
- OIDC authorization code is missing
- OIDC redirect URL is required
- A loopback OIDC redirect URL is required for local access
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/f88fad44e703924e.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/oidc_provider/provider.go:101
func (p *Provider) AuthURL(state, nonce, codeVerifier string) string {
if p.kind == conf.OIDCProviderGitHub {
return p.oauth2Config.AuthCodeURL(state, oauth2.S256ChallengeOption(codeVerifier))
}
return p.oauth2Config.AuthCodeURL(state, oidc.Nonce(nonce), oauth2.S256ChallengeOption(codeVerifier))
}
func (p *Provider) Exchange(ctx context.Context, code, codeVerifier, nonce string) (map[string]any, error) {
token, err := p.oauth2Config.Exchange(ctx, code, oauth2.VerifierOption(codeVerifier))
if err != nil {
return nil, fmt.Errorf("exchange OIDC authorization code failed: %w", err)
}
if p.kind == conf.OIDCProviderGitHub {
return exchangeGitHubClaims(ctx, token)
}
rawIDToken, ok := token.Extra("id_token").(string)
if !ok || rawIDToken == "" {
return nil, errors.New("OIDC response does not contain an ID token")
}
idToken, err := p.verifier.Verify(ctx, rawIDToken)
if err != nil {
return nil, fmt.Errorf("verify OIDC ID token failed: %w", err)
}
if idToken.Nonce != nonce {
return nil, errors.New("OIDC nonce does not match")
}
claims := map[string]any{}
if err = idToken.Claims(&claims); err != nil {
return nil, fmt.Errorf("decode OIDC claims failed: %w", err)
}
return claims, nil
}
func newGitHub(config *conf.OIDC, redirectURL string) *Provider {
scopes := append([]string{}, config.Scopes...)
if len(scopes) == 0 || isDefaultOIDCScopes(scopes) {View on GitHub (pinned to 9f775e8a12)