# V2 Authentication Guide

This document is the source of truth for V2 authentication.

Base URL:

```env
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:

```http
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:

```http
POST /auth/client/sign-in
Content-Type: application/json
```

```json
{
  "email": "legendary.owner+v2@krowd.com",
  "password": "Demo2026!"
}
```

Response:

```json
{
  "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:

```http
POST /auth/client/sign-up
Content-Type: application/json
```

```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:

```http
POST /auth/staff/phone/start
Content-Type: application/json
```

```json
{
  "phoneNumber": "+15551234567"
}
```

Possible response A:

```json
{
  "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:

```json
{
  "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:

```http
POST /auth/staff/phone/verify
Content-Type: application/json
```

```json
{
  "mode": "sign-in",
  "idToken": "firebase-id-token-from-device"
}
```

Response:

```json
{
  "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:

```json
{
  "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:

```http
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:

```http
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:

```json
{
  "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.
