Files
Krow-workspace/docs/BACKEND/API_GUIDES/V2/authentication.md

7.2 KiB

V2 Authentication Guide

This document is the source of truth for V2 authentication.

Base URL:

API_V2_BASE_URL=https://krow-api-v2-e3g6witsvq-uc.a.run.app

1) What is implemented

Client app

Client authentication is implemented through backend endpoints:

  • POST /auth/client/sign-in
  • POST /auth/client/sign-up
  • POST /auth/client/sign-out
  • GET /auth/session

The backend signs the user in with Firebase Identity Toolkit, validates the user against the V2 database, and returns the full auth envelope.

Staff app

Staff authentication is implemented, but it is a hybrid flow.

Routes:

  • POST /auth/staff/phone/start
  • POST /auth/staff/phone/verify
  • POST /auth/staff/sign-out
  • GET /auth/session

Important:

  • the default mobile path is not a fully backend-managed OTP flow
  • the usual mobile path uses the Firebase Auth SDK on-device for phone verification
  • after the device gets a Firebase idToken, frontend sends that token to POST /auth/staff/phone/verify

So if someone expects POST /auth/staff/phone/start to always send the SMS and always return sessionInfo, that expectation is wrong for the current implementation

2) Auth refresh

There is currently no backend /auth/refresh endpoint.

That is intentional for now.

Current refresh model:

  • frontend keeps Firebase Auth local session state
  • frontend lets the Firebase SDK refresh the ID token
  • frontend sends the latest Firebase ID token in:
Authorization: Bearer <firebase-id-token>

Use:

  • authStateChanges() / idTokenChanges() listeners
  • currentUser.getIdToken()
  • currentUser.getIdToken(true) only when a forced refresh is actually needed

GET /auth/session is not a refresh endpoint.

It is a context endpoint used to:

  • hydrate role/tenant/business/staff context
  • validate that the signed-in Firebase user is allowed in this app

3) Client auth flow

Client sign-in

Request:

POST /auth/client/sign-in
Content-Type: application/json
{
  "email": "legendary.owner+v2@krowd.com",
  "password": "Demo2026!"
}

Response:

{
  "sessionToken": "firebase-id-token",
  "refreshToken": "firebase-refresh-token",
  "expiresInSeconds": 3600,
  "user": {
    "id": "user-uuid",
    "email": "legendary.owner+v2@krowd.com",
    "displayName": "Legendary Owner",
    "phone": null
  },
  "tenant": {
    "tenantId": "tenant-uuid",
    "tenantName": "Legendary Event Staffing and Entertainment"
  },
  "business": {
    "businessId": "business-uuid",
    "businessName": "Google Mountain View Cafes"
  },
  "requestId": "uuid"
}

Frontend behavior:

  1. Call POST /auth/client/sign-in
  2. If success, sign in locally with Firebase Auth SDK using the same email/password
  3. Use Firebase SDK token refresh for later API calls
  4. Use GET /auth/session when role/session hydration is needed on app boot

Client sign-up

Request:

POST /auth/client/sign-up
Content-Type: application/json
{
  "companyName": "Legendary Event Staffing and Entertainment",
  "email": "legendary.owner+v2@krowd.com",
  "password": "Demo2026!"
}

What it does:

  • creates Firebase Auth account
  • creates V2 user
  • creates tenant
  • creates business
  • creates tenant membership
  • creates business membership

Frontend behavior after success:

  1. call POST /auth/client/sign-up
  2. sign in locally with Firebase Auth SDK using the same email/password
  3. use Firebase SDK for later token refresh

4) Staff auth flow

Step 1: start phone auth

Request:

POST /auth/staff/phone/start
Content-Type: application/json
{
  "phoneNumber": "+15551234567"
}

Possible response A:

{
  "mode": "CLIENT_FIREBASE_SDK",
  "provider": "firebase-phone-auth",
  "phoneNumber": "+15551234567",
  "nextStep": "Complete phone verification in the mobile client, then call /auth/staff/phone/verify with the Firebase idToken.",
  "requestId": "uuid"
}

This is the normal mobile path when frontend does not send recaptcha or integrity tokens.

Current dev demo worker:

  • phone number: +15557654321
  • email: ana.barista+v2@krowd.com

Those two now resolve to the same Firebase user and the same seeded staff profile in v2.

Possible response B:

{
  "mode": "IDENTITY_TOOLKIT_SMS",
  "phoneNumber": "+15551234567",
  "sessionInfo": "firebase-session-info",
  "requestId": "uuid"
}

This is the server-managed SMS path.

Step 2A: normal mobile path (CLIENT_FIREBASE_SDK)

Frontend must do this on-device:

  1. call FirebaseAuth.verifyPhoneNumber(...)
  2. collect the verificationId
  3. collect the OTP code from the user
  4. create a Firebase phone credential
  5. call signInWithCredential(...)
  6. get Firebase idToken
  7. call POST /auth/staff/phone/verify with that idToken

Request:

POST /auth/staff/phone/verify
Content-Type: application/json
{
  "mode": "sign-in",
  "idToken": "firebase-id-token-from-device"
}

Response:

{
  "sessionToken": "firebase-id-token-from-device",
  "refreshToken": null,
  "expiresInSeconds": 3600,
  "user": {
    "id": "user-uuid",
    "phone": "+15551234567"
  },
  "staff": {
    "staffId": "staff-uuid"
  },
  "requiresProfileSetup": false,
  "requestId": "uuid"
}

Important:

  • refreshToken is expected to be null in this path
  • refresh remains owned by Firebase Auth SDK on the device

Step 2B: server SMS path (IDENTITY_TOOLKIT_SMS)

If start returned sessionInfo, frontend can call:

{
  "mode": "sign-in",
  "sessionInfo": "firebase-session-info",
  "code": "123456"
}

The backend exchanges sessionInfo + code with Identity Toolkit and returns the hydrated auth envelope.

5) Sign-out

Routes:

  • POST /auth/sign-out
  • POST /auth/client/sign-out
  • POST /auth/staff/sign-out

All sign-out routes require:

Authorization: Bearer <firebase-id-token>

What sign-out does:

  • revokes backend-side Firebase sessions for that user
  • frontend should still clear local Firebase Auth state with FirebaseAuth.instance.signOut()

6) Session endpoint

Route:

  • GET /auth/session

Headers:

Authorization: Bearer <firebase-id-token>

Use it for:

  • app startup hydration
  • role validation
  • deciding whether this app should allow the current signed-in user

Do not use it as:

  • a refresh endpoint
  • a login endpoint

7) Error contract

All auth routes use the standard V2 error envelope:

{
  "code": "STRING_CODE",
  "message": "Human readable message",
  "details": {},
  "requestId": "uuid"
}

Common auth failures:

  • UNAUTHENTICATED
  • FORBIDDEN
  • VALIDATION_ERROR
  • AUTH_PROVIDER_ERROR

8) Troubleshooting

Staff sign-in does not work, but endpoints are reachable

The most likely causes are:

  1. frontend expected POST /auth/staff/phone/start to always return sessionInfo
  2. frontend did not complete Firebase phone verification on-device
  3. frontend called POST /auth/staff/phone/verify without a valid Firebase idToken
  4. frontend phone-auth setup in Firebase mobile config is incomplete

POST /auth/staff/phone/start returns CLIENT_FIREBASE_SDK

That is expected for the normal mobile flow when no recaptcha or integrity tokens are sent.

There is no /auth/refresh

That is also expected right now.

Refresh is handled by Firebase Auth SDK on the client.