7.1 KiB
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:
{
"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:
{
"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:
{
"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/setupPOST /api/v1/auth/pin/verifyPOST /api/v1/auth/pin/changePOST /api/v1/auth/pin/forgotPOST /api/v1/auth/pin/reset
For POST /api/v1/auth/pin/reset:
- If
method=password, send the resettokenfrom the email link pluspassword. - If
method=microsoft, send onlyreauth_tokenfrom 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:
2) TOTP Setup (Enable 2FA)
2.1 Setup
POST /api/v1/auth/totp/setup
Request:
{
"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:
{
"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:
{
"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:
{
"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:
{
"data": {
"type": "auth_sso_unlink",
"attributes": {
"provider": "microsoft"
}
}
}
5) WebAuthn (Passkey)
5.1 Register Credential (Authenticated User)
POST /api/v1/auth/webauthn/register/begin(auth required)- Browser executes
navigator.credentials.create(...)usingpublic_keyfrom response POST /api/v1/auth/webauthn/register/finishwithchallenge_token+credential
5.2 Login with WebAuthn
POST /api/v1/auth/webauthn/login/beginwith email- Browser executes
navigator.credentials.get(...)usingpublic_keyfrom response POST /api/v1/auth/webauthn/login/finishwithchallenge_token+credential- 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-logoutand 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; Secureso front-channel iframe logout can receive cookies. - Access token TTL is controlled by
AUTH_JWT_ACCESS_TTL(default10m) - Refresh token TTL is controlled by
AUTH_JWT_REFRESH_TTL(default1h) - 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_IDandAUTH_WEBAUTHN_RP_ORIGINS.