Story AUTH-008: BYO-DB readiness — cross-DB agency-scope write validation¶
Module: identity
Slice: arch §0.1 AUTH-004 (BYO branch) + arch §4.3 (agency scope validation)
Side: [BACKEND]
Version target: V0 — v0.0.4
Priority: 8
Depends on: AUTH-003; TENANT TenantAwareDataSourceResolver BYO branch (anchor D2)
Can develop concurrently with: AUTH-009
Merge order: After AUTH-003
Estimated complexity: S
PRD User Stories: AUTH-OP-09 (role/scope), R-ADM-009 / AUTH-TV-07 (cross-DB agency-scope integrity)
Wireframe: N/A — backend only
Objective¶
v0.0.4 introduces BYO-DB tenants (enterprise customers who host their business data in their own PostgreSQL). AUTH itself does NOT move — all auth tables stay on the platform DB (ADR-AUTH-01). What AUTH must prove here is that when an operator's agency_scope is set, each agency UUID is validated against the correct database (the tenant's own DB if BYO, the shared DB otherwise) via TenantConfigService, and that AUTH's own auth queries keep working unchanged on the platform DB regardless of the tenant's DB profile.
Backend Scope¶
Entities¶
Reads users.agency_scope. No new tables. No AUTH table moves to BYO (ADR-AUTH-01).
Service Layer¶
- Agency-scope write validation (R-ADM-009 / AUTH-TV-07, arch §4.3): when
agency_scopeis assigned or modified for an OPERATOR, resolve each agency UUID throughTenantConfigService.resolveAgency(tenantId, agencyId). This call transparently routes to the tenant's BYO DB or the shared tenant DB through theTenantAwareDataSourceResolver. Reject any UUID that does not exist or whosestatus != ACTIVE→InvalidAgencyScopeException(HTTP 422). - Write-time only: AUTH does NOT re-validate
agency_scopeon each token refresh in V1 (AUTH-TV-07). - Confirm AUTH's own reads/writes (login, signed-link verify, user lookup) always use
platformJdbcClientand are unaffected by the tenant's DB profile (arch §4.2).
API Endpoints¶
None new. This validation runs inside the user create/edit path (the operator user-management API is V1; in v0.0.4 the validation primitive must exist and be exercised by the bootstrap/admin path that sets scope).
Validation Rules¶
- Every agency UUID in a scope assignment must exist AND be ACTIVE in the resolving tenant DB.
- Validation occurs at write time; not on refresh.
- A disabled agency already present in a scope array is NOT mutated by AUTH (R-OP-018 handled elsewhere); this story only validates new/changed assignments.
Multi-Tenant Considerations¶
This is the core of the story. agency_scope (on the platform-DB users row) references agency.id which lives in the tenant DB (shared or BYO). There is no DDL FK across DBs (arch §2.1); integrity is enforced here at the application layer. The validation must produce the same outcome for a shared-DB tenant and a BYO-DB tenant — the only difference is which datasource TenantConfigService routes to. AUTH stays on the platform DB throughout.
Audit & Logging¶
operator_role_changed(when scope changes accompany a role/scope edit) —target_user_id,old_agency_scope,new_agency_scope(this event is fully exercised in the V1 user-management story; in v0.0.4 only the validation primitive is required).
Frontend Scope¶
N/A — backend only.
UI States (ALL REQUIRED)¶
N/A — backend only.
Acceptance Criteria¶
AC-001: Unknown or inactive agency UUID is rejected
Given a BYO-DB tenant
When an agency_scope assignment references a UUID that does not exist or whose status != ACTIVE in the tenant's DB
Then the write is rejected with 422 InvalidAgencyScope
AC-002: Valid agency UUID is accepted on both DB profiles
Given a valid ACTIVE agency UUID
When agency_scope is assigned for an OPERATOR
Then the write succeeds for a shared-DB tenant AND for a BYO-DB tenant (TenantConfigService routes to the correct DB)
AC-003: AUTH auth queries stay on the platform DB
Given a BYO-DB tenant
When the operator logs in and a signed link is verified
Then all AUTH reads/writes use the platform datasource (no BYO routing for auth primitives)
And login + verification behave identically to a shared-DB tenant
Compliance Rules¶
- No new compliance article. Data-separation posture (auth data under platform custody) supports the DPA positioning (ADR-AUTH-01).
Standards & Conventions¶
docs/standards/multi-datasource.md(TenantAwareDataSourceResolver, routing).docs/standards/multi-tenant-model.md(DB profile taxonomy SHARED / BYO).docs/standards/java-spring-guidelines.md,docs/standards/critical-rules.md(tenant isolation).
Testing Requirements¶
Unit Tests¶
- InvalidAgencyScopeException thrown for unknown / inactive agency UUID.
- Validation is write-time only (no call on token refresh).
Integration Tests¶
- Two-profile test: same scope assignment validated against a shared-DB tenant and a BYO-DB tenant (Testcontainers second Postgres for BYO), both behaving correctly.
- Login + signed-link verify for a BYO-DB tenant use only the platform datasource (assert no BYO query on the auth hot path).
What QA Will Validate¶
- Agency-scope assignments are validated against the right DB; bad UUIDs are rejected; auth itself never touches the BYO DB; behaviour is identical across DB profiles.
Out of Scope¶
- The full operator user-management UI (invite / role edit / deactivate) — V1 (AUTH-ADM-01/02, AUTH-OP-07).
- Moving any AUTH table to a BYO DB — never (ADR-AUTH-01).
- Tenant SHARED→BYO migration — V2.
- The
TenantAwareDataSourceResolverBYO branch itself — owned by the TENANT module (anchor D2); this story consumes it.
Definition of Done¶
- [ ] Agency-scope write validation rejects unknown/inactive UUIDs (422)
- [ ] Validation works identically for shared-DB and BYO-DB tenants
- [ ] AUTH auth queries remain on the platform datasource for BYO tenants
- [ ] Validation is write-time only (not on refresh)
- [ ] Unit + integration tests written and passing (both DB profiles)
- [ ] Tenant isolation verified (platform DB for auth; cross-DB validation via TenantConfigService)
- [ ] No silent failures on TenantConfigService routing errors