# Milestone 3: Authentication and RBAC

## Objective

Define the production-ready authentication and role-based access control design for the Project Management System. This milestone converts the database foundation from Milestone 2 into concrete auth flows, REST API contracts, middleware responsibilities, JWT and refresh token lifecycle, password security rules, permission guards, role seeding, and access-control behavior.

This milestone is a design and implementation contract. It does not yet cover project-level permissions in full detail because project membership tables will be delivered in the project module milestone.

## Scope

Milestone 3 covers:

- Login and logout flow
- Access token and refresh token design
- Refresh token rotation
- Password hashing
- Current-user session APIs
- Role and permission loading
- RBAC middleware
- Permission guard helpers
- User role assignment rules
- Seed data for roles and permissions
- Security controls for auth endpoints

Out of scope for this milestone:

- Project membership rules
- Client/project/task APIs
- Password reset email flow
- Two-factor authentication
- SSO/OAuth providers

These can be added later without changing the core auth foundation.

## Auth Principles

- Access tokens must be short lived.
- Refresh tokens must be rotated.
- Refresh tokens must be stored hashed in the database.
- Raw refresh tokens must never be stored.
- Passwords must be hashed with a strong adaptive hash.
- Permission checks must happen on the backend.
- UI-level hiding of buttons is not a security boundary.
- System roles and permissions should be seeded and stable.
- Deleted or inactive users must not authenticate.
- Suspended users must not receive new tokens.

## Recommended Token Configuration

| Token | Recommended TTL | Storage | Purpose |
| --- | --- | --- | --- |
| Access token | 15 minutes | frontend memory or secure HTTP-only cookie | API authorization |
| Refresh token | 7 to 30 days | secure HTTP-only cookie | access token renewal |

For browser-based use, prefer secure HTTP-only cookies for refresh tokens.

Recommended cookie flags in production:

- `httpOnly: true`
- `secure: true`
- `sameSite: 'lax'` or `'strict'`
- scoped path for refresh/logout routes where practical

## JWT Access Token Claims

Access tokens should include only stable, non-sensitive claims.

```json
{
  "sub": "userId",
  "email": "user@example.com",
  "roles": ["admin"],
  "permissions": ["project.create", "task.view"],
  "iat": 1710000000,
  "exp": 1710000900
}
```

Rules:

- `sub` is the user ID.
- Do not put password hashes, refresh token IDs, or sensitive user profile data in the token.
- Permission claims improve request speed, but critical role/permission changes should invalidate sessions or force token refresh.
- For strict enterprise security, load permissions from cache/database on each request instead of trusting long-lived permission claims.

## Refresh Token Design

Refresh tokens are opaque random strings generated by the backend.

Store this in `refreshTokens`:

- `tokenHash`
- `familyId`
- `userId`
- `expiresAt`
- `revokedAt`
- `replacedByTokenId`
- `ipAddress`
- `userAgent`

Rotation behavior:

1. Client sends refresh token.
2. Backend hashes the received token.
3. Backend finds a non-revoked, non-expired token.
4. Backend creates a new refresh token in the same family.
5. Backend revokes the old refresh token.
6. Backend links old token to new token using `replacedByTokenId`.
7. Backend returns a new access token and refresh token.

Reuse detection:

- If a revoked refresh token is used again, treat it as possible token theft.
- Revoke the whole `familyId`.
- Force the user to login again.

## Password Security

Recommended hashing:

- Argon2id preferred
- bcrypt acceptable with a strong cost factor if Argon2 is not available

Password rules:

- Minimum 10 characters
- Reject common weak passwords
- Require a mix of character types for admin-created accounts where appropriate
- Never log raw passwords
- Never return password hashes from APIs

Password verification:

1. Find active user by email.
2. Verify password against `passwordHash`.
3. Reject if `deletedAt` is not NULL.
4. Reject if `isActive = false`.
5. Reject if `status` is `SUSPENDED` or `DEACTIVATED`.

## Auth API Routes

Base route:

```text
/auth
```

