@ai-matrx/auth
Sign-in, session and organizations for every AI Matrx app — the small package at the top of the
app. Framework-free core; /react and /next sections; pay only for the sections you import.
import { createAuthClient, signInWithPassword, sessionCredentials, signOut } from "@ai-matrx/auth";
import { createActiveOrganization, readMemberships } from "@ai-matrx/auth/organizations";
const supabase = createAuthClient({ url: "https://db.matrxserver.com", key: PUBLISHABLE_KEY });
const { person } = await signInWithPassword(supabase, { email, password });
const active = createActiveOrganization({ client: supabase });
const orgId = await active.ensureOrgId(); // held until the ladder answers
const credentials = sessionCredentials(supabase); // hand to @ai-matrx/api's createApi
| Section | What |
|---|---|
@ai-matrx/auth | sign-in/out, session, refresh, sessionCredentials, storage per platform, createAuthClient |
/organizations | memberships, favorites, the active-organization ladder and createActiveOrganization |
/credentials | the five credential kinds and the one credential → header mapping |
/session-identity | who is acting, live from the session |
/cookies | the domain-wide auth cookie and the active-organization cookie |
/next | every Next.js Supabase client (browser, RSC, route, middleware) |
/browser | the framework-free browser Supabase client over the shared auth cookie (non-Next apps) |
/react | useSession, useActiveOrganization, useSessionIdentity |
/node | fileStorage — a script's session survives between runs |
/testing | fake sessions for tests |
Session storage: browser browserStorage(), extension extensionStorage(), desktop
keyValueStorage(store), Node fileStorage(path) or memoryStorage(), Next.js /next.
The active organization is never a list filter: reads show everything the person can see; the active organization is only where new things are saved and which organization a server call runs in.
Sections added in 0.2.0 — one entry point each
| Need | Import |
|---|---|
| Who is calling (local claims; browser de-dup) | getClaimsUser, createClaimsCache from @ai-matrx/auth |
| Server identity in a Next app | createServerAuth({ createClient, supabaseUrl, cache }) from @ai-matrx/auth/next |
| API route caller (Bearer or cookie) | verifyRequest(request, { url, publishableKey, cookieClient }) from @ai-matrx/auth/node (or /next) |
| Server-only secret-key client | createServiceClient({ url, secretKey }) from @ai-matrx/auth/node (or /next) |
| "Needs an organization" refusal | isOrganizationRequiredError, organizationRequired, organizationRequiredResponse from @ai-matrx/auth/organizations |
| Sign in with AI Matrx (extension, desktop, CLI) | createMatrxOAuth(config, { storage, openAuthorization }) from @ai-matrx/auth/matrx-oauth |
Is this person an admin (their own admin.admins row; admin + level / not_admin / unknown) | readAdminStatus(client, userId) from @ai-matrx/auth/admin |
| Decode a JWT segment (base64url → bytes / UTF-8 text; no signature check) | base64UrlToBytes, base64UrlToText from @ai-matrx/auth/jwt (dependency-free) |
@ai-matrx/auth/spa — a Vite SPA that holds its own token
For a host that is NOT a Next/cookie app (admin dashboard, Workflow Studio): sign-in arrives as a
one-time handoff code from aidream's OAuth hop, the access token lives in localStorage, and a
signed-in aimatrx.com session on the same apex refreshes it through the session bridge.
import { sendMatrxRequest } from "@ai-matrx/api";
import { createSpaSession, createSpaSupabase, adminWhoamiVerifier } from "@ai-matrx/auth/spa";
export const session = createSpaSession({
request: sendMatrxRequest,
onBeforeTokenChange: (prev, next) => transitionQueryIdentity(prev, next),
onTokenChange: () => refreshOrganizationIdentity(),
bridge: { verifyToken: adminWhoamiVerifier(AIDREAM_API_URL) }, // dashboard only: admin gate
});
export const supabase = createSpaSupabase({ url, publishableKey, session, headers: { "x-matrx-admin-lane": "1" } });
Callback route: classifyCallback(location.search) -> exchangeHandoff(api, handoff, sendMatrxRequest) -> session.setToken(token). Pass the same request to reportTokenInQuery(api, params, where, sendMatrxRequest). The request is required: @ai-matrx/auth/spa does not make network calls through global fetch.
Options: tokenKey (default matrx_admin_token), storage, loginPath (/login), navigate,
bridge.sessionTokenUrl, bridge.sameSiteOnly (true), bridge.attemptLimit (3) / attemptWindowMs (20 000).
The active organization: createSpaOrganizationContext({ legacyStorageKey, apexDomains, tokenKey, serverUrl, db, sendRequest, bindOrganizationHeader, onIdentityChanged }) returns withOrganizationHeader, peekOrganizationId, chooseOrganization, listMemberOrganizations, the cookie and the rest; pass sendMatrxRequest and applyOrganizationContextHeader from @ai-matrx/api.