1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono/jsx */
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());
}