| Route | Method | Controller | Service | Permission | Purpose |
| --- | --- | --- | --- | --- | --- |
| `/auth/login` | POST | `authController.login` | `authService.login` | Public | Login with email/password |
| `/auth/refresh` | POST | `authController.refresh` | `authService.refreshToken` | Public with refresh token | Rotate refresh token |
| `/auth/logout` | POST | `authController.logout` | `authService.logout` | Authenticated | Revoke current refresh token |
| `/auth/logout-all` | POST | `authController.logoutAll` | `authService.logoutAll` | Authenticated | Revoke all user refresh tokens |
| `/auth/me` | GET | `authController.me` | `authService.getCurrentUser` | Authenticated | Return current user profile and permissions |
| `/auth/change-password` | POST | `authController.changePassword` | `authService.changePassword` | Authenticated | Change own password |

## Auth Request and Response Contracts

### POST `/auth/login`

Request:

```json
{
  "email": "admin@example.com",
  "password": "StrongPassword123"
}
```

Response:

```json
{
  "success": true,
  "data": {
    "accessToken": "jwt-access-token",
    "user": {
      "id": "clx-user-id",
      "firstName": "Admin",
      "lastName": "User",
      "email": "admin@example.com",
      "status": "ACTIVE",
      "roles": ["admin"],
      "permissions": ["user.create", "project.view"]
    }
  }
}
```

The refresh token should be returned as an HTTP-only cookie in browser deployments.

### POST `/auth/refresh`

Request:

```json
{}
```

The refresh token is read from cookie or an explicitly approved secure client storage mechanism.

Response:

```json
{
  "success": true,
  "data": {
    "accessToken": "new-jwt-access-token"
  }
}
```

### POST `/auth/logout`

Response:

```json
{
  "success": true,
  "message": "Logged out successfully"
}
```

### GET `/auth/me`

Response:

```json
{
  "success": true,
  "data": {
    "id": "clx-user-id",
    "firstName": "Admin",
    "lastName": "User",
    "email": "admin@example.com",
    "status": "ACTIVE",
    "roles": [
      {
        "id": "role-id",
        "name": "Admin",
        "slug": "admin"
      }
    ],
    "permissions": ["user.create", "user.view"],
    "developerProfile": null
  }
}
```

### POST `/auth/change-password`

Request:

```json
{
  "currentPassword": "OldStrongPassword123",
  "newPassword": "NewStrongPassword123"
}
```

Response:

```json
{
  "success": true,
  "message": "Password changed successfully"
}
```

Security rule:

- After password change, revoke all refresh tokens except the current session or revoke all sessions depending on product policy.

## User and RBAC API Routes

Base routes:

```text
/users
/roles
/permissions
```

| Route | Method | Controller | Service | Required Permission | Purpose |
| --- | --- | --- | --- | --- | --- |
| `/users` | POST | `userController.create` | `userService.createUser` | `user.create` | Create user |
| `/users` | GET | `userController.list` | `userService.listUsers` | `user.view` | List users |
| `/users/:id` | GET | `userController.getById` | `userService.getUserById` | `user.view` | View user |
| `/users/:id` | PATCH | `userController.update` | `userService.updateUser` | `user.update` | Update user |
| `/users/:id` | DELETE | `userController.remove` | `userService.softDeleteUser` | `user.delete` | Soft delete user |
| `/users/:id/roles` | PUT | `userController.assignRoles` | `userService.assignRoles` | `permission.assign` | Replace user roles |
| `/roles` | POST | `roleController.create` | `roleService.createRole` | `role.create` | Create role |
| `/roles` | GET | `roleController.list` | `roleService.listRoles` | `role.view` | List roles |
| `/roles/:id` | PATCH | `roleController.update` | `roleService.updateRole` | `role.update` | Update role |
| `/roles/:id` | DELETE | `roleController.remove` | `roleService.softDeleteRole` | `role.delete` | Soft delete role |
| `/roles/:id/permissions` | PUT | `roleController.assignPermissions` | `roleService.assignPermissions` | `permission.assign` | Replace role permissions |
| `/permissions` | GET | `permissionController.list` | `permissionService.listPermissions` | `permission.view` | List permissions |

## Middleware Design

Recommended middleware order:

```text
helmet
cors
rateLimiter
jsonBodyParser
requestId
authOptional/authRequired
permissionRequired
validator
controller
errorHandler
```

### `authRequired`

Responsibilities:

- Read `Authorization: Bearer <token>` header or configured auth cookie.
- Verify JWT signature.
- Verify token expiry.
- Load user if required by strict mode.
- Reject inactive, deleted, suspended, or deactivated users.
- Attach `req.user`.

Example `req.user`:

```ts
type AuthUser = {
  id: string;
  email: string;
  roles: string[];
  permissions: string[];
};
```

