Skip to content

fix: conditionally omit userinfo_endpoint and access_token from verifier-OP#382

Merged
masv3971 merged 5 commits into
SUNET:mainfrom
sirosfoundation:fix/verifier-op-userinfo-accesstoken
May 18, 2026
Merged

fix: conditionally omit userinfo_endpoint and access_token from verifier-OP#382
masv3971 merged 5 commits into
SUNET:mainfrom
sirosfoundation:fix/verifier-op-userinfo-accesstoken

Conversation

@leifj
Copy link
Copy Markdown
Contributor

@leifj leifj commented May 6, 2026

Closes #381

Problem

The verifier-OP unconditionally advertises userinfo_endpoint in its discovery metadata and returns access_token/refresh_token in the token response. Since the verifier has no persistent user sessions, the access token is unusable at the UserInfo endpoint. This confuses standard OIDC RP libraries (coreos/go-oidc, golang.org/x/oauth2) that discover the endpoint and expect the access token to work there per OIDC Core §5.3.

Fix

Add enable_userinfo config field to OIDCOP (default: false).

When false (verifier mode):

  • Discovery: userinfo_endpoint is omitted from /.well-known/openid-configuration
  • Token response: access_token, refresh_token, and expires_in are omitted — only id_token, token_type, and scope are returned

When true (traditional OP mode):

  • Previous behavior is fully preserved

Changes

File Change
pkg/model/config.go Add EnableUserInfo bool to OIDCOP struct
internal/verifier/apiv1/handler_oidc.go Conditionally build userinfo URL in discovery; conditionally generate access/refresh tokens; omitempty on TokenResponse.AccessToken and DiscoveryMetadata.UserInfoEndpoint
internal/verifier/apiv1/handler_oidc_test.go Update assertions for default (no UserInfo) behavior; test custom URL with EnableUserInfo: true

Configuration

verifier:
  outbound:
    oidc_provider:
      enable_userinfo: false  # default — omit userinfo_endpoint and access_token
      # enable_userinfo: true  # restore previous behavior

…ier-OP

The verifier-OP is not a traditional IdP and does not maintain persistent
user sessions. Previously it unconditionally advertised userinfo_endpoint
in its discovery metadata and returned access/refresh tokens in the token
response. This confused standard OIDC RP libraries (e.g. coreos/go-oidc)
that expect these to work together per OIDC Core.

Changes:
- Add EnableUserInfo config field to OIDCOP (default: false)
- When false: omit userinfo_endpoint from discovery metadata
- When false: omit access_token and refresh_token from token response
- TokenResponse struct uses omitempty for access_token and expires_in
- DiscoveryMetadata struct uses omitempty for userinfo_endpoint
- Update tests to reflect default (no UserInfo) behavior

Setting enable_userinfo: true restores the previous behavior for
deployments where the OP supports real UserInfo sessions.

Closes SUNET#381
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a configuration switch to make the verifier’s OIDC OP behave more like a “verifier-mode OP” by avoiding advertising/issuing artifacts (UserInfo endpoint + access/refresh tokens) that aren’t usable without persistent user sessions, reducing incompatibilities with standard OIDC RP libraries.

Changes:

  • Add enable_userinfo (EnableUserInfo) to OIDCOP config (default false).
  • Conditionally omit userinfo_endpoint from discovery and omit access_token/refresh_token/expires_in from token responses when disabled.
  • Update verifier OIDC tests to assert the new default behavior and cover discovery behavior when enabled.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
pkg/model/config.go Adds EnableUserInfo flag to OIDC OP configuration.
internal/verifier/apiv1/handler_oidc.go Gates UserInfo discovery + access/refresh token issuance behind EnableUserInfo; adds omitempty where needed.
internal/verifier/apiv1/handler_oidc_test.go Updates tests for default no-UserInfo behavior and discovery behavior when enabled.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/verifier/apiv1/handler_oidc.go
Comment thread internal/verifier/apiv1/handler_oidc_test.go
Leif Johansson added 2 commits May 6, 2026 11:34
…Info=true test

- Conditionally include 'implicit' and 'refresh_token' in
  grant_types_supported only when EnableUserInfo is true, keeping
  discovery metadata consistent with actual token endpoint behavior.
- Add TestToken_EnableUserInfoTrue covering the full access/refresh
  token flow and session persistence when EnableUserInfo is enabled.
- Update discovery test to assert refresh_token/implicit are NOT
  advertised when EnableUserInfo is false.
When enable_userinfo is true, the token endpoint now issues a signed JWT
access token (typ: at+jwt) containing the same claims as the id_token.
The userinfo endpoint validates the JWT signature, expiration, and issuer
inline — no session lookup or database required.

Changes:
- Replace opaque access token with signed JWT (generateAccessToken)
- Make GetUserInfo fully stateless via JWT validation
- Remove refresh token generation (unnecessary for stateless flow)
- Remove refresh_token from advertised grant types
- Update tests for JWT-based access tokens and stateless userinfo
@leifj
Copy link
Copy Markdown
Contributor Author

leifj commented May 8, 2026

Stateless UserInfo endpoint (commit 9f27985)

Building on the enable_userinfo toggle, this adds a fully functional — and fully stateless — UserInfo endpoint when enable_userinfo: true.

How it works

  1. JWT access token (RFC 9068): The token endpoint now issues an at+jwt access token containing the same claims as the id_token (sub, name, email, etc.), signed with the same key.

  2. Stateless /userinfo: The endpoint validates the JWT signature, expiration, and issuer using the signing key's public key, then returns the embedded claims. No session lookup, no database, no cache required.

  3. No refresh tokens: Since the access token is self-contained, refresh tokens are unnecessary and no longer issued.

Flow

RP → /token → { id_token: <jwt>, access_token: <at+jwt with same claims> }
RP → /userinfo (Authorization: Bearer <at+jwt>) → { sub, name, email, ... }

The JWKS endpoint already publishes the public key, so RPs can also validate the access token independently.

Why

Some OIDC RP libraries (e.g. coreos/go-oidc, golang.org/x/oauth2) require a working UserInfo endpoint when one is advertised in discovery. This gives them one that works without adding any server-side state.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

Comment thread internal/verifier/apiv1/handler_oidc.go Outdated
Comment thread internal/verifier/apiv1/handler_oidc.go
Comment thread internal/verifier/apiv1/handler_oidc.go
Comment thread internal/verifier/apiv1/handler_oidc.go
Comment thread pkg/model/config.go
Leif Johansson added 2 commits May 18, 2026 21:48
… grant, update docs

- GetUserInfo now returns 404 when EnableUserInfo=false (not just hidden in discovery)
- Validate JWT typ=at+jwt header per RFC 9068 to reject id_tokens at userinfo
- Remove implicit grant type from discovery (not implemented regardless of mode)
- Document enable_userinfo field in CONFIGURATION.md
The EnableUserInfo guard added in the previous commit requires the test
to explicitly enable the feature for the userinfo endpoint tests.
@sonarqubecloud
Copy link
Copy Markdown

@masv3971 masv3971 merged commit 1c40859 into SUNET:main May 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: verifier-OP issues unusable access token and advertises userinfo_endpoint

3 participants