Architecture

Authentication

Google OAuth and email sign-in, plus session management.

routess supports two sign-in methods, Google OAuth (one-tap) and email + password, both ending in the same JWT session cookie on the API.

Google flow

  1. The web app loads the Google Identity Services SDK and renders the sign-in button.
  2. On success, Google returns an ID token to the browser.
  3. Web posts the ID token to POST /api/v1/auth/google.
  4. The API verifies the token's signature and audience against Google's JWKS.
  5. The API upserts the user (by Google sub), issues its own JWT, and sets it as an HttpOnly, Secure, SameSite=Lax cookie.
  6. Subsequent API calls include the cookie automatically.

Email flow

  1. POST /api/v1/auth/signup-email registers the account (passwords are at least 12 characters, hashed at rest) and sends a verification link, valid for 24 hours.
  2. POST /api/v1/auth/verify-email consumes the link's token and signs the user in.
  3. POST /api/v1/auth/login-email signs in on later visits and issues the same session cookie as the Google flow.
  4. POST /api/v1/auth/request-password-reset emails a reset link valid for 30 minutes; POST /api/v1/auth/reset-password consumes it.

Email delivery goes through Resend when RESEND_API_KEY is set; otherwise verification and reset URLs are logged to stdout (the dev/self-host console transport).

A Google-created account can add a password later (POST /api/v1/users/me/password), so both methods work for the same user.

Why a server-issued JWT?

  • Lets us add app-specific claims (user role, default unit)
  • Decouples session lifetime from Google's token
  • We don't need to call Google on every request

Sign-out

POST /api/v1/auth/logout clears the cookie. The web app also clears Zustand auth state.

Session refresh

JWTs are short-lived (15 minutes). The API refreshes silently on each authenticated request, returning a fresh cookie. If the user is gone for too long, the cookie expires and they re-sign-in via Google's silent re-auth (no user interaction in most cases).

On this page