mirror of
https://github.com/versia-pub/server.git
synced 2025-12-09 17:58:20 +01:00
refactor(api): 🔥 Simplify oauth authorize handler
This commit is contained in:
parent
44d7264b79
commit
c14621ee06
|
|
@ -78,12 +78,12 @@ export class MediaManager {
|
||||||
|
|
||||||
for (const preprocessor of this.preprocessors) {
|
for (const preprocessor of this.preprocessors) {
|
||||||
const result = await preprocessor.process(processedFile);
|
const result = await preprocessor.process(processedFile);
|
||||||
|
|
||||||
if ("blurhash" in result) {
|
if ("blurhash" in result) {
|
||||||
blurhash = result.blurhash as string;
|
blurhash = result.blurhash as string;
|
||||||
processedFile = result.file;
|
|
||||||
} else {
|
|
||||||
processedFile = result.file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processedFile = result.file;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadResult = await this.driver.addFile(processedFile);
|
const uploadResult = await this.driver.addFile(processedFile);
|
||||||
|
|
|
||||||
45
plugins/openid/errors.ts
Normal file
45
plugins/openid/errors.ts
Normal 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()}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ import { RolePermissions } from "@versia/kit/tables";
|
||||||
import { type JWTPayload, SignJWT, jwtVerify } from "jose";
|
import { type JWTPayload, SignJWT, jwtVerify } from "jose";
|
||||||
import { JOSEError } from "jose/errors";
|
import { JOSEError } from "jose/errors";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { errorRedirect, errors } from "../errors.ts";
|
||||||
import type { PluginType } from "../index.ts";
|
import type { PluginType } from "../index.ts";
|
||||||
|
|
||||||
const schemas = {
|
const schemas = {
|
||||||
|
|
@ -107,14 +108,7 @@ export default (plugin: PluginType): void =>
|
||||||
const { keys } = context.get("pluginConfig");
|
const { keys } = context.get("pluginConfig");
|
||||||
|
|
||||||
const errorSearchParams = new URLSearchParams(
|
const errorSearchParams = new URLSearchParams(
|
||||||
Object.fromEntries(
|
context.req.valid("json"),
|
||||||
Object.entries(context.req.valid("json")).filter(
|
|
||||||
([k, v]) =>
|
|
||||||
v !== undefined &&
|
|
||||||
k !== "password" &&
|
|
||||||
k !== "email",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await jwtVerify(jwt, keys.public, {
|
const result = await jwtVerify(jwt, keys.public, {
|
||||||
|
|
@ -130,14 +124,10 @@ export default (plugin: PluginType): void =>
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.InvalidJWT,
|
||||||
"Invalid JWT, could not verify",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,78 +136,54 @@ export default (plugin: PluginType): void =>
|
||||||
} = result;
|
} = result;
|
||||||
|
|
||||||
if (!(aud && sub && exp)) {
|
if (!(aud && sub && exp)) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.MissingJWTFields,
|
||||||
"Invalid JWT, missing required fields (aud, sub, exp)",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!z.string().uuid().safeParse(sub).success) {
|
if (!z.string().uuid().safeParse(sub).success) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.InvalidSub,
|
||||||
"Invalid JWT, sub is not a valid user ID",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await User.fromId(sub);
|
const user = await User.fromId(sub);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.UserNotFound,
|
||||||
"Invalid JWT, could not find associated user",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.hasPermission(RolePermissions.OAuth)) {
|
if (!user.hasPermission(RolePermissions.OAuth)) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.MissingOauthPermission,
|
||||||
`User is missing the required permission ${RolePermissions.OAuth}`,
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const application = await Application.fromClientId(client_id);
|
const application = await Application.fromClientId(client_id);
|
||||||
|
|
||||||
if (!application) {
|
if (!application) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.MissingApplication,
|
||||||
"Invalid client_id: no associated application found",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (application.data.redirectUri !== redirect_uri) {
|
if (application.data.redirectUri !== redirect_uri) {
|
||||||
errorSearchParams.append("error", "invalid_request");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.InvalidRedirectUri,
|
||||||
"Invalid redirect_uri: does not match application's redirect_uri",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,14 +194,10 @@ export default (plugin: PluginType): void =>
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.every((s) => application.data.scopes.includes(s))
|
.every((s) => application.data.scopes.includes(s))
|
||||||
) {
|
) {
|
||||||
errorSearchParams.append("error", "invalid_scope");
|
return errorRedirect(
|
||||||
errorSearchParams.append(
|
context,
|
||||||
"error_description",
|
errors.InvalidScope,
|
||||||
"Invalid scope: not a subset of the application's scopes",
|
errorSearchParams,
|
||||||
);
|
|
||||||
|
|
||||||
return context.redirect(
|
|
||||||
`${context.get("config").frontend.routes.login}?${errorSearchParams.toString()}`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue