TypeScript — Browser
The @mailmus/auth browser SDK — sign-up and sign-in for your Customers, with the publishable key.
Package: @mailmus/auth — handwritten (not generated), zero runtime dependencies, ESM only. Authenticated with your publishable key: exposing it in the browser is safe, unlike the secret key.
npm install @mailmus/authMailmusAuthClient holds state (session, refresh in flight, timer) — create it once per tab, in a dedicated file, never on demand inside a component or a handler:
// lib/mailmus-auth.ts
import { MailmusAuthClient } from "@mailmus/auth";
export const auth = new MailmusAuthClient({ publishableKey: "mailmus_publishable_key_live_..." });Then import that same instance everywhere else:
import { auth } from "./lib/mailmus-auth";That auth instance is the one the rest of this page uses.
publishableKey alone is enough: serverURL and hostedPagesURL point at production by default, and the project is decoded straight from the key (every key generated from the dashboard carries it). Set projectId/serverURL/hostedPagesURL explicitly only to point at local or staging, or if your key predates 13 August 2026 (the format without an embedded project).
No cookies are used: the tokens travel as Bearer tokens and are persisted on the client (localStorage by default, with a pluggable TokenStorage through the storage option).
[!NOTE] In React, do not build
MailmusAuthClientyourself — use@mailmus/auth-react, which already has its own single entry point (<MailmusAuthProvider>).
Sign-up / Sign-in
await auth.signUp({ email, password });
await auth.signIn({ email, password }); // can return an MFA challenge, see isMfaChallenge
await auth.signOut();
await auth.requestMagicLink({ email, redirectUri });
await auth.verifyMagicLink({ token });
await auth.requestOtp({ email });
await auth.verifyOtp({ email, code });
await auth.forgotPassword({ email, redirectUri });
await auth.resetPassword({ token, newPassword });
await auth.verifyEmail({ token });Every first-sign-in method (signIn, verifyMagicLink, verifyOtp, resetPassword) can return an MFA challenge instead of a session — use isMfaChallenge(result) to detect it.
auth.oauth — Google / GitHub / Apple OAuth
// Full-page redirect
await auth.oauth.redirect({ provider: "google", redirectUri });
// Or a popup, which resolves directly with the tokens
const tokens = await auth.oauth.popup({ provider: "google", redirectUri });The SDK never talks to the OAuth providers directly — everything goes through the Mailmus hosted pages. redirectUri must appear in the project's redirect allowlist (Project → Auth Settings).
auth.mfa — Two-factor authentication (TOTP)
const { secret, otpauthUrl } = await auth.mfa.enroll();
const { backupCodes } = await auth.mfa.confirm({ code }); // the first TOTP code generated
await auth.mfa.disable({ code });
// Continuing a sign-in that returned isMfaChallenge(result) === true
const { customer } = await auth.mfa.verify({ challengeToken, code });auth.sessions — Signed-in devices
const sessions = await auth.sessions.list(); // { current: boolean } on each entry
await auth.sessions.revoke(sessionId);auth.organizations — Organizations (B2B, self-service)
const orgs = await auth.organizations.mine();
await auth.organizations.setActive(organizationId); // forces an internal refresh
await auth.organizations.clearActive();
await auth.organizations.leave(organizationId);
await auth.organizations.acceptInvitation(token);
await auth.organizations.previewInvitation(token); // no session requiredauth.sso — SSO/SAML (Home Realm Discovery)
const result = await auth.sso.start({ email, redirectUri });
// When ssoRequired is true, the method already navigates to the IdP — nothing more to do.Session state
auth.session.getState(); // { status: "signed-out" } | { status: "signed-in", customer, organizationId }
const unsubscribe = auth.session.subscribe((state) => { /* ... */ });For anything to do with administration (listing or banning users, managing Organizations from your backend), see the server SDK.