HyphaBase

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.

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

/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.

Next

On this page