[READ-ONLY] Mirror of https://github.com/flo-bit/atmo-tools.
atmo.tools
2.2 kB
73 lines
1import type { Cookies } from '@sveltejs/kit';
2import { Client } from '@atcute/client';
3import type { Did } from '@atcute/lexicons';
4import {
5 type OAuthSession,
6 TokenInvalidError,
7 TokenRefreshError,
8 TokenRevokedError,
9 AuthMethodUnsatisfiableError
10} from '@atcute/oauth-node-client';
11import { createOAuthClient } from './oauth';
12import { getSignedCookie } from './signed-cookie';
13import { scopes } from '../settings';
14
15export type SessionLocals = {
16 session: OAuthSession | null;
17 client: Client | null;
18 did: Did | null;
19};
20
21/**
22 * Restores an OAuth session from the signed `did` cookie.
23 * Returns session locals to be assigned to `event.locals`.
24 * Deletes the cookie only if the session is genuinely unrecoverable.
25 * Transient failures (network, KV) preserve the cookie for retry.
26 */
27export async function restoreSession(
28 cookies: Cookies,
29 env?: App.Platform['env']
30): Promise<SessionLocals> {
31 const did = getSignedCookie(cookies, 'did') as Did | null;
32
33 if (!did) {
34 return { session: null, client: null, did: null };
35 }
36
37 // If permissions changed since login, invalidate the session
38 const savedScope = getSignedCookie(cookies, 'scope');
39 if (savedScope !== null && savedScope !== scopes.join(' ')) {
40 cookies.delete('did', { path: '/' });
41 cookies.delete('scope', { path: '/' });
42 return { session: null, client: null, did: null };
43 }
44
45 try {
46 const oauth = createOAuthClient(env);
47 const session = await oauth.restore(did);
48
49 return {
50 session,
51 client: new Client({ handler: session }),
52 did
53 };
54 } catch (e) {
55 console.error('Failed to restore session:', e);
56
57 // Only delete cookies when the session is genuinely unrecoverable.
58 // Transient errors (network issues, KV hiccups) should preserve the
59 // cookie so the next request can retry without forcing a full re-login.
60 const isSessionGone =
61 e instanceof TokenInvalidError ||
62 e instanceof TokenRevokedError ||
63 e instanceof TokenRefreshError ||
64 e instanceof AuthMethodUnsatisfiableError;
65
66 if (isSessionGone) {
67 cookies.delete('did', { path: '/' });
68 cookies.delete('scope', { path: '/' });
69 }
70
71 return { session: null, client: null, did: null };
72 }
73}