OpenPrinting / OpenPrinting/cups

OAuth/OIDC token-validation gaps: nonce not enforced when absent, at_hash alg, inbound bearer not signature/aud/exp-validated + unverified email used for authz

Open
#1,609 0 comments 0 reactions 1 assignee View on GitHub

@michaelrsweet is already working on this.

Since Jun 27, 2026.

investigating
Dominant language
C
Stars
1.8k
Forks
331
Avg merge
9h 42m
Merged PRs (30d)
6

Description

Describe the bug

While reviewing the new (2.5) OAuth/OpenID Connect support I noticed three token-validation gaps. These are hardening / spec-compliance issues (2.5 is unreleased, so filing here per SECURITY.md rather than as an advisory); their real-world impact depends on the configured Identity Provider (IdP), so I'm raising them for discussion rather than asserting a concrete bypass.

1. nonce not enforced when the id_token omits the claim — cups/oauth.c

// cups/oauth.c:1358-1364 (cupsOAuthGetTokens)
jwt    = cupsJWTImportString(id_value, CUPS_JWS_FORMAT_COMPACT);
jnonce = cupsJWTGetClaimString(jwt, "nonce");
nonce  = oauth_load_value(auth_uri, resource_uri, _CUPS_OTYPE_NONCE, false);
// Check nonce
if (!jwt || (jnonce && nonce && strcmp(jnonce, nonce)))
  goto done;

When CUPS sent a nonce (it is persisted whenever the openid scope is requested), OIDC Core §3.1.3.7 requires the returned id_token to contain a matching nonce. Here, if the id_token omits the claim (jnonce == NULL), the jnonce && nonce && ... test short-circuits and the nonce check is skipped, so the token is accepted on signature alone. Suggested fix: if a nonce was sent (stored value exists), require jnonce != NULL && !strcmp(jnonce, nonce).

2. at_hash only validated for SHA-256-class id_tokens — cups/oauth.c:1377-1391

The access-token hash is always computed with sha2-256 and the code requires exactly 16 decoded at_hash bytes. For an id_token signed with an RS/ES/PS-384 or -512 algorithm the correct at_hash is 24/32 bytes, so a legitimate token is rejected (at_hash_bytes != 16goto done). The hash algorithm should follow the id_token's signing algorithm.

3. cupsd does not cryptographically validate inbound Bearer tokens; authorization uses the unverified email claim — scheduler/auth.c

In the Bearer branch (scheduler/auth.c:725-782), cupsd passes the token to cupsOAuthGetUserId(), which (when no cached identity exists) forwards it to the IdP userinfo_endpoint and trusts the JSON response (cups/oauth.c:1454-1518). On this inbound path there is no local verification of the token signature, iss, aud/azp, exp, or nbf (the existing cupsJWTHasValidSignature() / JWKS code is only used in the client-side token-exchange flow). Consequently a token minted by the same IdP for a different relying party can authenticate to CUPS (audience confusion), since CUPS never checks the token was issued for it.

Additionally, authorization matches the unverified email claim:

// scheduler/auth.c:1893
else if (!_cups_strcasecmp(username, name) || (con->email[0] && !_cups_strcasecmp(con->email, name)))
// scheduler/auth.c:1887,1920  -> cupsArrayFind(og->members, con->email)

email_verified is never consulted. If the configured IdP issues tokens with a user-settable/unverified email, a user could set email to a value listed in a policy's Require user ... or an OAuthGroup member file and gain that identity's access.

Suggested fixes: validate inbound tokens (signature via the IdP JWKS, plus iss/aud/exp) before trusting them, and require email_verified before using email for authorization decisions.

Expected behavior

Token and claim validation should follow the OIDC spec (enforce nonce when sent, algorithm-appropriate at_hash, and audience/expiry/email_verified checks).

System Information:

  • OS and its version: Ubuntu 24.04 (x86_64)
  • CUPS version: master / 2.5b1 (OAuth/OIDC support is 2.5-only)

Additional context

These are validation/hardening gaps rather than a demonstrated exploit; severity is IdP-dependent. Happy to split into separate issues if the maintainers prefer.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.