5.2 KiB
Authentication API
Prefix:
/api/v1/userand/oauth
Permission: All endpoints in this module are PUBLIC (no JWT required) exceptuserinfoandlogout
POST /api/v1/user/send-code
Send OTP code to mobile number.
Permission: PUBLIC
Request Body
{
"mobile": "09123456789"
}
| Field | Type | Required | Validation |
|---|---|---|---|
mobile |
string | ✅ | Format: 09XXXXXXXXX (11 digits) |
Response 200
{
"success": true,
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"message": "کد تأیید ارسال شد"
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_VALIDATION_001 |
422 | Invalid mobile format |
ERR_AUTH_004 |
429 | OTP rate limit exceeded |
POST /api/v1/user/verify-code
Verify OTP code. Returns whether this is a new or existing user.
Permission: PUBLIC
Request Body
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"code": "123456"
}
| Field | Type | Required | Description |
|---|---|---|---|
uuid |
string | ✅ | UUID returned from send-code |
code |
string | ✅ | 6-digit OTP |
Response 200
{
"success": true,
"data": {
"message": "کد تأیید شد",
"is_new_user": false,
"uuid": "550e8400-e29b-41d4-a716-446655440000"
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_002 |
401 | Invalid OTP code |
ERR_AUTH_003 |
401 | OTP expired |
ERR_VALIDATION_002 |
422 | Missing required field |
POST /api/v1/user/register
Complete registration for new users (called only when is_new_user: true).
Permission: PUBLIC
Request Body
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"real_name": "علی احمدی"
}
| Field | Type | Required | Description |
|---|---|---|---|
uuid |
string | ✅ | Verified UUID from verify-code |
real_name |
string | ❌ | User's full name |
Response 201
{
"success": true,
"data": {
"message": "ثبتنام با موفقیت انجام شد",
"uuid": "550e8400-e29b-41d4-a716-446655440000"
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_VALIDATION_002 |
422 | Missing uuid |
ERR_CONFLICT_001 |
409 | User already registered |
POST /api/v1/user/login
Login with mobile number and password (for users who set a password).
Permission: PUBLIC
Request Body
{
"mobile_number": "09123456789",
"password": "mypassword"
}
| Field | Type | Required |
|---|---|---|
mobile_number |
string | ✅ |
password |
string | ✅ |
Response 200
{
"success": true,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def50200..."
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_005 |
401 | Wrong credentials |
ERR_AUTH_006 |
403 | Account suspended |
ERR_AUTH_004 |
429 | Too many attempts |
POST /oauth/token
Exchange verified UUID for JWT access token.
Permission: PUBLIC
Request Body
{
"grant_type": "mobile",
"uuid": "550e8400-e29b-41d4-a716-446655440000"
}
| Field | Type | Required | Description |
|---|---|---|---|
grant_type |
string | ✅ | Must be "mobile" |
uuid |
string | ✅ | UUID from verified OTP flow |
Response 200
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def50200..."
}
JWT payload:
{ username: mobile_number, roles: [...], iat, exp }
Access token TTL: 1 hour | Refresh token TTL: 30 days (stored in Redis)
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_002 |
400 | Invalid or expired UUID |
POST /oauth/token/refresh
Refresh expired JWT using refresh token.
Permission: PUBLIC
Request Body
{
"refresh_token": "def50200..."
}
Response 200
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def50200..."
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Invalid or expired refresh token |
GET /oauth/userinfo
Get authenticated user info.
Permission: AUTH — requires valid JWT
Headers
Authorization: Bearer <token>
Response 200
{
"success": true,
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"mobile_number": "09123456789",
"real_name": "علی احمدی",
"roles": ["ROLE_USER"],
"status": "active",
"created_at": 1717000000
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Invalid or missing token |
POST /oauth/logout
Invalidate the refresh token (stored in Redis).
Permission: AUTH
Request Body
{
"refresh_token": "def50200..."
}
| Field | Type | Required |
|---|---|---|
refresh_token |
string | ❌ |
Response 200
{
"success": true,
"data": {
"message": "با موفقیت خارج شدید"
}
}