### `permissionRequired`

Responsibilities:

- Check whether authenticated user has required permission.
- Allow Admin bypass by role slug if policy allows.
- Return `403 Forbidden` when permission is missing.

Example usage:

```ts
router.post(
  "/projects",
  authRequired,
  permissionRequired("project.create"),
  validate(createProjectSchema),
  projectController.create
);
```

### `anyPermissionRequired`

Allows access when the user has at least one listed permission.

```ts
anyPermissionRequired(["project.view", "project.manage"])
```

### `allPermissionsRequired`

Allows access only when the user has all listed permissions.

```ts
allPermissionsRequired(["project.view", "project.viewBudget"])
```

## Auth Service Responsibilities

### `authService.login`

Steps:

1. Normalize email.
2. Find user by email.
3. Verify user is active and not deleted.
4. Verify password hash.
5. Load roles and permissions.
6. Update `lastLoginAt`.
7. Generate access token.
8. Generate refresh token.
9. Hash refresh token.
10. Save refresh token with `familyId`.
11. Return user, permissions, access token, and refresh token cookie value.

### `authService.refreshToken`

Steps:

1. Read refresh token.
2. Hash received token.
3. Find token record.
4. Reject expired token.
5. Detect reuse if token is revoked.
6. Revoke token family on reuse.
7. Load active user.
8. Load roles and permissions.
9. Create new refresh token.
10. Revoke old refresh token.
11. Return new access token and refresh token.

### `authService.logout`

Steps:

1. Hash current refresh token.
2. Mark matching token as revoked.
3. Clear refresh token cookie.

### `authService.logoutAll`

Steps:

1. Find authenticated user.
2. Revoke all active refresh tokens for user.
3. Clear refresh token cookie.

### `authService.changePassword`

Steps:

1. Verify current password.
2. Validate new password strength.
3. Hash new password.
4. Update user password.
5. Revoke refresh tokens according to policy.

## Repository Responsibilities

### `authRepository`

- Find user by email
- Find user by ID
- Update last login
- Create refresh token
- Find refresh token by hash
- Revoke refresh token
- Revoke token family
- Revoke all user refresh tokens

### `permissionRepository`

- Load user roles
- Load user permissions
- List all permissions
- Assign permissions to role
- Assign roles to user

### `userRepository`

- Create user
- Update user
- Soft delete user
- List users
- Find user by ID

## Validation Rules

### Login

- `email`: required, valid email, normalized lowercase
- `password`: required, string

### Create User

- `firstName`: required, max 100
- `lastName`: optional, max 100
- `email`: required, valid email, unique
- `password`: required or generated invite flow
- `phone`: optional, max 30
- `roleIds`: required array, at least one role

### Assign Roles

- `roleIds`: required array
- all roles must exist
- system must prevent removing the final admin user

### Assign Permissions

- `permissionIds`: required array
- all permissions must exist
- system roles may only be modified by Admin

## Standard Error Responses

### 401 Unauthorized

```json
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication is required"
  }
}
```

### 403 Forbidden

```json
{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have permission to perform this action"
  }
}
```

### Invalid Login

Use a generic message to avoid account enumeration.

```json
{
  "success": false,
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Invalid email or password"
  }
}
```

## Rate Limiting

Recommended rate limits:

| Endpoint | Limit |
| --- | --- |
| `/auth/login` | 5 attempts per 15 minutes per IP/email combination |
| `/auth/refresh` | 60 requests per 15 minutes per IP/session |
| `/auth/change-password` | 5 attempts per hour per user |
| User/role management APIs | 100 requests per 15 minutes per user |

Failed login attempts should be logged for security monitoring.

## Seed Roles

Seed these roles:

```json
[
  {
    "name": "Admin",
    "slug": "admin",
    "isSystem": true
  },
  {
    "name": "Project Manager",
    "slug": "projectManager",
    "isSystem": true
  },
  {
    "name": "Team Leader",
    "slug": "teamLeader",
    "isSystem": true
  },
  {
    "name": "Team Member",
    "slug": "teamMember",
    "isSystem": true
  }
]
```

## Seed Permissions

Foundation permissions from Milestone 2:

```json
[
  "user.create",
  "user.view",
  "user.update",
  "user.delete",
  "role.create",
  "role.view",
  "role.update",
  "role.delete",
  "permission.view",
  "permission.assign",
  "client.create",
  "client.view",
  "client.update",
  "client.delete",
  "developerProfile.manage",
  "developerRate.manage"
]
```

