[PR #9069] [MERGED] refactor(generic-oauth)!: rewrite as first-class social provider with RFC compliance #16662

Closed
opened 2026-04-13 10:38:05 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/9069
Author: @gustavovalverde
Created: 4/9/2026
Status: Merged
Merged: 4/13/2026
Merged by: @gustavovalverde

Base: nextHead: refactor/generic-oauth-unification


📝 Commits (10+)

  • 9e045d3 refactor(generic-oauth)!: unify into core social sign-in flow
  • ebdfe53 fix: address CI failures and review comments
  • 9e26581 fix: move spread before explicit fields to avoid TS2783 overwrite error
  • bc7ec5a fix: preserve non-standard id field when sub is missing in userinfo response
  • 940d405 fix: resolve TS2322 — cast fallback id to string for type safety
  • 68c760a fix(generic-oauth): add RFC 9207 issuer validation, fix scopes, update docs
  • 2754eaf fix(generic-oauth): resolve merge conflicts, strengthen issuer validation
  • c15c477 fix(generic-oauth): RFC compliance and DX hardening
  • 980d891 feat(generic-oauth): typed provider IDs, docs guidance section
  • b96998d feat(generic-oauth): wire provider ID autocomplete to client signIn.social

📊 Changes

52 files changed (+972 additions, -2030 deletions)

View changed files

.changeset/generic-oauth-unification.md (+19 -0)
📝 docs/content/docs/authentication/line.mdx (+1 -4)
📝 docs/content/docs/authentication/other-social-providers.mdx (+10 -17)
📝 docs/content/docs/plugins/generic-oauth.mdx (+75 -185)
📝 docs/content/docs/plugins/sso.mdx (+118 -118)
📝 packages/better-auth/src/api/routes/callback.ts (+33 -11)
📝 packages/better-auth/src/client/client.test.ts (+0 -7)
📝 packages/better-auth/src/client/path-to-object.ts (+38 -1)
packages/better-auth/src/oauth2/error-codes.ts (+18 -0)
📝 packages/better-auth/src/plugins/admin/admin.ts (+1 -5)
📝 packages/better-auth/src/plugins/anonymous/index.ts (+0 -1)
📝 packages/better-auth/src/plugins/generic-oauth/client.ts (+5 -0)
📝 packages/better-auth/src/plugins/generic-oauth/error-codes.ts (+2 -8)
📝 packages/better-auth/src/plugins/generic-oauth/generic-oauth.test.ts (+188 -449)
📝 packages/better-auth/src/plugins/generic-oauth/index.ts (+222 -113)
📝 packages/better-auth/src/plugins/generic-oauth/providers/auth0.ts (+1 -1)
📝 packages/better-auth/src/plugins/generic-oauth/providers/gumroad.ts (+3 -1)
📝 packages/better-auth/src/plugins/generic-oauth/providers/hubspot.ts (+3 -1)
📝 packages/better-auth/src/plugins/generic-oauth/providers/keycloak.ts (+3 -1)
📝 packages/better-auth/src/plugins/generic-oauth/providers/microsoft-entra-id.ts (+1 -1)

...and 32 more files

📄 Description

Rewrites the generic OAuth plugin from a parallel endpoint set into a first-class social provider with OAuth 2.1 security defaults.

Architecture

Generic OAuth providers are now registered directly into ctx.socialProviders at init time. The plugin's dedicated endpoints (/sign-in/oauth2, /oauth2/callback/:providerId, /oauth2/link) are removed; all providers flow through the core signIn.social + /callback/:id + linkSocial path. Every feature that touches OAuth (proxy, anonymous linking, admin banned-user redirect, last-login-method, expo deep-linking) works automatically for generic providers.

RFC compliance

  • RFC 7636 (PKCE): defaults to true for all generic providers, as required by OAuth 2.1
  • RFC 9207 (Issuer Identification): callback validates iss parameter against the discovered issuer when present
  • RFC 8414 (Server Metadata): OIDC discovery fetched once at init time; issuer validated as a syntactically correct URL
  • RFC 7523 (private_key_jwt): client assertion support integrated from upstream; auth method migration generates a new secret via storeClientSecret
  • OIDC Core: openid scope auto-injected when the discovery document indicates OIDC capability

Developer experience

  • signIn.social({ provider: "my-idp" }) autocompletes generic provider IDs via const-generic type capture on genericOAuth and client-side InferGenericOAuthProviderIds
  • Pre-configured helpers (auth0, keycloak, okta, slack, etc.) return GenericOAuthConfig<"keycloak"> for literal ID preservation
  • mapProfileToUser profile parameter typed as OAuth2UserInfo & Record<string, unknown> (no more any)
  • OAUTH_CALLBACK_ERROR_CODES exported from oauth2/error-codes.ts with all redirect error strings as named constants
  • Startup diagnostics: error-level log when discovery fails without fallback endpoints; warning when no credentials configured; warning when a generic provider ID shadows a built-in
  • "When to use this plugin" docs section with customization options table

Breaking changes

  • signIn.oauth2({ providerId }) replaced by signIn.social({ provider })
  • oauth2.link() replaced by linkSocial()
  • Callback URL: /api/auth/oauth2/callback/:id changed to /api/auth/callback/:id
  • genericOAuthClient() deprecated (no longer needed)
  • authorizationUrlParams / tokenUrlParams only accept static Record<string, string>
  • issuer / requireIssuerValidation config fields removed (automatic via discovery)
  • pkce defaults to true (was false)
  • mapProfileToUser profile typed as OAuth2UserInfo & Record<string, unknown>

Closes #4917.


🔄 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/9069 **Author:** [@gustavovalverde](https://github.com/gustavovalverde) **Created:** 4/9/2026 **Status:** ✅ Merged **Merged:** 4/13/2026 **Merged by:** [@gustavovalverde](https://github.com/gustavovalverde) **Base:** `next` ← **Head:** `refactor/generic-oauth-unification` --- ### 📝 Commits (10+) - [`9e045d3`](https://github.com/better-auth/better-auth/commit/9e045d380195a5f06c14a1ba478888d7ae1ddf17) refactor(generic-oauth)!: unify into core social sign-in flow - [`ebdfe53`](https://github.com/better-auth/better-auth/commit/ebdfe5334ebd38e00525dc8a55b138f0ff61765b) fix: address CI failures and review comments - [`9e26581`](https://github.com/better-auth/better-auth/commit/9e26581f72d841e8cc1a901796c043557eb48e1a) fix: move spread before explicit fields to avoid TS2783 overwrite error - [`bc7ec5a`](https://github.com/better-auth/better-auth/commit/bc7ec5a0d902edb0d60015f3b7de551032660f11) fix: preserve non-standard id field when sub is missing in userinfo response - [`940d405`](https://github.com/better-auth/better-auth/commit/940d40595a11e1e82359ca679911dcb7c5c74a14) fix: resolve TS2322 — cast fallback id to string for type safety - [`68c760a`](https://github.com/better-auth/better-auth/commit/68c760a212a3cc2ec6faa63f3861e7729b951022) fix(generic-oauth): add RFC 9207 issuer validation, fix scopes, update docs - [`2754eaf`](https://github.com/better-auth/better-auth/commit/2754eaf379508d1508855c188df612113a892ab2) fix(generic-oauth): resolve merge conflicts, strengthen issuer validation - [`c15c477`](https://github.com/better-auth/better-auth/commit/c15c47763f0271859548861c4e414ebcfee760b5) fix(generic-oauth): RFC compliance and DX hardening - [`980d891`](https://github.com/better-auth/better-auth/commit/980d8911df77643a42e71527a7892309371ec3c8) feat(generic-oauth): typed provider IDs, docs guidance section - [`b96998d`](https://github.com/better-auth/better-auth/commit/b96998d3686beb4138e334d705fb51b6d71acaec) feat(generic-oauth): wire provider ID autocomplete to client signIn.social ### 📊 Changes **52 files changed** (+972 additions, -2030 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/generic-oauth-unification.md` (+19 -0) 📝 `docs/content/docs/authentication/line.mdx` (+1 -4) 📝 `docs/content/docs/authentication/other-social-providers.mdx` (+10 -17) 📝 `docs/content/docs/plugins/generic-oauth.mdx` (+75 -185) 📝 `docs/content/docs/plugins/sso.mdx` (+118 -118) 📝 `packages/better-auth/src/api/routes/callback.ts` (+33 -11) 📝 `packages/better-auth/src/client/client.test.ts` (+0 -7) 📝 `packages/better-auth/src/client/path-to-object.ts` (+38 -1) ➕ `packages/better-auth/src/oauth2/error-codes.ts` (+18 -0) 📝 `packages/better-auth/src/plugins/admin/admin.ts` (+1 -5) 📝 `packages/better-auth/src/plugins/anonymous/index.ts` (+0 -1) 📝 `packages/better-auth/src/plugins/generic-oauth/client.ts` (+5 -0) 📝 `packages/better-auth/src/plugins/generic-oauth/error-codes.ts` (+2 -8) 📝 `packages/better-auth/src/plugins/generic-oauth/generic-oauth.test.ts` (+188 -449) 📝 `packages/better-auth/src/plugins/generic-oauth/index.ts` (+222 -113) 📝 `packages/better-auth/src/plugins/generic-oauth/providers/auth0.ts` (+1 -1) 📝 `packages/better-auth/src/plugins/generic-oauth/providers/gumroad.ts` (+3 -1) 📝 `packages/better-auth/src/plugins/generic-oauth/providers/hubspot.ts` (+3 -1) 📝 `packages/better-auth/src/plugins/generic-oauth/providers/keycloak.ts` (+3 -1) 📝 `packages/better-auth/src/plugins/generic-oauth/providers/microsoft-entra-id.ts` (+1 -1) _...and 32 more files_ </details> ### 📄 Description Rewrites the generic OAuth plugin from a parallel endpoint set into a first-class social provider with OAuth 2.1 security defaults. ### Architecture Generic OAuth providers are now registered directly into `ctx.socialProviders` at init time. The plugin's dedicated endpoints (`/sign-in/oauth2`, `/oauth2/callback/:providerId`, `/oauth2/link`) are removed; all providers flow through the core `signIn.social` + `/callback/:id` + `linkSocial` path. Every feature that touches OAuth (proxy, anonymous linking, admin banned-user redirect, last-login-method, expo deep-linking) works automatically for generic providers. ### RFC compliance - **RFC 7636 (PKCE)**: defaults to `true` for all generic providers, as required by OAuth 2.1 - **RFC 9207 (Issuer Identification)**: callback validates `iss` parameter against the discovered issuer when present - **RFC 8414 (Server Metadata)**: OIDC discovery fetched once at init time; issuer validated as a syntactically correct URL - **RFC 7523 (private_key_jwt)**: client assertion support integrated from upstream; auth method migration generates a new secret via `storeClientSecret` - **OIDC Core**: `openid` scope auto-injected when the discovery document indicates OIDC capability ### Developer experience - `signIn.social({ provider: "my-idp" })` autocompletes generic provider IDs via const-generic type capture on `genericOAuth` and client-side `InferGenericOAuthProviderIds` - Pre-configured helpers (`auth0`, `keycloak`, `okta`, `slack`, etc.) return `GenericOAuthConfig<"keycloak">` for literal ID preservation - `mapProfileToUser` profile parameter typed as `OAuth2UserInfo & Record<string, unknown>` (no more `any`) - `OAUTH_CALLBACK_ERROR_CODES` exported from `oauth2/error-codes.ts` with all redirect error strings as named constants - Startup diagnostics: error-level log when discovery fails without fallback endpoints; warning when no credentials configured; warning when a generic provider ID shadows a built-in - "When to use this plugin" docs section with customization options table ### Breaking changes - `signIn.oauth2({ providerId })` replaced by `signIn.social({ provider })` - `oauth2.link()` replaced by `linkSocial()` - Callback URL: `/api/auth/oauth2/callback/:id` changed to `/api/auth/callback/:id` - `genericOAuthClient()` deprecated (no longer needed) - `authorizationUrlParams` / `tokenUrlParams` only accept static `Record<string, string>` - `issuer` / `requireIssuerValidation` config fields removed (automatic via discovery) - `pkce` defaults to `true` (was `false`) - `mapProfileToUser` profile typed as `OAuth2UserInfo & Record<string, unknown>` Closes #4917. --- <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-13 10:38:05 -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#16662