[PR #8678] feat(oauth-provider): extensible grant types, metadata, and token claims #16386

Open
opened 2026-04-13 10:30:33 -05:00 by GiteaMirror · 0 comments
Owner

Original Pull Request: https://github.com/better-auth/better-auth/pull/8678

State: open
Merged: No


Summary

Opens the oauth-provider for plugin-based extension. After this change, a third-party developer can create a custom OAuth grant type plugin in ~30 lines with no PRs to better-auth.

Merge order: #8677 must merge first.

Problem

The oauth-provider is sealed: z.enum() rejects unknown grant types at the schema level, the switch default throws, metadata accepts four narrow override fields, and internal utilities (createUserTokens, validateClientCredentials) are unexported. Adding a custom grant type requires forking the package.

Solution

OAuthProviderExtension contract with five extension points:

  • grantTypes: register custom handlers on the token endpoint
  • grantTypeURIs: add to the allowlist and grant_types_supported
  • metadata: contribute fields to discovery endpoints
  • tokenClaims: inject claims into access/ID tokens before signing (user's customAccessTokenClaims always overrides)
  • tokenEndpointAuthMethods: advertise additional auth methods

Token body schema changes from z.enum() to z.string() + .passthrough(). The grantTypes allowlist still rejects unknown types before the switch, so runtime behavior for valid requests is unchanged.

Exported utilities: createUserTokens, validateClientCredentials, basicToClientCredentials, getClient, checkResource, storeToken, getStoredToken.

Why this approach

Host-aggregates, guest-declares. A plugin declares contributions as static data (extensions: { "oauth-provider": { ... } satisfies OAuthProviderExtension }); the host collects via getExtensions during init. No runtime mutation, no after-hook patching, no JWT re-signing. Full compile-time checking at the producer side.

The alternative (options on OAuthOptions) requires users to coordinate between plugin config and oauth-provider config. Third-party plugins cannot self-register.

**Original Pull Request:** https://github.com/better-auth/better-auth/pull/8678 **State:** open **Merged:** No --- ## Summary Opens the oauth-provider for plugin-based extension. After this change, a third-party developer can create a custom OAuth grant type plugin in ~30 lines with no PRs to better-auth. > **Merge order**: #8677 must merge first. ## Problem The oauth-provider is sealed: `z.enum()` rejects unknown grant types at the schema level, the switch `default` throws, metadata accepts four narrow override fields, and internal utilities (`createUserTokens`, `validateClientCredentials`) are unexported. Adding a custom grant type requires forking the package. ## Solution `OAuthProviderExtension` contract with five extension points: - **`grantTypes`**: register custom handlers on the token endpoint - **`grantTypeURIs`**: add to the allowlist and `grant_types_supported` - **`metadata`**: contribute fields to discovery endpoints - **`tokenClaims`**: inject claims into access/ID tokens before signing (user's `customAccessTokenClaims` always overrides) - **`tokenEndpointAuthMethods`**: advertise additional auth methods Token body schema changes from `z.enum()` to `z.string()` + `.passthrough()`. The `grantTypes` allowlist still rejects unknown types before the switch, so runtime behavior for valid requests is unchanged. Exported utilities: `createUserTokens`, `validateClientCredentials`, `basicToClientCredentials`, `getClient`, `checkResource`, `storeToken`, `getStoredToken`. ## Why this approach Host-aggregates, guest-declares. A plugin declares contributions as static data (`extensions: { "oauth-provider": { ... } satisfies OAuthProviderExtension }`); the host collects via `getExtensions` during `init`. No runtime mutation, no after-hook patching, no JWT re-signing. Full compile-time checking at the producer side. The alternative (options on `OAuthOptions`) requires users to coordinate between plugin config and oauth-provider config. Third-party plugins cannot self-register.
GiteaMirror added the pull-request label 2026-04-13 10:30:33 -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#16386