Project/task/reporting permissions will be appended in later milestones.

## Seed Role Permission Matrix

### Admin

Admin receives all seeded permissions.

### Project Manager

Initial permissions:

- `user.view`
- `client.create`
- `client.view`
- `client.update`

Later project permissions will include project, milestone, sprint, task, report, budget, and credential permissions.

### Team Leader

Initial permissions:

- `user.view`
- `client.view`

Later project permissions will be limited by assigned team/project membership.

### Team Member

Initial permissions:

- `user.view` for own profile only

Later task permissions will be limited to assigned projects and tasks.

## Seed Admin User

Create one initial admin user from environment variables:

```text
SEED_ADMIN_EMAIL
SEED_ADMIN_PASSWORD
SEED_ADMIN_FIRST_NAME
SEED_ADMIN_LAST_NAME
```

Rules:

- Do not hardcode production admin credentials.
- Reject weak seed password.
- If admin email already exists, do not overwrite password automatically.
- Assign the `admin` role.

## Access Decision Model

Every protected request should answer these questions:

1. Is the user authenticated?
2. Is the user active and not deleted?
3. Does the user have the required permission?
4. If the route accesses a specific resource, is the user allowed to access that resource?

For Milestone 3, resource-specific checks are limited to user ownership and admin controls. Project-specific checks begin once project tables exist.

## Self-Access Rules

Users should be able to:

- View their own profile.
- Change their own password.
- View their own roles and permissions.

Users should not be able to:

- Assign roles to themselves.
- Grant permissions to themselves.
- Deactivate themselves if they are the final active admin.
- View another user profile without `user.view`.

## Final Admin Protection

The system must prevent:

- Deleting the final active admin user
- Removing the final active admin role assignment
- Deactivating the final active admin
- Removing required admin permissions from the final admin role

These checks belong in service-layer transactions.

## Activity Logging Hooks

Milestone 15 will define the full `activityLogs` module, but auth/RBAC services should already expose hook points for:

- User login
- Failed login
- Logout
- Password changed
- User created
- User updated
- User deleted
- Role assigned
- Permission assigned
- Refresh token reuse detected

Until `activityLogs` exists, these events can be emitted to structured application logs.

## Security Checklist

- Use HTTPS in production.
- Store refresh tokens in HTTP-only secure cookies for browser clients.
- Hash refresh tokens before database storage.
- Rotate refresh tokens on every refresh.
- Revoke refresh token family on reuse detection.
- Use generic invalid login errors.
- Enforce rate limits on login and refresh routes.
- Validate all request bodies.
- Never return `passwordHash`.
- Never log raw tokens or passwords.
- Use strong JWT secrets from environment variables.
- Keep access tokens short lived.
- Protect final admin account/role.

## Recommended Backend Files

```text
src/
  controllers/
    authController.ts
    userController.ts
    roleController.ts
    permissionController.ts
  services/
    authService.ts
    userService.ts
    roleService.ts
    permissionService.ts
  repositories/
    authRepository.ts
    userRepository.ts
    roleRepository.ts
    permissionRepository.ts
  middlewares/
    authRequired.ts
    permissionRequired.ts
    rateLimiters.ts
  validators/
    authValidators.ts
    userValidators.ts
    roleValidators.ts
  routes/
    authRoutes.ts
    userRoutes.ts
    roleRoutes.ts
    permissionRoutes.ts
  utils/
    password.ts
    jwt.ts
    refreshToken.ts
    cookies.ts
  prisma/
    seed.ts
```

## Milestone 3 Completion Criteria

Milestone 3 is complete when the following are accepted:

- Auth routes are defined.
- User/RBAC routes are defined.
- JWT access token rules are defined.
- Refresh token rotation rules are defined.
- Password hashing and validation rules are defined.
- Auth middleware responsibilities are defined.
- Permission middleware responsibilities are defined.
- Role and permission seed data is defined.
- Initial admin seed rules are defined.
- Security checklist is defined.

## Next Milestone

Milestone 4 should deliver Project Management Core:

- `projects` table
- `projectMembers` table
- Project status enum
- Project team assignment rules
- Project create/update/view/delete APIs
- Project manager, team leader, and team member relationships
- Project budget base fields
- Git/staging/production/API URL fields
- Project assignment notification triggers
