Browser apps
A customer signs in on the booking page and sees their own visits. Cookies, the security handshake, and the 419 you'll definitely hit.
Dana opens Brightside's booking page and signs in. From then on she sees her visits, her invoices, and nobody else's.
That runs on session cookies, not a key. Two reasons it's the right choice:
- The session is HttpOnly, so no token sits in JavaScript for a bug or a bad browser extension to steal.
- Every action is Dana's. Sarah's audit trail says "Dana rebooked Thursday", not "the server did something".
Three things have to be true: your web address is on the allowed list, every request is sent with credentials, and unsafe requests carry a security token.
The flow
Get a security token
Once, before the first sign-in — and again after a 419:
await fetch('https://api.hyphabase.com/sanctum/csrf-cookie', {
credentials: 'include',
});This sets an XSRF-TOKEN cookie. It's readable by JavaScript on purpose: you copy
its value into a request header, and doing so is what proves the request came from your
page rather than someone else's site.
Sign in
const response = await fetch(
'https://api.hyphabase.com/api/auth/session/login',
{
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-XSRF-TOKEN': readCookie('XSRF-TOKEN'),
},
body: JSON.stringify({ email, password }),
},
);
const { user, permissions } = await response.json();You get the person and what they're allowed to do — enough to hide buttons they can't use. The session itself is in a cookie you never touch.
Make requests
credentials: 'include', the security header on anything that writes, and the site id:
await fetch('https://api.hyphabase.com/api/modules/clients', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-XSRF-TOKEN': readCookie('XSRF-TOKEN'),
'X-Location-Id': locationId,
},
body: JSON.stringify({
first_name: 'Dana',
last_name: 'Whitfield',
email: 'dana@example.com',
phone: '+15035550143',
}),
});Sign out
await fetch('https://api.hyphabase.com/api/auth/session/logout', {
method: 'POST',
credentials: 'include',
headers: { 'X-XSRF-TOKEN': readCookie('XSRF-TOKEN') },
});The session is destroyed server-side, not just forgotten by the browser. Returns 204.
Reading the token cookie
The value is URL-encoded, so decode it:
function readCookie(name) {
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
return match ? decodeURIComponent(match[1]) : null;
}Most HTTP clients do this for you. Axios reads XSRF-TOKEN and sets X-XSRF-TOKEN
automatically once withCredentials: true is set, which is worth using rather than
hand-rolling.
The endpoints
| For | Endpoint |
|---|---|
| Sign in | POST /api/auth/session/login |
| Who's signed in | GET /api/auth/session/me |
| Sign out | POST /api/auth/session/logout |
| Update their details | PUT /api/auth/session/profile |
| Change password | PUT /api/auth/session/password |
/me is cached and sends an ETag, so asking repeatedly is cheap — a conditional
request gets a 304. The cache key includes the person's last-updated time, so editing
a profile invalidates it immediately; you don't have to clear anything.
When it doesn't work
Never reach for a secret key because sessions are fiddly
If you find yourself wanting sk_… in front-end code, put a thin endpoint on your own
server and keep the key behind it. A publishable key covers read-only cases without
the risk.