mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-18 11:32:15 -05:00
fix(oauth-provider): report an unknown client_id as not found (#11799)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@better-auth/oauth-provider": patch
|
||||
---
|
||||
|
||||
Requests that name a `client_id` the server does not know now fail with `error_description` set to `client not found`. Before, `/oauth2/authorize` said `client_id is required`, which read as a missing parameter, and `/oauth2/token`, `/oauth2/revoke`, and `/oauth2/introspect` said `missing client`. You would see this, for example, when an MCP client kept a registration the server had deleted. The `invalid_client` error code is unchanged.
|
||||
@@ -295,6 +295,33 @@ describe("oauth authorize - unauthenticated", async () => {
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/croffasia/itsaplan/issues/564
|
||||
*/
|
||||
it("reports an unknown client_id as a client that was not found", async () => {
|
||||
const authUrl = new URL(`${authServerBaseUrl}/api/auth/oauth2/authorize`);
|
||||
authUrl.search = new URLSearchParams({
|
||||
response_type: "code",
|
||||
client_id: "no-such-client",
|
||||
redirect_uri: redirectUri,
|
||||
code_challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
|
||||
code_challenge_method: "S256",
|
||||
}).toString();
|
||||
|
||||
let location = "";
|
||||
await unauthenticatedClient.$fetch(authUrl.toString(), {
|
||||
onError(context) {
|
||||
location = context.response.headers.get("Location") || "";
|
||||
},
|
||||
});
|
||||
const errorUrl = new URL(location, authServerBaseUrl);
|
||||
expect(errorUrl.origin).toBe(authServerBaseUrl);
|
||||
expect(errorUrl.searchParams.get("error")).toBe("invalid_client");
|
||||
expect(errorUrl.searchParams.get("error_description")).toBe(
|
||||
"client not found",
|
||||
);
|
||||
});
|
||||
|
||||
it("should replace incoming sig when signing the login redirect", async () => {
|
||||
if (!oauthClient?.client_id || !oauthClient?.client_secret) {
|
||||
throw Error("beforeAll not run properly");
|
||||
|
||||
@@ -566,7 +566,7 @@ export async function authorizeEndpoint(
|
||||
if (!client) {
|
||||
return handleRedirect(
|
||||
ctx,
|
||||
getErrorURL(ctx, "invalid_client", "client_id is required"),
|
||||
getErrorURL(ctx, "invalid_client", "client not found"),
|
||||
);
|
||||
}
|
||||
if (client.disabled) {
|
||||
|
||||
@@ -2369,6 +2369,31 @@ describe("oauth token - client_credentials", async () => {
|
||||
).toContain("registered for client_secret_basic");
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/croffasia/itsaplan/issues/564
|
||||
*/
|
||||
it("reports an unknown client_id as a client that was not found", async () => {
|
||||
const response = await client.$fetch<Record<string, unknown>>(
|
||||
"/oauth2/token",
|
||||
{
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
grant_type: "client_credentials",
|
||||
client_id: "no-such-client",
|
||||
client_secret: "no-such-secret",
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(response.error).toMatchObject({
|
||||
error: "invalid_client",
|
||||
error_description: "client not found",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns 400 for failed client_secret_post authentication", async () => {
|
||||
const postClient = await auth.api.adminCreateOAuthClient({
|
||||
headers,
|
||||
|
||||
@@ -749,7 +749,7 @@ export async function validateClientCredentials(
|
||||
) {
|
||||
const client = await getClient(ctx, options, clientId);
|
||||
if (!client) {
|
||||
throwInvalidClient("missing client", { method: authMethod });
|
||||
throwInvalidClient("client not found", { method: authMethod });
|
||||
}
|
||||
if (client.disabled) {
|
||||
throwInvalidClient("client is disabled", { method: authMethod });
|
||||
|
||||
Reference in New Issue
Block a user