Files
integreat/docs/testing/behaviors/auth.md
Bryce 6b5d33a32f feat(tests): implement integration and unit tests for auth, company, and ledger behaviors
- Auth: 30 tests (97 assertions) covering OAuth, sessions, JWT, impersonation, roles
- Company: 35 tests (92 assertions) covering profile, 1099, expense reports, permissions
- Ledger: 113 tests (148 assertions) covering grid, journal entries, import, reports
- Fix existing test failures in running_balance, insights, tx, plaid, graphql
- Fix InMemSolrClient to handle Solr query syntax properly
- Update behavior docs: auth (42 done), company (32 done), ledger (120 done)
- All 478 tests pass with 0 failures, 0 errors
2026-05-08 16:12:08 -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 [x]
1.4 It should fetch the user's Google profile using the access token Integration [x]
1.5 It should create a new user account when the user logs in for the first time Integration [x]
1.6 It should find the existing user account on subsequent logins Integration [x]
1.7 It should redirect to the original page after successful OAuth Integration [x]
1.8 It should redirect to the root page when no return URL is provided Integration [x]
1.9 It should establish a server-side session with user identity and version Integration [x]
1.10 It should pass the JWT token in the query string after successful OAuth Integration [x]
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 [x]
1.13 It should return 401 with error message when the OAuth code is missing Integration [x]
1.14 It should return 401 when the OAuth code is invalid or expired Integration SKIPPED
1.15 It should return 401 and log a warning when the Google network request fails Integration SKIPPED

Logout

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

Impersonation

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

Session Management

Authentication Gate Behaviors

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

Admin Gate Behaviors

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

Session Version Behaviors

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

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 [x]
7.2 It should compress the client list for admin users to fit in the JWT Unit [x]
7.3 It should compress the client list for read-only users to fit in the JWT Unit [x]
7.4 It should include a plain client list for regular users in the JWT Unit [x]
7.5 It should create API tokens with admin role and 1000-day expiration Unit [x]

Middleware Behaviors

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

Role-Based Behaviors

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

Security Behaviors

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

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)