--- name: oauth description: Use when a val needs to require login with a Val Town account — gating routes behind authentication, identifying the current user, building user-specific dashboards. Covers std/oauth's `oauthMiddleware` and `getOAuthUserData`, the auto-managed `/auth/*` routes, and session behavior. For third-party OAuth providers (Google, GitHub, etc.) see the `third-party-integrations` skill instead. triggers: [oauth, auth, login, signin, sign-in, logout, session, user, authentication, account, gated, protected, private] --- # OAuth (std/oauth) Val Town provides zero-config "Log in with Val Town" via `std/oauth`. No database setup, no provider config — wrap your Hono fetch handler and you get login, logout, and session management for free. Sessions are stored in encrypted cookies and last 30 days. This is for **Val Town account login only**. For Google / GitHub / Slack / etc. OAuth, see the `third-party-integrations` skill — those flows are documented per-service. If the goal is to keep an app internal to a team rather than to give it its own logged-in users, restricting the val's app access is the simpler answer — the platform gates the endpoint before your code runs, and you write no auth code. See the `restricted-access` skill. Don't apply both to one val: a restricted val that also runs `oauthMiddleware` makes visitors authenticate twice. ## Imports ```ts import { getOAuthUserData, oauthMiddleware, } from "https://esm.town/v/std/oauth/middleware.ts"; ``` ## Wrapping your app `oauthMiddleware(handler)` takes your Hono fetch handler and returns a wrapped handler that injects three auto-managed routes: - `GET /auth/login` — starts the login flow - `GET /auth/callback` — completes the login flow - `POST /auth/logout` — clears the session Export the wrapped handler as the val's default: ```ts import { Hono } from "npm:hono"; import { oauthMiddleware } from "https://esm.town/v/std/oauth/middleware.ts"; const app = new Hono(); app.onError((err) => Promise.reject(err)); app.get("/", (c) => c.text("hello")); export default oauthMiddleware(app.fetch); ``` You don't write the `/auth/*` routes yourself — the middleware adds them. Don't shadow them in your own app. ## Reading the current user Call `getOAuthUserData(rawRequest)` from any route. In Hono, `rawRequest` is `c.req.raw`. It returns the session data if the request is authenticated, or `null` otherwise. ```ts interface SessionData { user: { id: string; username: string | null; email: string | null; bio: string | null; tier: "free" | "pro" | null; type: "user" | "org"; url: string; links: { self: string; profileImageUrl: string | null; }; }; accessToken: string; // Val Town API token (act on behalf of the user) refreshToken?: string; idToken?: string; expiresAt: number; // Unix timestamp (ms) isOrgMember?: boolean; // true if user belongs to this val's org } ``` ```ts app.get("/", async (c) => { const session = await getOAuthUserData(c.req.raw); if (session?.user) { return c.html( `
Logged in as ${session.user.username}
` + `` ); } return c.html(`Log in with Val Town`); }); ``` ## Gating routes There's no built-in "require login" helper — gate routes by checking `getOAuthUserData` and returning a 401 or redirecting to `/auth/login` when the session is missing: ```ts app.get("/dashboard", async (c) => { const session = await getOAuthUserData(c.req.raw); if (!session?.user) return c.redirect("/auth/login"); return c.html(`