From 0baa9dd12dbda9338cf40b42c6883168de0a0ebd Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 1 May 2024 18:34:24 -1000 Subject: [PATCH] fix(api): :bug: Fix OAuth login bugs --- server/api/api/v1/apps/index.ts | 2 +- server/api/oauth/authorize/index.ts | 65 ++++++++++++++++++++++------- server/api/oauth/token/index.ts | 1 - 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/server/api/api/v1/apps/index.ts b/server/api/api/v1/apps/index.ts index f6e30364..11b3f6e5 100644 --- a/server/api/api/v1/apps/index.ts +++ b/server/api/api/v1/apps/index.ts @@ -37,7 +37,7 @@ export default apiRoute( .insert(Applications) .values({ name: client_name || "", - redirectUri: redirect_uris || "", + redirectUri: decodeURIComponent(redirect_uris) || "", scopes: scopes || "read", website: website || null, clientId: randomBytes(32).toString("base64url"), diff --git a/server/api/oauth/authorize/index.ts b/server/api/oauth/authorize/index.ts index c3a17142..d39b64b6 100644 --- a/server/api/oauth/authorize/index.ts +++ b/server/api/oauth/authorize/index.ts @@ -52,16 +52,22 @@ export const querySchema = z.object({ .default(60 * 60 * 24 * 7), }); -const returnError = (error: string, description: string) => - response(null, 302, { - Location: new URL( - `/oauth/authorize?${new URLSearchParams({ - error: error, - error_description: description, - }).toString()}`, - config.http.base_url, - ).toString(), +const returnError = (query: object, error: string, description: string) => { + const searchParams = new URLSearchParams(); + + // Add all data that is not undefined except email and password + for (const [key, value] of Object.entries(query)) { + if (key !== "email" && key !== "password" && value !== undefined) + searchParams.append(key, value); + } + + searchParams.append("error", error); + searchParams.append("error_description", description); + + return response(null, 302, { + Location: `/oauth/authorize?${searchParams.toString()}`, }); +}; /** * OIDC Authorization @@ -82,6 +88,7 @@ export default apiRoute( if (!cookie) return returnError( + extraData.parsedRequest, "invalid_request", "No cookies were sent with the request", ); @@ -93,6 +100,7 @@ export default apiRoute( if (!jwt) return returnError( + extraData.parsedRequest, "invalid_request", "No jwt cookie was sent in the request", ); @@ -125,20 +133,41 @@ export default apiRoute( if (!result) return returnError( + extraData.parsedRequest, "invalid_request", "Invalid JWT, could not verify", ); const payload = result.payload; - if (!payload.sub) return returnError("invalid_request", "Invalid sub"); - if (!payload.aud) return returnError("invalid_request", "Invalid aud"); - if (!payload.exp) return returnError("invalid_request", "Invalid exp"); + if (!payload.sub) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Invalid sub", + ); + if (!payload.aud) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Invalid aud", + ); + if (!payload.exp) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Invalid exp", + ); // Check if the user is authenticated const user = await User.fromId(payload.sub); - if (!user) return returnError("invalid_request", "Invalid sub"); + if (!user) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Invalid sub", + ); const responseTypes = response_type.split(" "); @@ -148,12 +177,14 @@ export default apiRoute( if (!asksCode && !asksToken && !asksIdToken) return returnError( + extraData.parsedRequest, "invalid_request", "Invalid response_type, must ask for code, token, or id_token", ); if (asksCode && !redirect_uri) return returnError( + extraData.parsedRequest, "invalid_request", "Redirect URI is required for code flow", ); @@ -177,12 +208,14 @@ export default apiRoute( if (!application) return returnError( + extraData.parsedRequest, "invalid_client", "Invalid client_id or client_secret", ); if (application.redirectUri !== redirect_uri) return returnError( + extraData.parsedRequest, "invalid_request", "Redirect URI does not match client_id", ); @@ -197,7 +230,11 @@ export default apiRoute( scope && !scope.split(" ").every((s) => applicationScopes.includes(s)) ) - return returnError("invalid_scope", "Invalid scope"); + return returnError( + extraData.parsedRequest, + "invalid_scope", + "Invalid scope", + ); // Generate tokens const code = randomBytes(256).toString("base64url"); diff --git a/server/api/oauth/token/index.ts b/server/api/oauth/token/index.ts index a01a02d3..16d12760 100644 --- a/server/api/oauth/token/index.ts +++ b/server/api/oauth/token/index.ts @@ -4,7 +4,6 @@ import { eq } from "drizzle-orm"; import { z } from "zod"; import { db } from "~drizzle/db"; import { Tokens } from "~drizzle/schema"; -import { config } from "~packages/config-manager"; export const meta = applyConfig({ allowedMethods: ["POST"],