ADR 035 Customer Authenticated Session and Account-Link Proof
Context
Section titled “Context”The legacy customer web restores login from CodeIgniter session keys and a
long-lived encrypted AUTH_TOKEN cookie, then compares USER_ID_PK with mutable
reservation rows inside booking, service-balance, and payment controllers. That
behavior is source evidence, not a target authority model. Copying it would make
cookie state, numeric database identifiers, and controller-specific ownership
checks competing sources of truth.
ADR-034 makes Supabase Auth the native identity owner and Member Management the mother-profile owner. Reservation creation and booking still need one explicit, reusable proof that the verified bearer subject maps to the active mother and member records being used by the workflow. Without that proof, each adapter can reconstruct a different actor from mutable rows.
Decision
Section titled “Decision”Resolve the session once through Member Management
Section titled “Resolve the session once through Member Management”application/member_management/features/customer_authenticated_session owns
CustomerAuthenticatedSessionProjection. Its query handler accepts only an
already verified Supabase Auth subject and resolves a
CustomerAccountLinkProof through a port. The projection contains the
authenticated subject, mother account/profile identity, member profile/member
identity, and profile revision required by downstream orchestration.
The bearer token remains transport input. It is verified by the existing HTTP Auth boundary and is never persisted in this projection, command fingerprint, audit, log, or browser evidence. The browser cannot submit internal profile, member, role, account, or revision identifiers as actor authority.
Keep account-link proof backend-owned and fail closed
Section titled “Keep account-link proof backend-owned and fail closed”The Supabase adapter reads one exact account link through
sanmopia_get_customer_account_link_proof(text). The database function:
- joins the authenticated subject to exactly one mother profile and member profile;
- requires a complete profile and an active or authorization-pending account with an applied authorization relationship receipt;
- returns a closed six-field proof including the profile revision;
- denies
anonandauthenticatedexecution and grants onlyservice_role; and - rejects withdrawn or privacy-lifecycle accounts.
Missing, ambiguous, stale, cross-member, or privacy-ineligible rows are authority failures. No adapter may fall back to a cookie, caller-supplied member id, role, hidden field, Korean status string, or the first matching row.
Compose cross-context consumers in orchestration
Section titled “Compose cross-context consumers in orchestration”Reservation Operations does not import the Member Management adapter directly.
orchestration/reservation_operations/features/customer_reservation_creation
contains the bridge from the session projection to the existing reservation
actor contract. Booking identity resolution consumes the same account-link proof
port and rejects a member profile whose member_id differs from the proof.
This is a Layer-first boundary:
application/member_management/features/customer_authenticated_session← adapters/member_management/features/customer_authenticated_session← orchestration/reservation_operations/features/customer_reservation_creationCQRS is feature-local. This read-only feature has queries/ and no empty
commands/ directory.
Do not overclaim booking and payment completion
Section titled “Do not overclaim booking and payment completion”The payment pre-registration boundary resolves a typed PaymentActorContext
from the same account-link proof.
ADR-036 now supplies the durable
CustomerSessionRecoveryAuditEvent.
ADR-037 now freezes
the resolved reservation parties, accepted review lineage, and expected
new-booking revision with the booking transaction. Frontend session recovery
against the new audit runtime, provider payment, and production deployment are
not proved by these backend increments.
Consequences
Section titled “Consequences”- One Member Management feature is the SSOT for authenticated customer account-link resolution.
- P04 and booking reuse the same proof instead of rebuilding ownership in each adapter.
- Direct feature-to-feature implementation imports remain forbidden; the cross-context bridge stays in orchestration.
- Any future payment consumer must use an explicit application contract and cannot infer actor authority from reservation rows.
- P04 remains RED/partial until its separate legal, production-source, finalized-booking, readiness, and current browser gates close.
Implementation Evidence
Section titled “Implementation Evidence”Backend
01ce8a5742691118cebeb9b2c2d1cf92fb7e1639 implements the application query,
Supabase adapter, reservation orchestration bridge, booking identity reuse,
migration 20260718162556_customer_account_link_proof.sql, and Tach declarations.
A disposable database was advanced through the complete current migration chain
with the repository-owned Supabase CLI. The service-role RPC returned the exact
closed proof for fake test-owned rows; grant inspection returned
anon=false, authenticated=false, and service_role=true; changing the
account to withdrawal_pending failed closed with SQLSTATE 42501. The
container, image attempt, temporary build file, and Tach cache were removed
after verification.
Backend
b699f9b1376a0369f67fb8734c9e97416b41a575 adds the typed
PaymentActorContext. It carries the mother account, mother profile, member
profile, member, and profile revision from the exact account-link proof. Direct
mother payment no longer needs a mutable generic member_role row. Family and
operator payment paths also fail closed when the obligation’s mother account is
inactive. The immutable payment actor snapshot retains account/profile/revision
lineage in authority_evidence.
The resolver moved from
adapters/member_management/features/family_account to the owning
adapters/pricing_settlement/features/payment slice. It consumes Member
Management only through the account-link proof and family-account application
contracts. No schema migration was added for this increment.
The true full backend suite with the Tach cache disabled reports
6997 passed. Tach MCP’s cache-disabled affected suite reports
6696 passed / 279 deselected; exact dependency/interface/external gates report
zero diagnostics and the architecture suite reports 13 passed. Vulture at
confidence 100 reports six Pydantic-validator or Protocol-parameter false
positives, all retained after live-reference verification. No new Chromium,
screenshot, animated WebP, stage, or production deployment evidence was
produced by this backend-only increment.
Backend 7e2b86db9d21c8d483957f0888620b06b1eb03ad closes the
customer-session recovery-audit gap through ADR-036. Backend
35f67ff912d32c92e4772e60895bf1da45014437 closes the remaining
reservation-party/expected-revision gap through ADR-037. This ADR’s scoped
account-link consumers are now implemented; P04 remains RED for its separate
legal, source-completeness, readiness, and browser gates.
Revision History
Section titled “Revision History”- 2026-07-18: accepted the backend-owned customer session and account-link proof; payment, recovery-audit, and frozen reservation-party/revision binding were initially open.
- 2026-07-18: bound typed payment actor context and immutable payment lineage to the account-link proof; recovery audit and reservation-party/revision binding remain open.
- 2026-07-18: ADR-036 implemented the durable recovery audit; frozen reservation-party/expected-revision binding remains open.
- 2026-07-18: ADR-037 froze reservation parties and expected new-booking revision in the booking transaction; the scoped ADR-035 implementation moved to implemented.