async function createSession(email: string, hostname: string) {
const { zip } = await import("https://esm.town/v/pomdtr/sql");
const { sqlite } = await import("https://esm.town/v/std/sqlite");
const sessionID = crypto.randomUUID();
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + 14);
await sqlite.batch([
`CREATE TABLE IF NOT EXISTS lastlogin_session (email TEXT, sessionID TEXT, hostname TEXT, expiresAt INTEGER)`,
{
sql: `DELETE FROM lastlogin_session WHERE email=? & hostname=?`,
args: [email, hostname],
},
{
sql: `INSERT INTO lastlogin_session VALUES (?, ?, ?, ?)`,
args: [email, sessionID, hostname, expiresAt.getTime()],
},
]);
return sessionID;
}
async function getSession(sessionID: string, hostname: string) {
const { zip } = await import("https://esm.town/v/pomdtr/sql");
const { sqlite } = await import("https://esm.town/v/std/sqlite");
try {
const res = await sqlite.execute({
sql: `SELECT * FROM lastlogin_session WHERE sessionID=? AND hostname=?`,
args: [sessionID, hostname],
});
const rows = zip(res);
if (rows.length === 0) {
return null;
}
const session = rows[0];
if (session.expiresAt < Date.now()) {
await sqlite.execute({
sql: `DELETE FROM lastlogin_session WHERE sessionID=?`,
args: [sessionID],
});
return null;
}
return session;
} catch (_) {
return null;
}
}
async function deleteSession(sessionID: string) {
const { sqlite } = await import("https://esm.town/v/std/sqlite");
await sqlite.execute({
sql: `DELETE FROM lastlogin_session WHERE sessionID=?`,
args: [sessionID],
});
}
const SESSION_COOKIE = "lastlogin_session";
const OAUTH_COOKIE = "oauth_store";
export function lastlogin(
handler: (req: Request) => Response | Promise<Response>,
) {
return async (req: Request) => {
const { api } = await import("https://esm.town/v/pomdtr/api");
const { deleteCookie, getCookies, setCookie } = await import("jsr:@std/http/cookie");
const url = new URL(req.url);
const clientID = `${url.protocol}//${url.host}/`;
const redirectUri = `${url.protocol}//${url.host}/auth/callback`;
if (url.pathname == "/auth/callback") {
const cookies = await getCookies(req.headers);
const store = JSON.parse(decodeURIComponent(cookies[OAUTH_COOKIE]));
const state = url.searchParams.get("state");
if (!state || state != store.state) {
return new Response("state mismatch", { status: 400 });
}
const code = url.searchParams.get("code");
if (!code) {
return new Response("code not found", { status: 400 });
}
const tokenUrl = new URL("https://lastlogin.io/token");
tokenUrl.searchParams.set("client_id", clientID);
tokenUrl.searchParams.set("code", code);
tokenUrl.searchParams.set("redirect_uri", redirectUri);
tokenUrl.searchParams.set("response_type", "code");
tokenUrl.searchParams.set("state", store.state);
const tokenResp = await fetch(tokenUrl.toString());
if (!tokenResp.ok) {
throw new Error(await tokenResp.text());
}