Files
integreat/docs/testing/behaviors/auth.md
Bryce d627e3c5d0 refactor(all): rewrite all behavior docs in table format with checkboxes
Rewrite all 11 remaining behavior documents to match the streamlined
invoice.md format:

- dashboard.md: 250 lines, 62 behaviors
- payment.md: 260 lines, behaviors for list, void, check printing, ACH
- transaction.md: 310 lines, list, import, admin insights
- ledger.md: 519 lines, entries, P&L, balance sheet, cash flows
- company.md: 320 lines, profile, 1099s, Plaid/Yodlee, reports
- admin.md: 494 lines, clients, accounts, vendors, rules, jobs, history
- pos.md: 405 lines, sales, deposits, tenders, refunds, shifts
- search-indicators.md: 167 lines, search modal, indicators
- auth.md: 184 lines, login, logout, impersonation, sessions
- outgoing-invoice.md: 192 lines, create, line items, PDF
- legacy-spa.md: 340 lines, all legacy pages (docs only)

All documents now use:
- Testing Patterns section with reusable abstractions
- Numbered tables: # | Behavior | Test Strategy | Status
- It should... behavior descriptions
- Checkboxes [ ]/[x] for tracking implementation
- Cross-Cutting Behaviors for permissions, lock dates, etc.
- Test Data Requirements tables
- Existing Tests to Preserve sections

Total: 3,844 lines of behavior documentation across 12 subsystem docs.
2026-05-04 13:48:51 -07:00

8.8 KiB

Authentication Behaviors

Overview

Authentication in Integreat uses Google OAuth 2.0 as the primary identity provider. Users authenticate via Google, receive a JWT token, and establish a server-side session. The system supports role-based access control (admin, user, read-only), client-scoped permissions, session versioning for SSR rollout, and admin impersonation via signed JWT tokens.

Testing Philosophy

  • Prefer unit tests for pure business logic (JWT generation, compression, validation)
  • Use integration tests for database interactions and cross-system flows (OAuth callback, session management)
  • Use UI tests only for end-to-end happy paths that touch multiple pages
  • Every behavior must be user-visible; no tests for implementation details

Testing Patterns

Pattern: OAuth Login Flow

The standard authentication flow follows these steps:

  1. Unauthenticated user navigates to a protected page
  2. User is redirected to /login
  3. User clicks "Sign in with Google"
  4. Browser redirects to Google OAuth consent screen
  5. User consents and Google redirects to /api/oauth?code=<code>&state=<state>
  6. Server exchanges code for token, fetches profile, finds/creates user
  7. Server redirects to original page (or /) with ?jwt=<token>
  8. Client reads JWT and establishes session

Test implications: Integration test the OAuth callback handler. UI test only the happy path once.

Pattern: Middleware Stack

Every protected route passes through authentication middleware:

  1. wrap-session-version — validates session version, redirects to login if outdated
  2. wrap-secure — checks for authenticated session, redirects to login if missing
  3. wrap-admin — checks for admin role, redirects to login if not admin
  4. wrap-client-redirect-unauthenticated — converts 401 responses to HTMX redirects

Test implications: Integration test each middleware independently. UI tests only verify redirect behavior.

Pattern: JWT Claims

The JWT token contains user identity and permissions:

  1. :user, :exp, :db/id, :user/role, :user/name for all users
  2. :gz-clients (compressed) for admin and read-only users
  3. :user/clients (plain) for regular users

Test implications: Unit test JWT generation for each role type.


Login

Display Behaviors

# Behavior Test Strategy Status
1.1 It should display a "Sign in with Google" button on the login page UI [ ]

OAuth Behaviors

# Behavior Test Strategy Status
1.2 It should redirect to Google OAuth when the user clicks "Sign in with Google" UI [ ]
1.3 It should exchange the authorization code for an access token on callback Integration [ ]
1.4 It should fetch the user's Google profile using the access token Integration [ ]
1.5 It should create a new user account when the user logs in for the first time Integration [ ]
1.6 It should find the existing user account on subsequent logins Integration [ ]
1.7 It should redirect to the original page after successful OAuth Integration [ ]
1.8 It should redirect to the root page when no return URL is provided Integration [ ]
1.9 It should establish a server-side session with user identity and version Integration [ ]
1.10 It should pass the JWT token in the query string after successful OAuth Integration [ ]
1.11 It should display the user's clients and data after successful login UI [ ]
1.12 It should handle users without email via Google provider ID Integration [ ]
1.13 It should return 401 with error message when the OAuth code is missing Integration [ ]
1.14 It should return 401 when the OAuth code is invalid or expired Integration [ ]
1.15 It should return 401 and log a warning when the Google network request fails Integration [ ]

