[PR #8292] [MERGED] feat(oauth-provider): pairwise subject identifiers (OIDC Core §8) #24774

Closed
opened 2026-04-15 22:33:17 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/8292
Author: @gustavovalverde
Created: 3/2/2026
Status: Merged
Merged: 3/3/2026
Merged by: @Bekacru

Base: canaryHead: feat/pairwise-subject-identifiers


📝 Commits (4)

  • fcd92ee feat(oauth-provider): add pairwise subject identifier support (OIDC Core Section 8)
  • 5f9b5a0 docs(oauth-provider): add pairwise subject identifiers documentation
  • 590fd27 fix(oauth): enforce sector_identifier_uri and pairwiseSecret validation
  • 832f984 fix(oauth-provider): avoid unnecessary DB call and payload mutation in pairwise sub resolution

📊 Changes

13 files changed (+794 additions, -6 deletions)

View changed files

📝 docs/content/docs/plugins/oauth-provider.mdx (+56 -0)
📝 packages/oauth-provider/src/introspect.ts (+24 -2)
📝 packages/oauth-provider/src/metadata.ts (+3 -1)
📝 packages/oauth-provider/src/oauth.ts (+8 -0)
📝 packages/oauth-provider/src/oauthClient/index.ts (+1 -0)
packages/oauth-provider/src/pairwise.test.ts (+569 -0)
📝 packages/oauth-provider/src/register.ts (+43 -0)
📝 packages/oauth-provider/src/schema.ts (+4 -0)
📝 packages/oauth-provider/src/token.ts (+3 -1)
📝 packages/oauth-provider/src/types/index.ts (+10 -0)
📝 packages/oauth-provider/src/types/oauth.ts (+8 -2)
📝 packages/oauth-provider/src/userinfo.ts (+16 -0)
📝 packages/oauth-provider/src/utils/index.ts (+49 -0)

📄 Description

Summary

Adds opt-in pairwise subject identifier support to @better-auth/oauth-provider, implementing OIDC Core Section 8. When enabled, each relying party receives a unique, unlinkable sub for the same user — preventing cross-RP user tracking.

Closes #8288

How it works

  • New config option: pairwiseSecret — a server-side HMAC-SHA256 key
  • Per-client opt-in: subject_type: "pairwise" during client registration (admin or DCR)
  • Sector identifier: derived from the client's redirect URI host per OIDC Core §8.1 — same-host clients share the same sector and get the same pairwise sub
  • Scope boundary: pairwise sub appears in id_tokens, /userinfo, and introspection. JWT access tokens retain the real user.id (needed for user lookup by resource servers)

Configuration

oauthProvider({
  loginPage: "/login",
  consentPage: "/consent",
  pairwiseSecret: process.env.PAIRWISE_SECRET, // 32+ char HMAC key
})

Then register clients with subject_type: "pairwise":

await auth.api.adminCreateOAuthClient({
  body: {
    redirect_uris: ["https://rp.example.com/callback"],
    subject_type: "pairwise",
    // ...
  },
})

Backward compatible

No pairwiseSecret configured = pure public behavior, identical to current. All existing tests pass unchanged.


Summary by cubic

Adds opt-in pairwise subject identifiers to @better-auth/oauth-provider per OIDC Core §8 so each client gets a unique, unlinkable sub for the same user. Applied to id_token, /userinfo, and introspection; discovery advertises "pairwise" only when enabled; defaults to public otherwise.

  • New Features

    • pairwiseSecret (32+ chars, HMAC-SHA256) enables pairwise subs; sector = first redirect_uri host.
    • Client registration accepts subject_type: "public" | "pairwise"; rejected if no server secret, or if redirect_uris use different hosts (until sector_identifier_uri is supported).
    • JWT access tokens keep real user.id; id_token, userinfo, and introspection return pairwise sub.
    • Userinfo/introspection sub resolution optimized to avoid unnecessary DB calls and avoid payload mutation.
  • Migration

    • Set pairwiseSecret in oauthProvider config (32+ chars).
    • Register or update clients with subject_type: "pairwise" and ensure all redirect_uris share the same host.
    • No changes needed if you don’t enable pairwise.

Written for commit 832f984a2a. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/8292 **Author:** [@gustavovalverde](https://github.com/gustavovalverde) **Created:** 3/2/2026 **Status:** ✅ Merged **Merged:** 3/3/2026 **Merged by:** [@Bekacru](https://github.com/Bekacru) **Base:** `canary` ← **Head:** `feat/pairwise-subject-identifiers` --- ### 📝 Commits (4) - [`fcd92ee`](https://github.com/better-auth/better-auth/commit/fcd92eeba96ef3a5311d96baf19eb30cecfadb68) feat(oauth-provider): add pairwise subject identifier support (OIDC Core Section 8) - [`5f9b5a0`](https://github.com/better-auth/better-auth/commit/5f9b5a0fd776fbf9a13f855ddb69d0a030facedf) docs(oauth-provider): add pairwise subject identifiers documentation - [`590fd27`](https://github.com/better-auth/better-auth/commit/590fd27738b6b465ac34533379259f91981ebd60) fix(oauth): enforce sector_identifier_uri and pairwiseSecret validation - [`832f984`](https://github.com/better-auth/better-auth/commit/832f984a2ad7089dd4f1c8d167e516883be2da69) fix(oauth-provider): avoid unnecessary DB call and payload mutation in pairwise sub resolution ### 📊 Changes **13 files changed** (+794 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `docs/content/docs/plugins/oauth-provider.mdx` (+56 -0) 📝 `packages/oauth-provider/src/introspect.ts` (+24 -2) 📝 `packages/oauth-provider/src/metadata.ts` (+3 -1) 📝 `packages/oauth-provider/src/oauth.ts` (+8 -0) 📝 `packages/oauth-provider/src/oauthClient/index.ts` (+1 -0) ➕ `packages/oauth-provider/src/pairwise.test.ts` (+569 -0) 📝 `packages/oauth-provider/src/register.ts` (+43 -0) 📝 `packages/oauth-provider/src/schema.ts` (+4 -0) 📝 `packages/oauth-provider/src/token.ts` (+3 -1) 📝 `packages/oauth-provider/src/types/index.ts` (+10 -0) 📝 `packages/oauth-provider/src/types/oauth.ts` (+8 -2) 📝 `packages/oauth-provider/src/userinfo.ts` (+16 -0) 📝 `packages/oauth-provider/src/utils/index.ts` (+49 -0) </details> ### 📄 Description ## Summary Adds opt-in **pairwise subject identifier** support to `@better-auth/oauth-provider`, implementing [OIDC Core Section 8](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes). When enabled, each relying party receives a unique, unlinkable `sub` for the same user — preventing cross-RP user tracking. Closes #8288 ### How it works - **New config option**: `pairwiseSecret` — a server-side HMAC-SHA256 key - **Per-client opt-in**: `subject_type: "pairwise"` during client registration (admin or DCR) - **Sector identifier**: derived from the client's redirect URI host per OIDC Core §8.1 — same-host clients share the same sector and get the same pairwise sub - **Scope boundary**: pairwise `sub` appears in id_tokens, `/userinfo`, and introspection. JWT access tokens retain the real `user.id` (needed for user lookup by resource servers) ### Configuration ```typescript oauthProvider({ loginPage: "/login", consentPage: "/consent", pairwiseSecret: process.env.PAIRWISE_SECRET, // 32+ char HMAC key }) ``` Then register clients with `subject_type: "pairwise"`: ```typescript await auth.api.adminCreateOAuthClient({ body: { redirect_uris: ["https://rp.example.com/callback"], subject_type: "pairwise", // ... }, }) ``` ### Backward compatible No `pairwiseSecret` configured = pure `public` behavior, identical to current. All existing tests pass unchanged. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds opt-in pairwise subject identifiers to @better-auth/oauth-provider per OIDC Core §8 so each client gets a unique, unlinkable sub for the same user. Applied to id_token, /userinfo, and introspection; discovery advertises "pairwise" only when enabled; defaults to public otherwise. - **New Features** - pairwiseSecret (32+ chars, HMAC-SHA256) enables pairwise subs; sector = first redirect_uri host. - Client registration accepts subject_type: "public" | "pairwise"; rejected if no server secret, or if redirect_uris use different hosts (until sector_identifier_uri is supported). - JWT access tokens keep real user.id; id_token, userinfo, and introspection return pairwise sub. - Userinfo/introspection sub resolution optimized to avoid unnecessary DB calls and avoid payload mutation. - **Migration** - Set pairwiseSecret in oauthProvider config (32+ chars). - Register or update clients with subject_type: "pairwise" and ensure all redirect_uris share the same host. - No changes needed if you don’t enable pairwise. <sup>Written for commit 832f984a2ad7089dd4f1c8d167e516883be2da69. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:33:17 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#24774