You’ll have a working, gated login in minutes. No billing until you see it work.
Magic link + Stripe + a simple gate in your app.
Observe Mode preset: verify the gate first, then activate enforcement when you’re ready.
Minimum required to run the callback and place your first gate.
Only needed if you want admin tools, cookie overrides, or strict redirect allowlists.
ADMIN_API_KEY is optional; gate placement and testing work without it.
JamAuth redirects back to your app after magic link login. The callback endpoint exchanges the grant and sets a first-party session cookie. Without the callback adapter, login cannot complete.
Choose your callback adapter:
Pick the fastest win. Page/Route is preselected and ready to go.
Put this at the top of the protected page/route. It asks JamAuth “allowed?” and blocks if not.
// Put this at the top of a protected page/route
async function jamauthGate(req, res, next) {
const token = req.cookies[process.env.COOKIE_NAME];
if (!token) return res.redirect('/login');
// Ask JamAuth if access is allowed
// Replace JAMAUTH_VERIFY_URL with your verify endpoint
const verified = await fetch(process.env.JAMAUTH_VERIFY_URL, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});
if (!verified.ok) return res.status(403).send('Access denied');
req.user = await verified.json();
next();
}
app.get('/protected', jamauthGate, (req, res) => {
res.render('protected', { user: req.user });
});
// middleware.ts
import { NextResponse } from 'next/server';
export async function middleware(req) {
const token = req.cookies.get(process.env.COOKIE_NAME);
if (!token) return NextResponse.redirect(new URL('/login', req.url));
// TODO: verify token with JamAuth
return NextResponse.next();
}
// app.js
app.get('/pro', jamauthGate, (req, res) => {
res.render('pro', { user: req.user });
});
API guards use bearer tokens or cookie sessions. For now, use the page/route gate or jump to advanced examples.
Feature gates are coming next. Start with a Page/Route gate or use the widget in Advanced.
In Observe Mode, JamAuth evaluates access and returns decisions, but does not block users yet.
Quick checklist:
Configure your tenant, validate the setup, and test your magic link flow. This is the complete onboarding loop: Save → Validate → Test.
Update your tenant settings before testing. Configure app type, domain, and redirect URIs.
Check your tenant health and catch misconfigurations before testing.
Generate a test magic link to verify end-to-end authentication.
After clicking the link, check your browser DevTools → Application → Cookies to confirm the session cookie was set.
Deploy a demo, receive a real magic link, and verify the flow.
You'll receive a real magic link and log in to a live app.
Deploy to Netlify →
This deploys a starter with 3 tiny serverless functions:
• Config endpoint (public)
• Callback (PKCE, sets secure cookie)
• Session validator (server-side check)
After deploy: Set 2 environment variables (JAMAUTH_HOST + JAMAUTH_CLIENT_ID) and visit /protected.html
After testing the demo, copy the gate snippet into any HTML page on your site. The snippet fetches config from your Netlify functions and injects the JamAuth widget.
What to copy:
netlify/functions/Full instructions: Starter README
Try emails from multiple teams and environments. Inspect req.user on your callback to confirm
tenant_id, scope, and other claims are scoped correctly.
JamAuth never stores Stripe keys in your browser. Webhook secret is used server-side to verify Stripe events. This page does not persist secrets.
Paste your Stripe webhook signing secret (whsec_…). JamAuth generates a unique webhook URL for your tenant. Add it in Stripe → Developers → Webhooks.
Found in Stripe Dashboard → Developers → Webhooks → (select endpoint) → Signing secret.
Optional. Stored encrypted. Not required for v1. Used later for reconciliation.
Billing starts when enforcement is enabled.
Keep testing in Observe Mode (no billing yet)
ℹ️ If you regenerate, update the endpoint in Stripe.
Session adapters, env vars, and deeper infra paths are tucked here.
Use middleware to guard server routes, e.g.
app.use('/api/*', jamauthSession), and pass
req.user through to your handlers.
Verify session JWTs locally in each service using your JamAuth public key. Share tenant secrets via Vault/SSM, and avoid cross-service calls for auth.
This onboarding assumes you've already received your client_id and client_secret from the one-time setup link.
Your client_secret is shown only once during setup. Store it securely.
This page doesn't need your secret.
Power-user setup paths, adapters, and advanced integrations.
If you're starting with a static site, you can add JamAuth in minutes.
For the quick win demo, JamAuth hosts the callback. For production, install the tenant callback adapter above.
Do those core steps and you're done. Everything else is optional.
JamAuth supports web apps (cookie sessions) and mobile apps (PKCE + bearer tokens). Select your app type to see the right integration code.
Same login + entitlement rules. Only the transport changes (cookie vs bearer).
Callback sets an HttpOnly cookie session. Protect pages by requiring the cookie.
Go to cookie guard snippet →
Use token mode (bearer tokens). Clients send Authorization: Bearer <token>. Protect APIs by verifying the token server-to-server.
Recommended when you want a clean auth.yourapp.com domain.
You can skip this for now and come back later.
This step doesn't affect the login flow; it only improves DX and branding.
auth → cname.jamauth.com then click "Check Verification".
CNAME is best for most setups and required if we'll host UI/assets on your subdomain. Use TXT only if your DNS flattens CNAMEs or a proxy hides it.
Standard pattern for full-stack apps: protect server routes and hydrate your templates with a trusted user object.
For mobile apps and APIs: extract the bearer token from Authorization header,
verify it server-to-server with JamAuth.
JamAuth separates authentication (who the user is) from validation (whether they're allowed right now). Stripe is the recommended default (configured in Step 5). Use these alternatives for custom access control logic.
Control how long sessions last and whether they can be refreshed without forcing users to click a new magic link.
JamAuth exposes these as environment variables (or config) that your backend can use when issuing and verifying session tokens, e.g.:
Use webhooks to invalidate sessions or adjust access when subscriptions or roles change.
For larger teams, wire JamAuth into your existing security and observability stack: subscriptions, scopes, audit logs, and fleets of services.
Use JamAuth as a shared auth layer across many tenants, apps, or teams.
client_id/client_secret pair.tenant_id in req.user.You now have login + protected access with Stripe as the default signal.
This flow is one JamAuth pattern. You can swap Stripe for any signal, or enforce access server-to-server, without re-authing users. Explore infra-first options →