refactor(api): 🔥 Simplify oauth authorize handler

This commit is contained in:
Jesse Wierzbinski 2024-12-30 16:47:48 +01:00
parent 44d7264b79
commit c14621ee06
No known key found for this signature in database
3 changed files with 82 additions and 75 deletions

View file

@ -78,12 +78,12 @@ export class MediaManager {
for (const preprocessor of this.preprocessors) {
const result = await preprocessor.process(processedFile);
if ("blurhash" in result) {
blurhash = result.blurhash as string;
processedFile = result.file;
} else {
processedFile = result.file;
}
processedFile = result.file;
}
const uploadResult = await this.driver.addFile(processedFile);

45
plugins/openid/errors.ts Normal file
View file

@ -0,0 +1,45 @@
import type { Context, TypedResponse } from "hono";
export const errors = {
InvalidJWT: ["invalid_request", "Invalid JWT: could not verify"],
MissingJWTFields: [
"invalid_request",
"Invalid JWT: missing required fields (aud, sub, exp, iss)",
],
InvalidSub: ["invalid_request", "Invalid JWT: sub is not a valid user ID"],
UserNotFound: [
"invalid_request",
"Invalid JWT, could not find associated user",
],
MissingOauthPermission: [
"unauthorized",
"User missing required 'oauth' permission",
],
MissingApplication: [
"invalid_request",
"Invalid client_id: no associated API application found",
],
InvalidRedirectUri: [
"invalid_request",
"Invalid redirect_uri: does not match API application's redirect_uri",
],
InvalidScope: [
"invalid_request",
"Invalid scope: not a subset of the application's scopes",
],
};
export const errorRedirect = (
context: Context,
error: (typeof errors)[keyof typeof errors],
extraParams?: URLSearchParams,
): Response & TypedResponse<undefined, 302, "redirect"> => {
const errorSearchParams = new URLSearchParams(extraParams);
errorSearchParams.append("error", error[0]);
errorSearchParams.append("error_description", error[1]);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
);
};

View file

@ -5,6 +5,7 @@ import { RolePermissions } from "@versia/kit/tables";
import { type JWTPayload, SignJWT, jwtVerify } from "jose";
import { JOSEError } from "jose/errors";
import { z } from "zod";
import { errorRedirect, errors } from "../errors.ts";
import type { PluginType } from "../index.ts";
const schemas = {
@ -107,14 +108,7 @@ export default (plugin: PluginType): void =>
const { keys } = context.get("pluginConfig");
const errorSearchParams = new URLSearchParams(
Object.fromEntries(
Object.entries(context.req.valid("json")).filter(
([k, v]) =>
v !== undefined &&
k !== "password" &&
k !== "email",
),
),
context.req.valid("json"),
);
const result = await jwtVerify(jwt, keys.public, {
@ -130,14 +124,10 @@ export default (plugin: PluginType): void =>
});
if (!result) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid JWT, could not verify",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.InvalidJWT,
errorSearchParams,
);
}
@ -146,78 +136,54 @@ export default (plugin: PluginType): void =>
} = result;
if (!(aud && sub && exp)) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid JWT, missing required fields (aud, sub, exp)",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.MissingJWTFields,
errorSearchParams,
);
}
if (!z.string().uuid().safeParse(sub).success) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid JWT, sub is not a valid user ID",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.InvalidSub,
errorSearchParams,
);
}
const user = await User.fromId(sub);
if (!user) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid JWT, could not find associated user",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.UserNotFound,
errorSearchParams,
);
}
if (!user.hasPermission(RolePermissions.OAuth)) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
`User is missing the required permission ${RolePermissions.OAuth}`,
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.MissingOauthPermission,
errorSearchParams,
);
}
const application = await Application.fromClientId(client_id);
if (!application) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid client_id: no associated application found",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.MissingApplication,
errorSearchParams,
);
}
if (application.data.redirectUri !== redirect_uri) {
errorSearchParams.append("error", "invalid_request");
errorSearchParams.append(
"error_description",
"Invalid redirect_uri: does not match application's redirect_uri",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.InvalidRedirectUri,
errorSearchParams,
);
}
@ -228,14 +194,10 @@ export default (plugin: PluginType): void =>
.split(" ")
.every((s) => application.data.scopes.includes(s))
) {
errorSearchParams.append("error", "invalid_scope");
errorSearchParams.append(
"error_description",
"Invalid scope: not a subset of the application's scopes",
);
return context.redirect(
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
return errorRedirect(
context,
errors.InvalidScope,
errorSearchParams,
);
}