Authentication
Moon ERP uses Laravel Sanctum for token-based API authentication. Every request to a protected endpoint must include a valid bearer token in the Authorization header.
How It Works
- The client sends credentials to the login or register endpoint.
- The server returns a plain-text Sanctum token.
- The client includes this token in the
Authorizationheader for all subsequent requests. - On logout, the token is revoked and can no longer be used.
Token Lifetime
Tokens do not expire by default. A token remains valid until the user explicitly logs out, which revokes it. You can store the token securely on the client and reuse it across sessions.
Authorization Header
All protected endpoints require the following header:
Authorization: Bearer {token}Replace {token} with the value returned by login or register.
Endpoints
POST /api/auth/register
Create a new user account. This endpoint is public (no token required).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
company_id | integer | Yes | ID of the company to register under |
name | string | Yes | Full name (English) |
name_ar | string | Yes | Full name (Arabic) |
email | string | Yes | Email address (unique per company) |
password | string | Yes | Minimum 8 characters |
password_confirmation | string | Yes | Must match password |
phone | string | No | Phone number |
branch_id | integer | No | ID of the branch to assign |
curl -X POST https://moon-erp.test/api/auth/register \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"company_id": 1,
"name": "Sara Ali",
"name_ar": "سارة علي",
"email": "sara@example.com",
"password": "secret1234",
"password_confirmation": "secret1234"
}'import 'dart:convert';
import 'package:http/http.dart' as http;
final response = await http.post(
Uri.parse('https://moon-erp.test/api/auth/register'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode({
'company_id': 1,
'name': 'Sara Ali',
'name_ar': 'سارة علي',
'email': 'sara@example.com',
'password': 'secret1234',
'password_confirmation': 'secret1234',
}),
);Response 201 Created
{
"data": {
"id": 2,
"name": "سارة علي",
"name_en": "Sara Ali",
"name_ar": "سارة علي",
"email": "sara@example.com",
"phone": null,
"locale": "ar",
"is_active": true,
"roles": ["employee"],
"permissions": [],
"created_at": "2026-02-16T12:00:00.000000Z",
"updated_at": "2026-02-16T12:00:00.000000Z"
},
"token": "3|newtoken..."
}POST /api/auth/login
Authenticate an existing user. This endpoint is public (no token required).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address |
password | string | Yes | User password |
curl -X POST https://moon-erp.test/api/auth/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"email": "sara@example.com",
"password": "secret1234"
}'final response = await http.post(
Uri.parse('https://moon-erp.test/api/auth/login'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode({
'email': 'sara@example.com',
'password': 'secret1234',
}),
);
final data = jsonDecode(response.body);
final token = data['token']; // Store this securelyResponse 200 OK
{
"data": {
"id": 2,
"name": "سارة علي",
"name_en": "Sara Ali",
"name_ar": "سارة علي",
"email": "sara@example.com",
"phone": null,
"locale": "ar",
"is_active": true,
"company": { "id": 1, "name": "Moon Corp" },
"branch": null,
"roles": ["employee"],
"permissions": [],
"created_at": "2026-02-16T12:00:00.000000Z",
"updated_at": "2026-02-16T12:00:00.000000Z"
},
"token": "4|logintoken..."
}Error Response 401 Unauthorized
{
"message": "Invalid credentials"
}Error Response 403 Forbidden (inactive account)
{
"message": "Account is inactive"
}POST /api/auth/logout
Revoke the current access token. Requires authentication.
curl -X POST https://moon-erp.test/api/auth/logout \
-H "Accept: application/json" \
-H "Authorization: Bearer 4|logintoken..."final response = await http.post(
Uri.parse('https://moon-erp.test/api/auth/logout'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);
// After logout, discard the stored tokenResponse 200 OK
{
"message": "Logged out"
}After logout, the token is permanently invalidated. The client must log in again to obtain a new token.
GET /api/auth/me
Retrieve the authenticated user's profile, including roles and permissions. Requires authentication.
curl -X GET https://moon-erp.test/api/auth/me \
-H "Accept: application/json" \
-H "Authorization: Bearer 4|logintoken..."final response = await http.get(
Uri.parse('https://moon-erp.test/api/auth/me'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
);
final profile = jsonDecode(response.body)['data'];Response 200 OK
{
"data": {
"id": 2,
"name": "سارة علي",
"name_en": "Sara Ali",
"name_ar": "سارة علي",
"email": "sara@example.com",
"phone": null,
"locale": "ar",
"is_active": true,
"company": { "id": 1, "name": "Moon Corp" },
"branch": null,
"roles": ["employee"],
"permissions": ["core.users.view", "core.settings.view"],
"created_at": "2026-02-16T12:00:00.000000Z",
"updated_at": "2026-02-16T12:00:00.000000Z"
}
}PUT /api/auth/me
Update the authenticated user's own profile. Requires authentication. All fields are optional -- only include the fields you want to change.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Full name (English) |
name_ar | string | No | Full name (Arabic) |
email | string | No | Email address (unique per company) |
phone | string | No | Phone number |
locale | string | No | Preferred locale (ar or en) |
password | string | No | New password (min 8 characters) |
password_confirmation | string | No | Required if password is provided |
curl -X PUT https://moon-erp.test/api/auth/me \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer 4|logintoken..." \
-d '{
"name": "Sara Ahmed Ali",
"locale": "en"
}'final response = await http.put(
Uri.parse('https://moon-erp.test/api/auth/me'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({
'name': 'Sara Ahmed Ali',
'locale': 'en',
}),
);Response 200 OK
{
"data": {
"id": 2,
"name": "Sara Ahmed Ali",
"name_en": "Sara Ahmed Ali",
"name_ar": "سارة علي",
"email": "sara@example.com",
"phone": null,
"locale": "en",
"is_active": true,
"company": { "id": 1, "name": "Moon Corp" },
"branch": null,
"roles": ["employee"],
"permissions": ["core.users.view", "core.settings.view"],
"created_at": "2026-02-16T12:00:00.000000Z",
"updated_at": "2026-02-16T14:00:00.000000Z"
}
}Security Recommendations
- Store tokens in secure storage (Keychain on iOS, EncryptedSharedPreferences on Android, flutter_secure_storage in Flutter).
- Never log tokens or include them in URLs.
- Always use HTTPS in production.
- Call the logout endpoint when the user signs out rather than just discarding the token locally.