···1+// Initialize all stores with shared KV instance
2+import { getKv } from "./kv.ts";
3+import { SessionStore } from "./session-store.ts";
4+import { OAuthStateStore } from "./oauth-state-store.ts";
5+6+const kv = await getKv();
7+8+export const sessionStore = new SessionStore(kv);
9+export const oauthStateStore = new OAuthStateStore(kv);
+8-12
frontend/src/routes/middleware.ts
···1-import { UserSessionManager, atprotoClient } from "../config.ts";
023export interface AuthenticatedUser {
4 handle?: string;
···1314export async function withAuth(req: Request): Promise<RouteContext> {
15 // Get current user info (this already restores tokens from session)
16- const currentUser = await UserSessionManager.getCurrentUser(req);
1718- // Check if we need to refresh the session cookie with updated tokens
19 let sessionCookieHeader: string | undefined;
20 if (currentUser.isAuthenticated) {
21 const tokens = atprotoClient.getTokenStorage();
22 if (tokens && tokens.accessToken) {
23- // Refresh the session cookie to extend expiration and update any refreshed tokens
24- const userData = {
25- handle: currentUser.handle,
26- sub: currentUser.sub,
27- tokens: tokens,
28- };
29- sessionCookieHeader = await UserSessionManager.createSessionCookie(
30- userData
31- );
32 }
33 }
34
···1+import { atprotoClient } from "../config.ts";
2+import { sessionStore } from "../lib/stores.ts";
34export interface AuthenticatedUser {
5 handle?: string;
···1415export async function withAuth(req: Request): Promise<RouteContext> {
16 // Get current user info (this already restores tokens from session)
17+ const currentUser = await sessionStore.getCurrentUser(req);
1819+ // Check if we need to update the session with refreshed tokens
20 let sessionCookieHeader: string | undefined;
21 if (currentUser.isAuthenticated) {
22 const tokens = atprotoClient.getTokenStorage();
23 if (tokens && tokens.accessToken) {
24+ // Update the session with any refreshed tokens
25+ await sessionStore.updateTokensFromRequest(req, tokens);
26+27+ // No need to set cookie header since session ID remains the same
0000028 }
29 }
30
+31-12
frontend/src/routes/oauth.ts
···2import {
3 atprotoClient,
4 oauthConfig,
5- OAuthStateManager,
6- UserSessionManager,
7} from "../config.ts";
0089async function handleOAuthAuthorize(req: Request): Promise<Response> {
10 try {
11 // Clear any existing auth state before new login attempt
12- atprotoClient.oauth.logout();
1314 const formData = await req.formData();
15 const loginHint = formData.get("loginHint") as string;
···18 return new Response("Missing login hint", { status: 400 });
19 }
20000021 const authResult = await atprotoClient.oauth.authorize({
22 loginHint,
23 redirectUri: oauthConfig.redirectUri,
···25 });
2627 // Store OAuth state for later verification
28- OAuthStateManager.store(authResult.state, authResult.codeVerifier);
2930 // Redirect to authorization URL
31 return Response.redirect(authResult.authorizationUrl, 302);
···58 }
5960 // Retrieve stored code verifier
61- const codeVerifier = OAuthStateManager.retrieve(state);
62 if (!codeVerifier) {
63 return Response.redirect(
64 new URL(
···70 );
71 }
72000000000073 // Exchange code for tokens
74 await atprotoClient.oauth.handleCallback({
75 code,
···79 });
8081 // Fetch and store user info
82- const userData = await UserSessionManager.refreshUserInfo();
8384- // Redirect to main app on successful login with session cookie
85- const sessionCookie = await UserSessionManager.createSessionCookie(
86- userData
87- );
88 return new Response(null, {
89 status: 302,
90 headers: {
···105}
106107async function handleLogout(req: Request): Promise<Response> {
108- atprotoClient.oauth.logout();
00000109 return new Response(null, {
110 status: 302,
111 headers: {
112 Location: new URL("/login", req.url).toString(),
113- "Set-Cookie": UserSessionManager.createClearCookie(),
114 },
115 });
116}
···2import {
3 atprotoClient,
4 oauthConfig,
005} from "../config.ts";
6+import { sessionStore, oauthStateStore } from "../lib/stores.ts";
7+import { createSessionCookie, clearSessionCookie } from "../lib/session-store.ts";
89async function handleOAuthAuthorize(req: Request): Promise<Response> {
10 try {
11 // Clear any existing auth state before new login attempt
12+ atprotoClient.oauth?.logout();
1314 const formData = await req.formData();
15 const loginHint = formData.get("loginHint") as string;
···18 return new Response("Missing login hint", { status: 400 });
19 }
2021+ if (!atprotoClient.oauth) {
22+ return new Response("OAuth client not configured", { status: 500 });
23+ }
24+25 const authResult = await atprotoClient.oauth.authorize({
26 loginHint,
27 redirectUri: oauthConfig.redirectUri,
···29 });
3031 // Store OAuth state for later verification
32+ await oauthStateStore.store(authResult.state, authResult.codeVerifier);
3334 // Redirect to authorization URL
35 return Response.redirect(authResult.authorizationUrl, 302);
···62 }
6364 // Retrieve stored code verifier
65+ const codeVerifier = await oauthStateStore.retrieve(state);
66 if (!codeVerifier) {
67 return Response.redirect(
68 new URL(
···74 );
75 }
7677+ if (!atprotoClient.oauth) {
78+ return Response.redirect(
79+ new URL(
80+ "/login?error=" + encodeURIComponent("OAuth client not configured"),
81+ req.url
82+ ),
83+ 302
84+ );
85+ }
86+87 // Exchange code for tokens
88 await atprotoClient.oauth.handleCallback({
89 code,
···93 });
9495 // Fetch and store user info
96+ const userData = await sessionStore.refreshUserInfo();
9798+ // Create new session and get session cookie
99+ const sessionId = await sessionStore.createSessionFromUserData(userData);
100+ const sessionCookie = createSessionCookie(sessionId);
101+102 return new Response(null, {
103 status: 302,
104 headers: {
···119}
120121async function handleLogout(req: Request): Promise<Response> {
122+ // Delete the session from KV store
123+ await sessionStore.deleteSessionFromRequest(req);
124+125+ // Logout from OAuth client
126+ atprotoClient.oauth?.logout();
127+128 return new Response(null, {
129 status: 302,
130 headers: {
131 Location: new URL("/login", req.url).toString(),
132+ "Set-Cookie": clearSessionCookie(),
133 },
134 });
135}
+3-1
frontend/src/routes/pages.tsx
···1import type { Route } from "@std/http/unstable-route";
2import { render } from "preact-render-to-string";
3-import { withAuth } from "./middleware.ts";
4import { atprotoClient } from "../config.ts";
5import { buildAtUri } from "../utils/at-uri.ts";
6import { IndexPage } from "../pages/IndexPage.tsx";
···1516async function handleIndexPage(req: Request): Promise<Response> {
17 const context = await withAuth(req);
001819 // Slice list page - get real slices from AT Protocol
20 let slices: Array<{ id: string; name: string; createdAt: string }> = [];
···1import type { Route } from "@std/http/unstable-route";
2import { render } from "preact-render-to-string";
3+import { withAuth, requireAuth } from "./middleware.ts";
4import { atprotoClient } from "../config.ts";
5import { buildAtUri } from "../utils/at-uri.ts";
6import { IndexPage } from "../pages/IndexPage.tsx";
···1516async function handleIndexPage(req: Request): Promise<Response> {
17 const context = await withAuth(req);
18+ const authResponse = requireAuth(context, req);
19+ if (authResponse) return authResponse;
2021 // Slice list page - get real slices from AT Protocol
22 let slices: Array<{ id: string; name: string; createdAt: string }> = [];