273 lines
7.1 KiB
Markdown
273 lines
7.1 KiB
Markdown
# 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`.
|