Files
memorium/routes/api/auth/callback.ts
2026-01-10 13:03:29 +01:00

94 lines
2.5 KiB
TypeScript

import { create, getNumericDate } from "@zaubrik/djwt";
import { oauth2Client } from "@lib/auth.ts";
import { getCookies, setCookie } from "@std/http/cookie";
import { codeChallengeMap } from "./login.ts";
import { GITEA_SERVER, JWT_SECRET, SESSION_DURATION } from "@lib/env.ts";
import { GiteaOauthUser } from "@lib/types.ts";
import { BadRequestError } from "@lib/errors.ts";
import { db } from "@lib/db/sqlite.ts";
import { userTable } from "@lib/db/schema.ts";
import { eq } from "drizzle-orm";
import { define } from "../../../utils.ts";
export const handler = define.handlers({
async GET(ctx) {
if (!JWT_SECRET) {
throw new BadRequestError();
}
// Exchange the authorization code for an access token
const cookies = getCookies(ctx.req.headers);
const stored = codeChallengeMap.get(cookies["code_challenge"]);
if (!stored) {
throw new BadRequestError();
}
const { codeVerifier, redirect } = stored;
const tokens = await oauth2Client.code.getToken(ctx.req.url, {
codeVerifier,
});
// Use the access token to make an authenticated API request
const userInfo = `${GITEA_SERVER}/login/oauth/userinfo`;
const userResponse = await fetch(userInfo, {
headers: {
Authorization: `token ${tokens.accessToken}`,
},
});
const oauthUser = await userResponse.json() as GiteaOauthUser;
let user = await db.select().from(userTable).where(
eq(userTable.name, oauthUser.name),
).limit(1).then((users) => users[0]);
if (!user) {
const res = await db.insert(userTable).values({
id: crypto.randomUUID(),
email: oauthUser.email,
name: oauthUser.name,
}).returning();
user = res[0];
}
if (!JWT_SECRET) {
throw new BadRequestError();
}
const key = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(JWT_SECRET),
{ name: "HMAC", hash: "SHA-512" },
false,
["sign", "verify"],
);
const jwt = await create({ alg: "HS512", type: "JWT" }, {
id: user.id,
name: user.name,
exp: getNumericDate(SESSION_DURATION),
}, key);
const headers = new Headers({
location: redirect || "/",
});
setCookie(headers, {
name: "session_cookie",
value: jwt,
path: "/",
maxAge: SESSION_DURATION,
httpOnly: false,
secure: true,
sameSite: "Lax",
});
return new Response(null, {
headers,
status: 302,
});
},
});