Reference

Auth API

@reckona/mreact-auth builds on router sessions and adds request-scoped claims, redirecting guards, non-throwing authorization checks, and browser-safe claim serialization.

When to use this page

Use this page when configuring auth defaults, protecting loaders or route handlers, checking roles and permissions, or exposing safe session claims to client code.

Configure auth

Call configureAuth() once from server startup or an app-local auth module. Defaults are /login for missing sessions and /forbidden for failed authorization.

import { configureAuth } from "@reckona/mreact-auth";

configureAuth({
  redirectTo: "/sign-in",
  forbiddenTo: "/forbidden",
  serializeClaims(data) {
    if (typeof data !== "object" || data === null) {
      return undefined;
    }

    const session = data as { roles?: string[]; userId?: string };

    return {
      userId: session.userId,
      roles: session.roles,
    };
  },
});

Guard a loader

requireSession(), requireRole(), and requirePermission() redirect when the requirement is not met.

import { requirePermission } from "@reckona/mreact-auth";
import { sessionStore } from "../../lib/session.server.js";

export async function loader({ request }: { request: Request }) {
  const session = await requirePermission(request, sessionStore, "admin:read", {
    mode: "all",
  });

  return { user: session.data };
}

Non-throwing checks

Use tryRequireRole() and tryRequirePermission() when you want to return JSON, render partial UI, or branch without redirects.

import { tryRequireRole } from "@reckona/mreact-auth";

export async function GET(request: Request) {
  const result = await tryRequireRole(request, sessionStore, "admin");

  if (!result.authorized) {
    return Response.json({ error: result.reason }, { status: 403 });
  }

  return Response.json({ ok: true });
}

Request-scoped auth

Use runWithAuthRequest() when integration code needs request-scoped claims during a manually assembled request pipeline. Pass { config } when that request should use different auth redirects or claim serialization without changing the process-wide defaults.

import { getCurrentSession, getSessionClaims, runWithAuthRequest } from "@reckona/mreact-auth";

export async function handleRequest(request: Request, tenant: Tenant) {
  return runWithAuthRequest(
    async () => {
      await getCurrentSession(request, tenant.sessionStore);

      return Response.json({
        claims: getSessionClaims(),
      });
    },
    {
      config: {
        redirectTo: `/${tenant.slug}/login`,
        forbiddenTo: `/${tenant.slug}/forbidden`,
        serializeClaims: (data) => serializeTenantClaims(data, tenant),
      },
    },
  );
}

Server calls without an active request scope return undefined from getSessionClaims() instead of falling back to shared module-global claims.

API reference: @reckona/mreact-auth, configureAuth(), runWithAuthRequest(), requirePermission(), tryRequireRole(), and SessionStore.