Logout

# Behavior Test Strategy Status
2.1 It should clear the session when the user navigates to /logout Integration [ ]
2.2 It should redirect to the login page after logout Integration [ ]
2.3 It should remain idempotent when logging out without an active session Integration [ ]

Impersonation

# Behavior Test Strategy Status
3.1 It should allow admin users to assume another user's identity via signed JWT Integration [ ]
3.2 It should validate the impersonation JWT signature with :jwt-secret and :hs512 Integration [ ]
3.3 It should reject expired impersonation JWTs Integration [ ]
3.4 It should block non-admin users from accessing /impersonate Integration [ ]
3.5 It should block unauthenticated users from accessing /impersonate Integration [ ]
3.6 It should replace the admin's session with the impersonated user's session Integration [ ]

Session Management

Authentication Gate Behaviors

# Behavior Test Strategy Status
4.1 It should allow authenticated requests to proceed to protected routes Integration [ ]
4.2 It should redirect unauthenticated users to /login with a redirect-to parameter Integration [ ]
4.3 It should return hx-redirect: /login for unauthenticated HTMX requests Integration [ ]

Admin Gate Behaviors

# Behavior Test Strategy Status
5.1 It should allow admin requests to proceed to admin-only routes Integration [ ]
5.2 It should redirect non-admin users to /login when accessing admin routes Integration [ ]

Session Version Behaviors

# Behavior Test Strategy Status
6.1 It should invalidate sessions with outdated version numbers Integration [ ]
6.2 It should redirect to /login when an outdated session accesses normal routes Integration [ ]
6.3 It should return hx-redirect: /login for outdated sessions on HTMX routes Integration [ ]
6.4 It should return 401 for outdated sessions on GraphQL routes Integration [ ]
6.5 It should treat sessions without a version as outdated Integration [ ]

Cross-Cutting Behaviors

JWT Behaviors

# Behavior Test Strategy Status
7.1 It should generate a JWT containing the user's role and client access on login Unit [ ]
7.2 It should compress the client list for admin users to fit in the JWT Unit [ ]
7.3 It should compress the client list for read-only users to fit in the JWT Unit [ ]
7.4 It should include a plain client list for regular users in the JWT Unit [ ]
7.5 It should create API tokens with admin role and 1000-day expiration Unit [ ]

Middleware Behaviors

# Behavior Test Strategy Status
8.1 It should convert 401 responses to HTMX redirects for unauthenticated users Integration [ ]

Role-Based Behaviors

# Behavior Test Strategy Status
9.1 It should allow admin users to access all clients Integration [ ]
9.2 It should allow regular users to access only their assigned clients Integration [ ]
9.3 It should allow read-only users to access all clients with view-only permissions Integration [ ]
9.4 It should handle admin users with no clients by providing an empty compressed list Unit [ ]
9.5 It should handle regular users with no clients by providing an empty client vector Unit [ ]

Security Behaviors

# Behavior Test Strategy Status
10.1 It should reject tampered JWTs during impersonation Integration [ ]
10.2 It should treat sessions with nil identity as unauthenticated Integration [ ]

Test Data Requirements

Entity Requirements
Users Admin user with multiple clients; regular user with subset of clients; read-only user with multiple clients; new user (not in database) with Google provider details; existing user with Google provider details
Clients Multiple clients with :client/code, :client/name, :client/locations; client associations on users
OAuth Mock Mock Google token endpoint responses (success and failure); mock Google userinfo endpoint responses

Existing Tests to Preserve

  • None identified

Dependencies

  • Google OAuth 2.0 (authorization code exchange and userinfo retrieval)
  • Buddy JWT (token signing/unsigning with HS512)
  • Datomic (user lookup and creation)
  • Legacy SPA (login and needs-activation pages, no SSR tests)