# Auth Flow

This document describes the recommended authentication flows in Wucher. All API responses follow **JSON:API**.

## 1) Email/Password Registration

### 1.1 Register
`POST /api/v1/auth/register`

Notes:
- The user role is assigned based on `AUTH_DEFAULT_ROLE`.
- For production, you can disable this endpoint by setting `AUTH_DISABLE_REGISTER=true`.

Request:
```json
{
  "data": {
    "type": "auth_register",
    "attributes": {
      "email": "user@example.com",
      "password": "StrongP@ssw0rd",
      "first_name": "John",
      "last_name": "Doe",
      "mobile_phone": ""
    }
  }
}
```

Response: `201 Created` with user resource.

### 1.2 Verify Email
User receives a verification link in email:
`GET /api/v1/auth/verify-email?token=...`

Response: `200 OK` with `{ verified: true }`.

### 1.3 Login
`POST /api/v1/auth/login`

If TOTP **is not enabled**, response is `200 OK` and cookies are set:
- `wucher_at` (access)
- `wucher_rt` (refresh)

If TOTP **is enabled**, response is `202 Accepted` with `challenge_token`.

### 1.4 Forgot Password
`POST /api/v1/auth/forgot-password`

Request:
```json
{
  "data": {
    "type": "auth_forgot_password",
    "attributes": {
      "email": "user@example.com"
    }
  }
}
```

Response is always `200 OK` with a generic message:
`If the account exists, a password reset link has been sent.`

### 1.5 Reset Password
`POST /api/v1/auth/reset-password`

Request:
```json
{
  "data": {
    "type": "auth_reset_password",
    "attributes": {
      "token": "<token>",
      "new_password": "StrongPassphrase123!",
      "confirm_password": "StrongPassphrase123!"
    }
  }
}
```

Success response: `200 OK` with message instructing the user to log in again.

Invalid/expired/used token response: `400 Bad Request` with:
`This reset link is invalid or expired.`

### 1.6 Security PIN for Sensitive Actions

Security PIN endpoints:
- `POST /api/v1/auth/pin/setup`
- `POST /api/v1/auth/pin/verify`
- `POST /api/v1/auth/pin/change`
- `POST /api/v1/auth/pin/forgot`
- `POST /api/v1/auth/pin/reset`

For `POST /api/v1/auth/pin/reset`:
- If `method=password`, send the reset `token` from the email link plus `password`.
- If `method=microsoft`, send only `reauth_token` from Microsoft re-auth.

Sensitive actions require header:
- `X-PIN-Verification-Token: <one-time-token>`

`auth/pin/verify` menggunakan `action` berupa UUID opaque (transaction ID), bukan permission key seperti `user.delete`.

Detailed flow + full payload examples:
- See [docs/security-pin.md](security-pin.md)

## 2) TOTP Setup (Enable 2FA)

### 2.1 Setup
`POST /api/v1/auth/totp/setup`

Request:
```json
{
  "data": {
    "type": "auth_totp_setup",
    "attributes": {
      "user_id": "<uuid>",
      "email": "user@example.com"
    }
  }
}
```

Response: `200 OK` with `secret` and `otpauth_url`.
> TOTP is **not** enabled yet (pending).

### 2.2 Confirm
`POST /api/v1/auth/totp/confirm`

Request:
```json
{
  "data": {
    "type": "auth_totp_confirm",
    "attributes": {
      "user_id": "<uuid>",
      "code": "123456"
    }
  }
}
```

Response: `200 OK` and TOTP becomes **enabled**.

## 3) TOTP Login (2FA)

### 3.1 Login
`POST /api/v1/auth/login`

Response: `202 Accepted` with `challenge_token`.

### 3.2 Verify
`POST /api/v1/auth/totp/verify`

Request:
```json
{
  "data": {
    "type": "auth_totp_verify",
    "attributes": {
      "challenge_token": "<token>",
      "code": "123456"
    }
  }
}
```

Response: `200 OK` and cookies are set.

## 4) Microsoft Entra SSO

### 4.1 Start Login
`GET /api/v1/auth/microsoft/login`

