Skip to content
runlot

env.auth.user

Returns the signed-in user associated with a request, or null if the request has no authenticated session.

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const user = await env.auth.user(request);
    if (!user) return Response.redirect(new URL("/__runlot/auth/sign-in", request.url));
    return Response.json({ email: user.email });
  },
};
Always pass the request object to user(). The session lives in the request's cookies or headers, and env outlives any single request. If you omit the argument you get the error env.auth.user(request): pass the request object. Next.js is the exception: with @runlot/next the adapter tracks the current request, so you can call it with no arguments.

User

export interface User {
  id: string;                  // uuid
  email: string | null;
  emailVerified: boolean;
  name: string | null;
  avatarUrl: string | null;
  providers: string[];         // e.g. ["password", "github"]
  createdAt: string;           // ISO 8601
  lastSignInAt: string | null;
}

id is the primary key of runlot_auth.users. Use it to link user-owned rows in your own tables.

create table posts (
  id       bigserial primary key,
  owner_id uuid not null,
  title    text not null
);
const user = await env.auth.user(request);
await env.db.exec("insert into posts (owner_id, title) values ($1, $2)", [user.id, title]);

Logging out

if (url.pathname === "/logout") {
  return new Response(null, {
    status: 302,
    headers: { location: "/", ...(await env.auth.signOut(request)) },
  });
}

signOut deletes the server-side session and returns a headers object that clears the cookie. From a browser form you can also post to POST /__runlot/auth/sign-out.

Managing users

const u = await env.auth.users.get(id);
const byEmail = await env.auth.users.getByEmail("[email protected]");
const page = await env.auth.users.list({ limit: 50, cursor });
await env.auth.users.update(id, { name: "New name" });
await env.auth.users.delete(id);
await env.auth.sessions.revokeAll(userId);

list returns 50 records by default and 200 at most. An empty cursor means you are on the last page.

Browser helpers

import { config, session, signInUrl, signOutUrl } from "@runlot/auth/client";

const { user } = await session();          // { user: null } when not logged in
const { providers, password, allowSignup } = await config();
location.href = signInUrl("github", { next: "/app" });

config() reads /__runlot/auth/config: the brand, the providers that are on (OIDC providers with their name and icon), whether password and magic-link login are on, and allowSignup. It holds no secrets; providers() returns the list alone. Draw a custom login page from it instead of hard-coding buttons. This module has no external dependencies. If you pass an external URL to next, it is ignored for security.

Calling without a request

Calling env.auth.user() without a request argument is a feature of the framework adapters. Today the only adapter is @runlot/next for Next.js. Everywhere else, always pass the request object.

On this page