# Implementation Plan: Authentication (SP-01)

**Branch**: `feat/SP-01-authentication` | **Date**: 2026-06-01 | **Spec**: [spec.md](./spec.md)

**Input**: Feature specification from `/specs/001-authentication/spec.md`

## Summary

Implement authentication foundation for a single-admin API system using Laravel Sanctum token-based authentication. Admin authenticates via email/password at `/api/v1/auth/login` to receive a Bearer token, then uses it on all protected endpoints. Features include login, logout, and session verification (me).

## Technical Context

**Language/Version**: PHP 8.2+

**Primary Dependencies**: Laravel 11, Laravel Sanctum

**Storage**: MySQL 8+

**Testing**: PHPUnit (Feature Tests)

**Target Platform**: Linux server (API-first Backend consumed via Mobile App)

**Project Type**: web-service

**Performance Goals**: Authentication response <2s, rate limit response <100ms

**Constraints**: Single admin only, Asia/Damascus timezone, no soft deletes permitted

**Scale/Scope**: Single user (Admin) — no multi-admin support

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

| Rule | Status |
|------|--------|
| Clean Architecture layers (Request→FormRequest→Controller→Service→Repository→Model→Resource→Response) | ✅ PASS |
| No business logic in Controller or Model | ✅ PASS |
| Repository is sole database gateway | ✅ PASS |
| Laravel Sanctum for token-based authentication | ✅ PASS |
| Unified API response format (success/error) | ✅ PASS |
| FormRequest for all validation | ✅ PASS |
| PHPDoc on all public methods | ✅ PASS |
| Rate Limiting on /login (5/min) | ✅ PASS |
| No Soft Deletes | ✅ PASS |
| Token storage hashed (not plaintext) | ✅ PASS |

**Result**: All gates pass. No violations to justify.

## Project Structure

### Documentation (this feature)

```text
specs/001-authentication/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md        # Phase 1 output
├── contracts/           # Phase 1 output
└── tasks.md             # Phase 2 output (/speckit.tasks - NOT created here)
```

### Source Code (repository root)

```text
tamkeen/
├── app/
│   └── Domains/
│       └── Auth/
│           ├── Models/
│           │   └── Admin.php
│           ├── Repositories/
│           │   ├── Contracts/
│           │   │   └── AdminRepositoryInterface.php
│           │   └── Eloquent/
│           │       └── EloquentAdminRepository.php
│           ├── Services/
│           │   └── AuthService.php
│           └── Http/
│               ├── Controllers/
│               │   └── AuthController.php
│               ├── Requests/
│               │   └── LoginRequest.php
│               └── Resources/
│                   └── AdminResource.php
├── database/
│   └── seeders/
│       └── AdminSeeder.php
├── routes/
│   └── api.php
└── tests/
    └── Feature/
        └── Auth/
            └── AuthenticationTest.php
```

**Structure Decision**: Follows Constitution Domain Separation pattern exactly. Auth is its own domain under `app/Domains/Auth/`. No violations detected.

## Complexity Tracking

No complexity violations. All requirements align with Constitution patterns. No simpler alternatives rejected — standard Laravel Sanctum implementation with repository pattern for testability.

---

## Phase 0: Research

### Decision: Token-Based Authentication via Laravel Sanctum

**Chosen**: Laravel Sanctum with Bearer tokens

**Rationale**:
- Constitution mandates Sanctum for token-based auth (Section 5. Security Constitution)
- Stateless tokens work better for API-first architecture consumed by mobile app
- Sanctum handles token storage (hashed) automatically
- Built-in rate limiting support via middleware

**Alternatives considered**:
- Session-based auth — rejected (Constitution specifies token-based, not sessions)
- JWT (custom) — rejected (Sanctum is Laravel native, better integration)
- OAuth2 — rejected (Constitution states no OAuth/social login)

### Decision: Single Admin with Pre-Seeded Account

**Chosen**: Admin pre-seeded via ADMIN_NAME, ADMIN_EMAIL, ADMIN_PASSWORD env vars

**Rationale**:
- Specification explicitly states single-admin with no self-registration
- Environment-based seeding is secure (no plaintext credentials in DB)
- Idempotent seeder prevents duplicate creation

**Alternatives considered**:
- Database-seeded with fixed credentials — rejected (env vars allow deployment flexibility)
- Registration endpoint — explicitly out of scope per spec

### Decision: Rate Limiting via Laravel Throttle Middleware

**Chosen**: 5 attempts per minute per IP using Laravel's throttle middleware

**Rationale**:
- Constitution mandates rate limiting on /login (5 attempts/minute)
- Laravel's built-in throttle is sufficient, no external package needed
- Per-IP limiting matches specification requirement

## Phase 1: Design

### Entities

**Admin**:
- id: BIGINT UNSIGNED PK AUTO_INCREMENT
- name: VARCHAR(100) NOT NULL
- email: VARCHAR(150) UNIQUE NOT NULL
- password: VARCHAR(255) NOT NULL (bcrypt)
- created_at: TIMESTAMP
- updated_at: TIMESTAMP

**Personal Access Token** (Sanctum managed):
- tokens table managed by Sanctum (name, token, abilities, last_used_at, expires_at)

### API Contracts

**POST /api/v1/auth/login**
- Request: `{ "email": "admin@example.com", "password": "secret" }`
- Success Response (200): `{ "success": true, "message": "Login successful", "data": { "token": "...", "token_type": "Bearer", "admin": { "id": 1, "name": "...", "email": "..." } } }`
- Error Response (401): `{ "success": false, "message": "Invalid credentials", "errors": {} }`
- Error Response (422): `{ "success": false, "message": "Validation failed", "errors": { "email": ["The email field is required."] } }`
- Error Response (429): `{ "success": false, "message": "Too many attempts", "errors": {} }`

**DELETE /api/v1/auth/logout**
- Headers: `Authorization: Bearer <token>`
- Success Response (200): `{ "success": true, "message": "Logged out successfully", "data": {} }`
- Error Response (401): `{ "success": false, "message": "Unauthenticated", "errors": {} }`

**GET /api/v1/auth/me**
- Headers: `Authorization: Bearer <token>`
- Success Response (200): `{ "success": true, "message": "Success", "data": { "id": 1, "name": "...", "email": "...", "created_at": "..." } }`
- Error Response (401): `{ "success": false, "message": "Unauthenticated", "errors": {} }`

---

## Implementation Notes

- AuthController must remain thin — delegates all logic to AuthService
- AuthService contains: login(), logout(), me()
- LoginRequest validates: email (required, email format), password (required, string)
- AdminResource returns: id, name, email, created_at
- Admin model extends Authenticatable and uses HasApiTokens trait
- AdminSeeder is idempotent (checks by email before creating)
- Rate limiting middleware applied ONLY to /login route