# Auth API Documentation

Token-based authentication (Laravel Sanctum) with OTP email verification and OTP password reset.

- **Base URL:** `http://localhost:8000/api`
- **Prefix:** all endpoints below are under `/auth`
- **Headers (all requests):** `Accept: application/json`, `Content-Type: application/json`
- **Protected endpoints:** `Authorization: Bearer <token>`

## Roles

| Role | Where it can log in |
|------|--------------------|
| `user` (customer) | **API only** (`/api/auth/*`). Cannot sign in to the web panel (403 on web pages). |
| `admin` | **Web panel only**. API login returns `403 role_not_allowed`. |

- Everyone who registers through the API is always created as `user`; any `role` sent in the body is ignored.
- Admins are created by seeder/tinker only (`DatabaseSeeder` creates `bappy@dev.local`). Users that existed before the `role` column was added were migrated to `admin`.
- Web routes are protected by the `admin` middleware, so a customer cannot reach the panel even with a session.
- Every `user` object in API responses now includes `"role": "user"`.

## Endpoint Summary

| Method | Endpoint | Auth | Throttle | Purpose |
|--------|----------|------|----------|---------|
| POST | `/auth/register` | – | 10/min | Create account, send verification OTP |
| POST | `/auth/email/verify` | – | 10/min | Verify email with OTP |
| POST | `/auth/email/resend-otp` | – | 6/min | Resend verification OTP |
| POST | `/auth/login` | – | 10/min | Log in, receive Bearer token |
| GET | `/auth/me` | Bearer | – | Current user |
| POST | `/auth/logout` | Bearer | – | Revoke current token |
| POST | `/auth/password/forgot` | – | 6/min | Send password-reset OTP |
| POST | `/auth/password/reset` | – | 10/min | Reset password with OTP |

## OTP Rules

| Rule | Value |
|------|-------|
| Length | 6 digits |
| Expiry | 10 minutes |
| Resend cooldown | 60 seconds (per email + purpose) |
| Max wrong attempts | 5 (then OTP is destroyed; request a new one) |
| Storage | Hashed in `otps` table; one active OTP per email per purpose |
| Delivery | Email (`MAIL_MAILER=log` in dev → read the code in `storage/logs/laravel.log`) |

## Response Format

Success:
```json
{ "success": true, "message": "…", "data": { } }
```

Validation error (HTTP 422, standard Laravel):
```json
{
  "message": "The email field is required.",
  "errors": { "email": ["The email field is required."] }
}
```

Other errors: `401 Unauthenticated`, `403 email_not_verified`, `429 Too Many Requests` (rate limit or OTP cooldown).

---

## Flow Overview

```
Register ──► OTP emailed ──► POST email/verify ──► Login ──► Bearer token ──► protected APIs
               │  (expired / lost?)
               └──► POST email/resend-otp (after 60s)

Forgot password ──► OTP emailed ──► POST password/reset ──► Login with new password
                    (resend by calling forgot again after 60s)
```

---

## 1. Register

`POST /api/auth/register`

**Body**

| Field | Type | Rules |
|-------|------|-------|
| `name` | string | required, max 255 |
| `email` | string | required, valid email, unique |
| `password` | string | required, min 8 (12 + mixed case/numbers/symbols in production) |
| `password_confirmation` | string | required, must match `password` |

```json
{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "password123",
  "password_confirmation": "password123"
}
```

**201 Created**
```json
{
  "success": true,
  "message": "Registration successful. A verification code has been sent to your email.",
  "data": {
    "user": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "role": "user",
      "email_verified_at": null,
      "created_at": "2026-10-13T10:00:00+00:00"
    }
  }
}
```

**422** – e.g. email already taken:
```json
{ "message": "The email has already been taken.", "errors": { "email": ["The email has already been taken."] } }
```

> No token is issued on registration. The user must verify email, then log in.

---

## 2. Verify Email

`POST /api/auth/email/verify`

| Field | Type | Rules |
|-------|------|-------|
| `email` | string | required, email |
| `otp` | string | required, exactly 6 digits |

```json
{ "email": "john@example.com", "otp": "482913" }
```

**200 OK**
```json
{ "success": true, "message": "Email verified successfully." }
```
(Already verified accounts also get `200` with `"Email is already verified."`)

**422 – wrong / expired OTP**
```json
{ "message": "The code is invalid or has expired.", "errors": { "otp": ["The code is invalid or has expired."] } }
```

**422 – 5 wrong attempts**
```json
{ "message": "Too many incorrect attempts. Please request a new code.", "errors": { "otp": ["Too many incorrect attempts. Please request a new code."] } }
```

---

## 3. Resend OTP (email verification)

`POST /api/auth/email/resend-otp`

| Field | Type | Rules |
|-------|------|-------|
| `email` | string | required, email |

```json
{ "email": "john@example.com" }
```

**200 OK** (same response whether or not the account exists / is unverified, to avoid account enumeration)
```json
{ "success": true, "message": "If the account exists and is unverified, a new code has been sent." }
```

