fabro/apps/arc-web/app/lib/session.server.ts
Bryan Helmkamp fb47745cc2 Add Tailscale authentication provider
Read Tailscale-User-Login/Name/Profile-Pic headers when web.auth.provider
is "tailscale", checking login against required allowed_usernames list.
Rename githubLogin → login across session/callback/shell for provider
neutrality. Update app-shell loader and auth-login page to handle the
new provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 23:46:55 -05:00

70 lines
2 KiB
TypeScript

import { redirect } from "react-router";
import { getAppConfig } from "./config.server";
import { createSqliteSessionStorage } from "./session-storage.server";
interface SessionData {
userUrl: string;
githubId: number;
githubNodeId: string;
login: string;
name: string;
email: string;
avatarUrl: string;
accessToken: string;
}
function getSessionStorage() {
const secret = process.env.SESSION_SECRET;
if (!secret) {
throw new Error("SESSION_SECRET is not set");
}
return createSqliteSessionStorage(secret);
}
export async function getSession(request: Request) {
const storage = getSessionStorage();
return storage.getSession(request.headers.get("Cookie"));
}
export async function commitSession(session: Awaited<ReturnType<typeof getSession>>) {
const storage = getSessionStorage();
return storage.commitSession(session);
}
export async function destroySession(session: Awaited<ReturnType<typeof getSession>>) {
const storage = getSessionStorage();
return storage.destroySession(session);
}
export async function getUser(request: Request) {
const { provider, allowed_usernames } = getAppConfig().web.auth;
if (provider === "tailscale") {
const login = request.headers.get("Tailscale-User-Login");
if (!login || !allowed_usernames.includes(login)) return null;
return {
userUrl: `tailscale:${login}`,
login,
name: request.headers.get("Tailscale-User-Name") ?? login,
email: login,
avatarUrl: request.headers.get("Tailscale-User-Profile-Pic") ?? "",
};
}
const session = await getSession(request);
const login = session.get("login");
if (!login) return null;
return {
userUrl: session.get("userUrl") ?? "",
login,
name: session.get("name") ?? login,
email: session.get("email") ?? "",
avatarUrl: session.get("avatarUrl") ?? "",
};
}
export async function requireUser(request: Request) {
const user = await getUser(request);
if (!user) throw redirect("/auth/login");
return user;
}