Response: `200 OK` with JSON:API body (`type: auth_microsoft_url`) containing `url` and `redirect_url`

### 4.2 Callback
Microsoft redirects to:
`GET /api/v1/auth/microsoft/callback?code=...&state=...`

Backend behavior:
- If SSO mapping (`provider + provider_subject`) is linked to an existing user, login succeeds.
- If mapping is missing, login is rejected (`401 SSO not linked`).
- If mapping exists but user record is missing, login is rejected (`404 user not found`).
- No auto-register on SSO callback.

On success, backend sets cookies and may redirect to:
`AUTH_SSO_SUCCESS_REDIRECT`

### 4.3 Link SSO (authenticated user)
`POST /api/v1/auth/sso/link`

Body:
```json
{
  "data": {
    "type": "auth_sso_link",
    "attributes": {
      "provider": "microsoft",
      "code": "<microsoft_authorization_code>"
    }
  }
}
```

Rules:
- 1 user only 1 SSO mapping.
- Same SSO account cannot be linked to another user.

### 4.4 Unlink SSO (authenticated user)
`DELETE /api/v1/auth/sso/unlink`

Body:
```json
{
  "data": {
    "type": "auth_sso_unlink",
    "attributes": {
      "provider": "microsoft"
    }
  }
}
```

## 5) WebAuthn (Passkey)

### 5.1 Register Credential (Authenticated User)
1. `POST /api/v1/auth/webauthn/register/begin` (auth required)
2. Browser executes `navigator.credentials.create(...)` using `public_key` from response
3. `POST /api/v1/auth/webauthn/register/finish` with `challenge_token` + `credential`

### 5.2 Login with WebAuthn
1. `POST /api/v1/auth/webauthn/login/begin` with email
2. Browser executes `navigator.credentials.get(...)` using `public_key` from response
3. `POST /api/v1/auth/webauthn/login/finish` with `challenge_token` + `credential`
4. On success, backend sets auth cookies.

## 6) Refresh & Logout

### Refresh
`POST /api/v1/auth/refresh`

Uses refresh cookie to issue new tokens.

### Logout
`POST /api/v1/auth/logout`

Revokes refresh token and clears cookies.

Frontend note:
- Use `credentials: 'include'` on requests that depend on auth cookies.
- Keep app logout local-only; do not redirect to Microsoft logout from this endpoint.
- Microsoft front-channel logout is handled separately at `/api/v1/auth/microsoft/front-channel-logout` and is designed to work in an iframe.

## Notes
- Access token is stored in HttpOnly cookie: `AUTH_JWT_ACCESS_COOKIE`
- Refresh token is stored in HttpOnly cookie: `AUTH_JWT_REFRESH_COOKIE`
- Cookies are configured by `AUTH_JWT_COOKIE_*` envs
- Microsoft SSO sessions use `SameSite=None; Secure` so front-channel iframe logout can receive cookies.
- Access token TTL is controlled by `AUTH_JWT_ACCESS_TTL` (default `10m`)
- Refresh token TTL is controlled by `AUTH_JWT_REFRESH_TTL` (default `1h`)
- TOTP challenge tokens are stored server-side in MySQL-backed runtime storage for `AUTH_TOTP_CHALLENGE_TTL`
- Password-reset tokens are stored in MySQL as SHA-256 hashes (raw token is never stored)
- Password-reset tokens are single-use and expire by `AUTH_PASSWORD_RESET_TTL` (default 20m)
- Forgot-password endpoint has per-IP and per-email throttling
- Login (including SSO callback and post-TOTP token issue) bumps per-user session version, so a new login invalidates previous active sessions
- Successful password reset revokes active sessions by bumping per-user session version
- PIN can be blocked after max failed attempts; use `POST /api/v1/auth/pin/request-reset` (auth required) to request reset email for blocked accounts
- WebAuthn challenge token is single-use, stored server-side, and expires by `AUTH_WEBAUTHN_CHALLENGE_TTL`.
- WebAuthn credential verification enforces RP ID + RP origin checks from `AUTH_WEBAUTHN_RP_ID` and `AUTH_WEBAUTHN_RP_ORIGINS`.
