Set up JamAuth for Netlify
-
Register your site
In progressEnter the exact origin where your deployed site is running, then choose the first login and protected paths.
Site registered
JamAuth saved your deployed site and callback URL.
- Site origin
- Callback URL
-
Choose who gets access
LockedSelect the rule that grants access
Add the email address you will use for the first login test. Only addresses on this list can complete login while Approved Emails is active.
Owning the JamAuth project does not automatically grant access to the protected site.
Loading access settings…
No approved emails yet.
Add the email you will use to test login. -
Add login
LockedPut the login form on your site
Create this login page in your Netlify publish directory. It loads the JamAuth widget and sends the user back to the protected path after login completes.
The magic link can be requested from this page, but the login cannot complete end to end until you add the callback in Step 4.
- Login URL
- File path inside your Netlify publish directory
- Public Client ID
- Default protected destination
- Canonical widget URL
This path is nested. Create the folder shown above inside your Netlify publish directory before adding the file.
JamAuth cannot see your repository. Mark this complete after you add the generated file. Step 7 will verify the deployed login flow.
-
Add callback
LockedExchange the magic link for a session
Add these two Netlify functions. The callback completes the magic-link exchange and sets the first-party session cookie. The session function lets the login widget check that cookie.
Loading callback and session source…
- Registered callback URL
- Callback target path
- Session endpoint URL
- Session target path
- Canonical JamAuth host
- Public Client ID
- Required later (Step 6)
Create these files in your Netlify functions directory. If your
netlify.tomlconfigures a custom functions directory, place these files there using the shown filenames.- Purpose
- Completes the magic-link exchange and sets the first-party session cookie.
- Target path
- Purpose
- Reads the session cookie and calls JamAuth
refresh-sessionfor the widget. - Target path
JamAuth cannot see your repository. Mark this complete after you add both files. Step 6 will configure and deploy them; Step 7 will verify the live flow.
-
Protect your page
LockedRequire that session before serving content
Add this server-side function. It checks the JamAuth session before returning any protected content.
This function returns a protected starter page. Step 6 will route your configured protected URL to it. After verification, replace the response template with your content or adapt the verified-session block to your server route.
Loading gate function source…
- Configured protected URL
- Configured login URL
- Gate function endpoint URL
- Gate target path
- Public Client ID
- Canonical JamAuth host
- Required later (Step 6) environment values
- Purpose
- Checks the JamAuth session with refresh-session before returning protected content.
- Target path
Create this file in your Netlify functions directory. If
netlify.tomluses a custom functions directory, placeprotected-page.jsthere.JamAuth cannot see your repository. Mark this complete after you add the function. Step 6 will configure the route, environment values, and deployment; Step 7 will verify that the protected URL fails closed.
-
Configure and deploy
LockedAdd environment values and route mappings
Set the production environment values, merge the protected-route rewrite, and deploy to your registered origin. JamAuth cannot inspect your repository, change your Netlify settings, or deploy for you.
- Registered production origin
Set these in Netlify’s environment-variable settings (Project configuration → Environment variables) or with the CLI below, scoped to Functions and the Production context. Do not commit this block merely because it uses .env formatting — none of these five values is a client secret, but they still should not be committed to your repository.
Testing on plain HTTP localhost?
Production stays on
__Host-jam_session— the callback sets that cookie withSecure, and plain HTTP localhost cannot use it as a normal browser session. For local HTTP function testing only, you may use a non-prefixed local value instead, supplied consistently to the callback, session, and gate functions. This does not change your registered production origin, and a local smoke test is not Step 7 verification.- Netlify CLI
- Run from the customer site repository.
Review each command before running it. These set the five values above, scoped to Functions, in the Production context, and list them back for verification. No command links or relinks a site, imports with a destructive replace flag, or executes automatically.
Environment-variable scopes unavailable on your plan?
Per-scope environment-variable controls (like
--scope functionsabove) require a Netlify plan with that feature. JamAuth cannot detect your plan from this page. If your Netlify CLI or dashboard does not offer scope controls, use this fallback instead — it keeps the Production context explicit but omits--scope, so each value becomes available to every scope (builds, functions, runtime, post-processing) rather than only Functions.- Protected-route rewrite
- Maps your exact configured protected path to the gate function.
Use the routing file your site already maintains. Do not replace an existing file; merge the generated rule into it.
A matching
_redirectsrule always runs beforenetlify.toml. Netlify processes every rule in the published_redirectsfile first, then every rule innetlify.toml, and stops at the first match — anetlify.tomlrule can never outrank an existing_redirectscatch-all, no matter where it is placed insidenetlify.toml. If your publish directory already contains a_redirectsfile with a matching catch-all such as/* /index.html 200, either (A) select the_redirectsstrategy below and place the JamAuth rule above that catch-all, or (B) remove or narrow the conflicting_redirectsrule. Merely placing the JamAuth rule early insidenetlify.tomlis not sufficient in that case.Within a single file, put the JamAuth protected-route rule before any broader catch-all rule such as an SPA
/*rewrite — Netlify still stops at the first match within each file.This rule protects the configured route. It does not automatically protect every path beneath it.
JamAuth cannot inspect your repository — check that this rule appears before any broader rule of your own that would also match the protected path, in both files if you have both.
- Deployment checklist
-
- Login page added inside the publish directory.
- Callback and session functions added inside the functions directory.
- Protected-page function added inside the functions directory.
- Five production environment values set.
- Protected-route rewrite merged before conflicting broader rules.
- Production deploy completed at the registered origin.
Connected Git deployment. Commit the customer-site files and configuration to its normal production branch, then push so Netlify builds and deploys that branch.
Netlify CLI deployment. Run
netlify deploy --prodfrom the customer site repository. It uses the site’s existing Netlify link/configuration — review the command before running it. This is a production deployment to the registered site, not a draft deploy.After changing environment values, create a new production deploy so the functions receive the new configuration.
A Netlify draft deploy or deploy-preview URL is not your registered production origin and is not a valid substitute for it — JamAuth origin/callback validation may reject it.
JamAuth cannot inspect your repository, Netlify settings, or deploy from this step. Mark this complete after you set the five production environment values, merge the protected route, and deploy to the registered origin. Step 7 will verify the live flow.
-
Verify
LockedTest allowed, denied, and signed-out access
JamAuth cannot fetch your site from its own servers — that would turn a customer-controlled origin into a new server-side fetch target. Instead, run a small self-contained script on your own computer to check the live, deployed HTTP contract, then confirm the real approved-email flow yourself in a private browser window.
- Registered production origin
- Login URL
- Protected verification URL
- Callback URL
- Session endpoint URL
- Public Client ID
- Current verification challenge
No client secret is involved in any Step 7 check.
Starting a fresh verification generates a new challenge and clears any imported verifier result, final-URL evidence, and attestations for this attempt.
- 1. Run live deployment checks
Run it from your own computer. It performs public, read-only HTTP checks only. It does not send an email or log in. Review it before running it. Paste only its JSON result below — never paste a magic-link URL or email token into JamAuth setup.
node jamauth-live-verify.mjsnode jamauth-live-verify.mjs > jamauth-verification-result.json- 2. Test an approved email
Passive HTTP checks cannot prove the real email/callback/browser-cookie path — only you can access your mailbox and complete a first-party browser flow.
- Open the verification URL below in a fresh private/incognito window.
- Confirm protected content is not shown before login.
- Confirm the browser redirects to the configured login page.
- Use an email that is currently approved in Step 2.
- Open the magic link in the SAME private browser context where it was requested. Using webmail in that private window, or copying the link into that same private window, is acceptable.
- Do not open the link in a different browser or profile — the PKCE verifier cookie belongs to the browser context that requested the link.
- Confirm the callback returns to the protected route.
- Confirm protected content is visible.
- Confirm the final address still contains the exact verification challenge.
Do not paste the magic-link URL, a token, state, a grant code, a PKCE verifier, a cookie, or an email body anywhere in JamAuth setup.
Opening this link here opens a normal browser tab, not a private window — you must open a fresh private/incognito window yourself.
This is only for the final post-login protected-page address, never the magic-link URL.
- 3. Optional: test a denied email
Optional but recommended. Because Approved Emails is your current access mode:
- Use a separate fresh private browser context.
- Use an inbox you control that is NOT in the Approved Emails list.
- The login widget still shows the generic "check your email" response.
- After clicking the link, JamAuth denies access before the tenant callback.
- The browser sees the JamAuth-hosted access-denied response.
- No tenant session cookie is issued.
- Protected content is not reached.
Do not paste the denied magic-link URL or the complete-login URL anywhere in JamAuth setup.
- 4. Finish verification
Live deployment checks, the approved-email evidence, and all three required attestations are needed before you can finish.