init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

272
docs/auth-flow.md Normal file
View File

@@ -0,0 +1,272 @@
# 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`.