mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
fix(api): 🐛 Fix OAuth login bugs
This commit is contained in:
parent
edf62aa015
commit
0baa9dd12d
|
|
@ -37,7 +37,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
.insert(Applications)
|
.insert(Applications)
|
||||||
.values({
|
.values({
|
||||||
name: client_name || "",
|
name: client_name || "",
|
||||||
redirectUri: redirect_uris || "",
|
redirectUri: decodeURIComponent(redirect_uris) || "",
|
||||||
scopes: scopes || "read",
|
scopes: scopes || "read",
|
||||||
website: website || null,
|
website: website || null,
|
||||||
clientId: randomBytes(32).toString("base64url"),
|
clientId: randomBytes(32).toString("base64url"),
|
||||||
|
|
|
||||||
|
|
@ -52,16 +52,22 @@ export const querySchema = z.object({
|
||||||
.default(60 * 60 * 24 * 7),
|
.default(60 * 60 * 24 * 7),
|
||||||
});
|
});
|
||||||
|
|
||||||
const returnError = (error: string, description: string) =>
|
const returnError = (query: object, error: string, description: string) => {
|
||||||
response(null, 302, {
|
const searchParams = new URLSearchParams();
|
||||||
Location: new URL(
|
|
||||||
`/oauth/authorize?${new URLSearchParams({
|
// Add all data that is not undefined except email and password
|
||||||
error: error,
|
for (const [key, value] of Object.entries(query)) {
|
||||||
error_description: description,
|
if (key !== "email" && key !== "password" && value !== undefined)
|
||||||
}).toString()}`,
|
searchParams.append(key, value);
|
||||||
config.http.base_url,
|
}
|
||||||
).toString(),
|
|
||||||
|
searchParams.append("error", error);
|
||||||
|
searchParams.append("error_description", description);
|
||||||
|
|
||||||
|
return response(null, 302, {
|
||||||
|
Location: `/oauth/authorize?${searchParams.toString()}`,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OIDC Authorization
|
* OIDC Authorization
|
||||||
|
|
@ -82,6 +88,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
|
||||||
if (!cookie)
|
if (!cookie)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"No cookies were sent with the request",
|
"No cookies were sent with the request",
|
||||||
);
|
);
|
||||||
|
|
@ -93,6 +100,7 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
|
||||||
if (!jwt)
|
if (!jwt)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"No jwt cookie was sent in the request",
|
"No jwt cookie was sent in the request",
|
||||||
);
|
);
|
||||||
|
|
@ -125,20 +133,41 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"Invalid JWT, could not verify",
|
"Invalid JWT, could not verify",
|
||||||
);
|
);
|
||||||
|
|
||||||
const payload = result.payload;
|
const payload = result.payload;
|
||||||
|
|
||||||
if (!payload.sub) return returnError("invalid_request", "Invalid sub");
|
if (!payload.sub)
|
||||||
if (!payload.aud) return returnError("invalid_request", "Invalid aud");
|
return returnError(
|
||||||
if (!payload.exp) return returnError("invalid_request", "Invalid exp");
|
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
|
// Check if the user is authenticated
|
||||||
const user = await User.fromId(payload.sub);
|
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(" ");
|
const responseTypes = response_type.split(" ");
|
||||||
|
|
||||||
|
|
@ -148,12 +177,14 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
|
||||||
if (!asksCode && !asksToken && !asksIdToken)
|
if (!asksCode && !asksToken && !asksIdToken)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"Invalid response_type, must ask for code, token, or id_token",
|
"Invalid response_type, must ask for code, token, or id_token",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (asksCode && !redirect_uri)
|
if (asksCode && !redirect_uri)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"Redirect URI is required for code flow",
|
"Redirect URI is required for code flow",
|
||||||
);
|
);
|
||||||
|
|
@ -177,12 +208,14 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
|
|
||||||
if (!application)
|
if (!application)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_client",
|
"invalid_client",
|
||||||
"Invalid client_id or client_secret",
|
"Invalid client_id or client_secret",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (application.redirectUri !== redirect_uri)
|
if (application.redirectUri !== redirect_uri)
|
||||||
return returnError(
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
"invalid_request",
|
"invalid_request",
|
||||||
"Redirect URI does not match client_id",
|
"Redirect URI does not match client_id",
|
||||||
);
|
);
|
||||||
|
|
@ -197,7 +230,11 @@ export default apiRoute<typeof meta, typeof schema>(
|
||||||
scope &&
|
scope &&
|
||||||
!scope.split(" ").every((s) => applicationScopes.includes(s))
|
!scope.split(" ").every((s) => applicationScopes.includes(s))
|
||||||
)
|
)
|
||||||
return returnError("invalid_scope", "Invalid scope");
|
return returnError(
|
||||||
|
extraData.parsedRequest,
|
||||||
|
"invalid_scope",
|
||||||
|
"Invalid scope",
|
||||||
|
);
|
||||||
|
|
||||||
// Generate tokens
|
// Generate tokens
|
||||||
const code = randomBytes(256).toString("base64url");
|
const code = randomBytes(256).toString("base64url");
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { eq } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { Tokens } from "~drizzle/schema";
|
import { Tokens } from "~drizzle/schema";
|
||||||
import { config } from "~packages/config-manager";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue