Skip to content

Authentication & Authorization

Session-based authentication with a 5-role RBAC hierarchy and object-level permissions.

Authentication

Session-Based Auth

The system uses django-allauth in headless mode with session-based authentication:

  • Session cookies: httpOnly, Secure (in production), SameSite=Lax
  • CSRF protection: Token sent via X-CSRFToken header on mutating requests
  • No JWT/token auth: Sessions are server-side, stored in the database

Login Flow

1. GET  /api/v1/auth/session          → Establish CSRF cookie
2. POST /api/v1/auth/login            → Authenticate (email + password)
3. GET  /api/v1/users/me              → Fetch user profile + permissions

Rate Limiting

Auth endpoints are rate-limited to prevent brute-force attacks:

Endpoint Rate Limit
Login 10/min
Signup Rate-limited per action
Password reset request Rate-limited per email
Reauthentication Rate-limited per action

Rate limiting uses allauth's built-in consume_or_429() mechanism.

Production Security Headers

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000        # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True

RBAC Role Hierarchy

Five roles with additive permissions. Each role inherits all permissions from roles below it:

Role Level Description
superadmin Bypasses all tenant role checks (user flag)
admin 4 Full tenant management
loan_officer 3 Create/edit loans, borrowers, servicing
collector 2 Collection actions, payment recording
viewer 1 Read-only access to all resources

Permission Classes

Async permission classes in common/permissions.py enforce role requirements:

Class Minimum Role
IsAuthenticated Any authenticated user
IsSuperAdmin Superadmin flag
IsTenantAdmin admin (level 4)
IsLoanOfficerOrAbove loan_officer (level 3)
IsCollectorOrAbove collector (level 2)
IsViewerOrAbove viewer (level 1)

Permission classes are set at the controller level and can be overridden per route:

@api_controller("/borrowers", permissions=[IsViewerOrAbove])
class BorrowerController(ControllerBase):
    @http_get("")
    async def list_borrowers(self, ...):
        ...  # Requires viewer or above

    @http_post("", permissions=[IsLoanOfficerOrAbove])
    async def create_borrower(self, ...):
        ...  # Requires loan_officer or above

Tenant Role Lookup

Permission classes look up the user's role via TenantUser (maps users to tenant roles). The result is cached on the request object (_cached_tenant_role) to avoid repeated queries within a single request.

react-admin RBAC

GET /api/v1/users/me returns permissions in react-admin RBAC format:

{
  "id": "uuid",
  "email": "user@example.com",
  "role": "loan_officer",
  "permissions": [
    {"action": "list", "resource": "borrowers"},
    {"action": "create", "resource": "borrowers"},
    {"action": "edit", "resource": "borrowers"},
    {"action": "show", "resource": "loans"},
    {"type": "deny", "action": "write", "resource": "borrowers.ssn_last_four"}
  ]
}

Permission Sources (merged)

  1. Tenant role mapping (common/rbac.py) --- Role-based defaults
  2. Django auth permissions --- Group/Permission model grants
  3. django-guardian grants --- Object-level permissions on specific loans/borrowers

Field-Level Restrictions

Certain fields have explicit deny rules applied to all roles:

FIELD_RESTRICTIONS = [
    {"type": "deny", "action": "write", "resource": "borrowers.ssn_last_four"},
]

Object-Level Permissions

django-guardian provides object-level permissions for fine-grained access control:

  • Loan officers can be granted access to specific loans
  • Collectors can be assigned to specific collection queues
  • Object permissions are checked via has_object_permission() in permission classes
  • Permissions are composed with & (AND), | (OR), ~ (NOT)

Portal Authentication

The borrower portal uses a separate auth flow:

  • AuthContext manages borrower session state
  • IsBorrowerUser permission class restricts portal endpoints
  • Borrower.portal_user FK maps borrowers to user accounts
  • All portal data is scoped to the authenticated borrower's records

See Also