memorium/routes/api/auth/callback.ts

82 lines
2.2 KiB
TypeScript

import { Handlers } from "$fresh/server.ts";
import { create, getNumericDate } from "https://deno.land/x/djwt@v2.2/mod.ts";
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/sqlite/sqlite.ts";
import { userTable } from "@lib/sqlite/schema.ts";
import { eq } from "drizzle-orm";
export const handler: Handlers = {
async GET(request) {
if (!JWT_SECRET) {
throw new BadRequestError();
}
// Exchange the authorization code for an access token
const cookies = getCookies(request.headers);
const stored = codeChallengeMap.get(cookies["code_challenge"]);
if (!stored) {
throw new BadRequestError();
}
const { codeVerifier, redirect } = stored;
const tokens = await oauth2Client.code.getToken(request.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];
}
const jwt = await create({ alg: "HS512", type: "JWT" }, {
id: user.id,
name: user.name,
exp: getNumericDate(SESSION_DURATION),
}, JWT_SECRET);
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,
});
},
};