Files
tiennm99 559bac8104 feat(auth): replace Supabase Auth with app-native GitHub OAuth
Self-contained GitHub OAuth (Arctic) with a stateless HS256 signed-cookie
session (jose); Supabase is downgraded to the Postgres host only.

- Origin-derived callback (no redirect-uri env); read:user scope; access
  token read once at callback and discarded (no token storage).
- CSRF via single-use state cookie; open-redirect guard on next.
- getCurrentGithubIdentity() now reads the session cookie, preserving the
  numeric provider_id identity contract for admin/dashboard/mint.
- Remove @supabase/ssr + @supabase/supabase-js, middleware, and the
  supabase-dependent rls test; delete lib/supabase clients.
2026-06-14 12:19:40 +07:00

66 lines
1.8 KiB
JavaScript

import "server-only";
import { cookies } from "next/headers";
import {
SESSION_MAX_AGE_SECONDS,
encodeSecret,
signSessionToken,
verifySessionToken,
} from "./session-token";
/**
* Stateless signed-cookie session. The app has no user table — identity is just
* the GitHub `provider_id` (numeric, immutable) + login, carried in an HS256 JWT
* inside an httpOnly cookie. No DB session row; on expiry the user re-logs in.
* JWT logic lives in `session-token.js` (testable); this module wires cookies.
*/
const COOKIE_NAME = "llmapikey_session";
/**
* Cookie attributes. SameSite=Lax (not Strict) so the cookie rides the
* top-level GET redirect back from GitHub; Strict would drop it.
*/
const COOKIE_ATTRS = {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
maxAge: SESSION_MAX_AGE_SECONDS,
};
/** @returns {Uint8Array} */
function secret() {
return encodeSecret(process.env.AUTH_SESSION_SECRET);
}
/**
* Sign the identity into the session cookie.
*
* @param {{ githubUserId: string, githubUsername: string }} identity
*/
export async function createSession(identity) {
const jwt = await signSessionToken(identity, secret());
const cookieStore = await cookies();
cookieStore.set(COOKIE_NAME, jwt, COOKIE_ATTRS);
}
/**
* Read + verify the session cookie. Token errors → null; config errors (bad
* `AUTH_SESSION_SECRET`) propagate (intentional fail-fast).
*
* @returns {Promise<{ githubUserId: string, githubUsername: string } | null>}
*/
export async function readSession() {
const cookieStore = await cookies();
const token = cookieStore.get(COOKIE_NAME)?.value;
return verifySessionToken(token, secret());
}
/** Clear the session cookie (sign-out). */
export async function clearSession() {
const cookieStore = await cookies();
cookieStore.delete(COOKIE_NAME);
}