WithinTx callback whose methods all use callback *gorm.DB
WithinTx
Make the existing Postgres schema and app store faithfully represent wristband's client and pending-request state.
Purpose: Separate persistence correctness from protocol and route wiring so nullability, indexes, locking, cap serialization, and sweep semantics are independently verifiable.
Output: Corrected models, additive migration, GORM backend, and real-Postgres tests.
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08-oauth2-1-authorization-server/08-CONTEXT.md
@.planning/phases/08-oauth2-1-authorization-server/08-RESEARCH.md
@.planning/phases/08-oauth2-1-authorization-server/08-01-SUMMARY.md
Task 1: Specify schema and store behavior in compiling RED tests
../fonoteka.go/plugins/golem15/fonoteka/updates/oauth_schema_correction_test.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store_test.go
- Public client secret, pending request id/code hash/user id nullability, named indexes, and rollback refusal are proven on real Postgres.
- Two pending requests coexist; atomic cap/sweep/create cannot exceed the configured cap under contention.
- Failures emit `PHASE8_RED:persistence` only for absent persistence behavior.
D-18: add real-Postgres tests using the existing auth TestMain harness. Compile them against the interfaces from 08-01; use `PHASE8_RED:persistence` assertions for intentionally missing migration/store behavior, and do not use undefined symbols as RED. Include T-08-DCR-FLOOD and transaction-handle tests that detect accidental use of the outer DB.
scripts/check-phase8-red.sh persistence bash -lc "cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/classes/auth ./plugins/golem15/fonoteka/updates -run 'TestOAuth(Schema|Store|RegistrationCap)' -count=1"
The real-Postgres RED suite compiles, runs named tests, and fails only through the persistence marker.
Task 2: Correct OAuth schema and implement the transaction-scoped store
../fonoteka.go/plugins/golem15/fonoteka/models/oauth_client.go, ../fonoteka.go/plugins/golem15/fonoteka/models/oauth_auth_code.go, ../fonoteka.go/plugins/golem15/fonoteka/updates/12_oauth_schema_correction.go, ../fonoteka.go/plugins/golem15/fonoteka/updates/oauth_schema_correction_test.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store.go, ../fonoteka.go/plugins/golem15/fonoteka/classes/auth/oauth_store_test.go
- Four lifecycle fields are pointers and database nullable; PHP-equivalent operational indexes exist.
- Down migration refuses when null lifecycle rows would be lost.
- Every store mutation uses the callback transaction and app-tier `FOR UPDATE` where required.
D-07: correct `client_secret_hash`, `request_id`, `code_hash`, and `user_id` model fields to pointers and implement the wristband Backend/Tx adapter without importing GORM into wristband. Add a new gormigrate step rather than editing applied history; drop four NOT NULL constraints, create named indexes idempotently, and fail rollback if null rows exist. Implement atomic DCR sweep/cap/create, exact expired-row sweep, and row-lock-capable lifecycle methods using only the callback `*gorm.DB`. D-17: retain unexpired rotated/revoked refresh rows.
cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/classes/auth ./plugins/golem15/fonoteka/updates -run 'TestOAuth(Schema|Store|RegistrationCap|Sweep)' -count=1
Postgres can persist public clients and concurrent pending requests, exposes required indexes, serializes DCR cap enforcement, and sweeps only expired rows.
<threat_model>
Trust Boundaries
Boundary
Description
wristband records → GORM
App-agnostic state crosses into persistent rows and locks.
STRIDE Threat Register
Threat ID
Category
Component
Disposition
Mitigation Plan
T-08-DCR-FLOOD
Denial of Service
client store
mitigate
Transactionally serialized cap/sweep/create with contention test.
T-08-CODE-REPLAY
Spoofing
auth-code store
mitigate
App-tier row-lock methods and single transaction handle.
T-08-REFRESH-REPLAY
Spoofing/Elevation
refresh store
mitigate
Preserve replay evidence until expiry and expose locked traversal.