Skip to content

Authentication API

MethodPathAuthPurpose
POST/auth/registerPublicCreate an account (role user) and return a token.
POST/auth/loginPublicExchange email and password for a token.
POST/auth/googlePublicExchange a Google ID token for a session. 503 when Google sign-in is not configured.
GET/auth/meTokenThe current user.
POST/auth/verify-emailPublicConfirm an emailed verification link.
POST/auth/request-verificationTokenResend the verification email. Always 200.
POST/auth/forgot-passwordPublicStart a password reset. Always 200, so it cannot probe which emails exist.
POST/auth/reset-passwordPublicComplete a reset with the emailed token and a new password.
Terminal window
curl -s https://<your-host>/auth/login \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","password":"a-long-random-password"}'
{
"token": "<jwt>",
"user": { "id": "local", "email": "you@example.com", "role": "admin", "emailVerified": true, "createdAt": "2026-01-01T00:00:00.000Z" }
}

A wrong password returns 401 with a deliberately generic message. Password hashes are never returned by any route.

Terminal window
curl -s https://<your-host>/auth/register \
-H 'content-type: application/json' \
-d '{"email":"new@example.com","password":"at-least-eight-chars"}'

Returns 201 with the same shape as login. New self-registered accounts get the user role. Passwords must be at least 8 characters; a duplicate email returns 409.

Terminal window
curl -s https://<your-host>/auth/me -H "authorization: Bearer $TOKEN"
  • POST /auth/verify-email with { "token": "<token-from-the-link>" } — public, because the link is clicked while signed out. Verification tokens last 24 hours.
  • POST /auth/request-verification — authenticated; resends the email. Always returns 200, and does nothing if the address is already verified.

With no SMTP configured, the link is logged to the server console instead of sent, which is enough for local development.

  • POST /auth/forgot-password with { "email": "..." } — public, and always returns 200 whether or not the address exists, so it cannot be used to discover accounts.
  • POST /auth/reset-password with { "token": "...", "password": "..." } — completes the reset. Reset tokens last one hour and are single-use: changing the password invalidates any outstanding link.
Terminal window
curl -s https://<your-host>/auth/google \
-H 'content-type: application/json' \
-d '{"idToken":"<google-id-token>"}'

Enabled only when GOOGLE_OAUTH_CLIENT_IDS lists the client id that minted the token; otherwise the route returns 503. The ID token is verified locally against Google’s public keys, and its audience must be one of the configured client ids.

  • Tokens are signed with AUTH_JWT_SECRET (HS256) and carry the user id and role.
  • The algorithm is pinned, so an alg: none token is rejected.
  • There is no server-side session and no refresh flow: when a token expires, sign in again.
  • Store tokens the way you would any credential — never in a URL or a log.