mates-auth
v1.0.1
Published
EXPERIMENTAL — not ready for production. JWT cookie auth, social login, and SSO for mates-fullstack.
Maintainers
Readme
mates-auth
Authentication middleware for mates-fullstack: JWT cookie sessions, social login (10 providers), and cross-domain SSO.
For current fullstack auth work, use mates-fs-auth in the mates repo (not yet published).
npm install mates-authDepends on mates-fullstack.
useJWT — JWT cookie auth
import { useJWT, auth } from "mates-auth";
useJWT({ secret: process.env.AUTH_JWT_SECRET! });
// After login, issue tokens:
await auth.login(ctx, { userId: user.id, email: user.email });
// After logout:
auth.logout(ctx);Verifies httpOnly access/refresh tokens on every request. Populates c.auth for all middleware, REST, and RPC functions.
Token behaviour
| Token | Lifetime | Cookie | Rotates on use? | |---|---|---|---| | Access | 15 min (configurable) | httpOnly | No (short-lived) | | Refresh | 30 days (configurable) | httpOnly | Yes — new JTI each use |
Refresh token replay detection tracks consumed JTIs in-process. Any reuse of a consumed refresh token forces logout.
Options
useJWT({
secret: "your-256-bit-secret", // required, or AUTH_JWT_SECRET env var
accessExpiresIn: "15m", // access token lifetime
refreshExpiresIn: "30d", // refresh token lifetime
path: "/", // cookie path
domain: ".example.com", // cookie domain for shared subdomains
sameSite: "lax", // cookie same-site policy
secure: true, // auto-set in production
onRefresh: async (userId, refreshPayload, ctx) => {
// return null to force re-login (e.g. user deleted/suspended)
return { userId, email: ctx.auth.email };
},
onVerify: async (auth, ctx) => {
// return false to reject (e.g. token version check)
return true;
},
});useArctic — Social login
10 built-in OAuth providers. Routes like /auth/google and /auth/google/callback are registered automatically.
import { useArctic } from "mates-auth";
useArctic({
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
onSuccess: async (profile, ctx) => {
const user = await db.users.upsert({ providerId: profile.id });
await auth.login(ctx, { userId: user.id, email: user.email });
},
},
});Built-in providers
| Key | Provider | Extra config |
|---|---|---|
| google | Google | — |
| github | GitHub | — |
| discord | Discord | — |
| microsoft | Microsoft Entra ID | tenant option |
| twitter | Twitter / X | — |
| linkedin | LinkedIn | — |
| facebook | Facebook | — |
| apple | Apple | teamId, keyId, privateKey |
| spotify | Spotify | — |
| gitlab | GitLab | baseURL for self-hosted |
Custom provider
import { arcticProvider } from "mates-auth";
useArctic({
myapp: arcticProvider({
clientId: "...",
clientSecret: "...",
onSuccess: async (profile, ctx) => { ... },
handler: {
defaultScopes: ["read"],
usesPKCE: false,
start(config, redirectUri, state) {
return new URL(`https://myapp.com/oauth?state=${state}`);
},
async callback(config, redirectUri, code) {
return { provider: "myapp", id: "123", email: "[email protected]", ... };
},
},
}),
});useSsoProvider / useSsoClient — Cross-domain SSO
For apps on different domains sharing one auth server. The auth server signs a 30-second code JWT — each app verifies it locally with zero round-trip.
Provider (auth.com)
import { useSsoProvider } from "mates-auth";
useSsoProvider({
secret: process.env.SSO_SECRET!,
login: "/login",
allowedOrigins: ["https://app1.com", "https://app2.com"],
});Registers:
| Route | What it does |
|---|---|
| GET /api/sso/code?redirect=... | If authenticated: signs 30s code JWT and redirects to app. If not: redirects to login page. |
| GET /api/sso/after-login | Trampoline — redirect here after login to complete the flow. |
Client (app1.com)
import { useSsoClient } from "mates-auth";
useSsoClient({
authUrl: "https://auth.com",
secret: process.env.SSO_SECRET!,
protected: ["/dashboard", "/settings"],
afterLogin: "/",
});Registers:
| Route / Guard | What it does |
|---|---|
| GET /auth/sso/callback?code=... | Verifies code JWT, calls auth.login(), redirects. |
| Protected route guard | Redirects unauthenticated users to auth server. |
Flow
app1.com/dashboard → no session → redirect to auth.com/api/sso/code?redirect=...
auth.com → check session → sign 30s JWT → redirect to app1.com/auth/sso/callback?code=<jwt>
app1.com → verify JWT (shared secret) → auth.login() → set own httpOnly cookies → redirect to /dashboardEach app issues its own httpOnly cookies scoped to its own domain. No cookies shared across origins.
auth.login / auth.logout
import { auth } from "mates-auth";
// Issue tokens and set httpOnly cookies:
await auth.login(ctx, {
userId: "user_123",
email: "[email protected]",
roles: ["admin"],
});
// Clear auth cookies:
auth.logout(ctx);The ctx parameter is the mates-fullstack Context (c) from any onRequest, REST handler, or SSO callback.