**429 – cooldown not elapsed**
```json
{ "message": "Please wait 42 seconds before requesting another code.", "errors": { "email": ["Please wait 42 seconds before requesting another code."] } }
```

A new OTP invalidates the previous one and resets the attempt counter.

---

## 4. Login

`POST /api/auth/login`

| Field | Type | Rules |
|-------|------|-------|
| `email` | string | required, email |
| `password` | string | required |
| `device_name` | string | optional, max 100 (label for the token, default `api`) |

```json
{ "email": "john@example.com", "password": "password123", "device_name": "iPhone 15" }
```

**200 OK**
```json
{
  "success": true,
  "message": "Login successful.",
  "data": {
    "user": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "role": "user",
      "email_verified_at": "2026-10-13T10:05:00+00:00",
      "created_at": "2026-10-13T10:00:00+00:00"
    },
    "token": "1|Zk3q…plainTextToken",
    "token_type": "Bearer"
  }
}
```

**422 – bad credentials**
```json
{ "message": "These credentials do not match our records.", "errors": { "email": ["These credentials do not match our records."] } }
```

**403 – admin account (API is for customers only)**
```json
{ "success": false, "message": "This account is not allowed to log in through the API.", "code": "role_not_allowed" }
```

**403 – email not verified** (call `email/verify`, or `email/resend-otp` first)
```json
{ "success": false, "message": "Your email address is not verified.", "code": "email_not_verified" }
```

---

## 5. Current User

`GET /api/auth/me` — `Authorization: Bearer <token>`

**200 OK**
```json
{
  "success": true,
  "data": {
    "user": { "id": 1, "name": "John Doe", "email": "john@example.com", "role": "user", "email_verified_at": "2026-10-13T10:05:00+00:00", "created_at": "2026-10-13T10:00:00+00:00" }
  }
}
```

**401**
```json
{ "message": "Unauthenticated." }
```

---

## 6. Logout

`POST /api/auth/logout` — `Authorization: Bearer <token>`

Revokes only the token used for this request.

**200 OK**
```json
{ "success": true, "message": "Logged out successfully." }
```

---

## 7. Forgot Password

`POST /api/auth/password/forgot`

| Field | Type | Rules |
|-------|------|-------|
| `email` | string | required, email |

```json
{ "email": "john@example.com" }
```

**200 OK** (always, even if the email is unknown)
```json
{ "success": true, "message": "If the account exists, a password reset code has been sent." }
```

**429** – if called again within 60 seconds for an existing account (same shape as resend cooldown). To resend the reset OTP, call this endpoint again after the cooldown.

---

## 8. Reset Password

`POST /api/auth/password/reset`

| Field | Type | Rules |
|-------|------|-------|
| `email` | string | required, email |
| `otp` | string | required, 6 digits |
| `password` | string | required, confirmed, password rules as in register |
| `password_confirmation` | string | required |

```json
{
  "email": "john@example.com",
  "otp": "730184",
  "password": "newpassword123",
  "password_confirmation": "newpassword123"
}
```

**200 OK**
```json
{ "success": true, "message": "Password reset successfully. Please log in." }
```

**422** – invalid/expired/locked OTP, same shapes as in *Verify Email*.

On success **all existing tokens for the user are revoked** (every device is logged out).

---

## Client Flow Examples

**Sign-up**
1. `POST /auth/register` → show OTP screen
2. `POST /auth/email/verify` → on 200 go to login
3. `POST /auth/login` → store `data.token`; send `Authorization: Bearer …` on later calls
4. OTP not received → `POST /auth/email/resend-otp` (enable button after 60s)

**Forgot password**
1. `POST /auth/password/forgot`
2. `POST /auth/password/reset` with OTP + new password
3. `POST /auth/login`

## cURL Quick Test

```bash
curl -X POST http://localhost:8000/api/auth/register \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"name":"John","email":"john@example.com","password":"password123","password_confirmation":"password123"}'

# read OTP from storage/logs/laravel.log, then:
curl -X POST http://localhost:8000/api/auth/email/verify \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"email":"john@example.com","otp":"482913"}'

curl -X POST http://localhost:8000/api/auth/login \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"email":"john@example.com","password":"password123"}'

curl http://localhost:8000/api/auth/me -H "Accept: application/json" -H "Authorization: Bearer <token>"
```

## Setup Notes

- Run `php artisan migrate` (adds `personal_access_tokens`, `otps` tables and `users.role`).
- Create another admin: `User::factory()->create(['email' => '…'])` in tinker, or set `role` to `admin`.
- Configure real SMTP (`MAIL_*` in `.env`) for production; `MAIL_MAILER=log` writes emails to the log.
- Code: `routes/api.php`, `app/Http/Controllers/Api/Auth/*`, `app/Services/OtpService.php`, `app/Notifications/OtpNotification.php`.
- Tests: `php artisan test tests/Feature/Auth/ApiAuthTest.php`.
