128 lines
7.1 KiB
Markdown
128 lines
7.1 KiB
Markdown
---
|
|
phase: 08-oauth2-1-authorization-server
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- wristband/server.go
|
|
- wristband/stores.go
|
|
- wristband/crypto.go
|
|
- wristband/register.go
|
|
- wristband/registration_test.go
|
|
- scripts/check-phase8-red.sh
|
|
autonomous: true
|
|
requirements: [AUTH-05, AUTH-06]
|
|
must_haves:
|
|
truths:
|
|
- "D-01: The app-agnostic standard-library wristband package serves exact RFC 8414 metadata and validates RFC 7591 registration without zitadel/oidc."
|
|
- "D-06: PHP-minimal response shapes and configurable metadata fields are wristband defaults with no response hooks."
|
|
- "D-02: Registration is JSON-only, and D-21: its body is bounded at 64 KiB before decoding with endpoint-native errors."
|
|
- "D-04: Client-secret checks use constant-time fixed transforms and DCR cap/sweep behavior is deterministic under concurrency."
|
|
artifacts:
|
|
- path: "wristband/server.go"
|
|
provides: "Options, exact metadata writer, and app-agnostic server contract"
|
|
- path: "wristband/register.go"
|
|
provides: "RFC 7591 validation, issuance, cap, sweep, and bounded handler"
|
|
- path: "scripts/check-phase8-red.sh"
|
|
provides: "Fail-closed RED verifier rejecting syntax/setup/missing-test failures"
|
|
key_links:
|
|
- from: "wristband/register.go"
|
|
to: "wristband.Backend.WithinTx"
|
|
via: "serialized sweep, cap check, and create"
|
|
pattern: "WithinTx"
|
|
---
|
|
|
|
<objective>
|
|
Define and implement the framework-only discovery and dynamic-registration contract before app persistence or routing.
|
|
|
|
Purpose: Keep D-01's protocol engine small and app-agnostic while making the RED phase executable and diagnostic.
|
|
Output: `wristband` metadata/DCR contracts, deterministic tests, crypto helpers, and the shared RED verifier.
|
|
</objective>
|
|
|
|
## Phase Goal
|
|
|
|
**As a** connector implementer, **I want to** exercise discovery and registration against a deterministic OAuth engine, **so that** the app adapter can persist and mount an already proven wire contract.
|
|
|
|
<execution_context>
|
|
@/home/jin/.codex/get-shit-done/workflows/execute-plan.md
|
|
@/home/jin/.codex/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.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-PATTERNS.md
|
|
@.planning/phases/08-oauth2-1-authorization-server/08-VALIDATION.md
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Create compiling RED discovery and registration contracts</name>
|
|
<files>wristband/server.go, wristband/stores.go, wristband/registration_test.go, scripts/check-phase8-red.sh</files>
|
|
<behavior>
|
|
- Metadata is the exact unwrapped 11-field document with recorded order, content type, and cache header.
|
|
- Public and confidential DCR validate PHP-compatible URI, grant, response, auth-method, cap, sweep, and 65,536-byte rules.
|
|
- Test failures use `PHASE8_RED:registration` only for missing behavior; syntax, build, setup, missing-test, panic, and unrelated failures are rejected.
|
|
</behavior>
|
|
<action>D-06: define the exported options, typed records, transaction-scoped Backend/Tx contracts, handler signatures, and deterministic clock/random seams with compiling stubs. D-18: add behavior tests that compile and intentionally fail through `PHASE8_RED:registration` assertions. Create `scripts/check-phase8-red.sh` to run the supplied command, require a nonzero result and the requested marker, and fail if output contains `build failed`, `setup failed`, `syntax error`, `no tests to run`, `no test files`, or a panic. Include named T-08-DCR-FLOOD, T-08-SECRET-TIMING, and T-08-REQUEST-LEAK cases. Commit RED separately.</action>
|
|
<verify>
|
|
<automated>scripts/check-phase8-red.sh registration go test ./wristband -run 'Test(Metadata|Register|Registration)' -count=1</automated>
|
|
</verify>
|
|
<done>The tests compile, the named tests execute, and the verifier accepts only the expected missing-behavior RED marker.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Implement exact metadata, DCR, bounds, and cryptography</name>
|
|
<files>wristband/server.go, wristband/stores.go, wristband/crypto.go, wristband/register.go, wristband/registration_test.go</files>
|
|
<behavior>
|
|
- Fixed SHA-256 transforms use `crypto/subtle.ConstantTimeCompare`; raw client secrets are returned once and never persisted/logged.
|
|
- Sweep, cap check, and create occur within one backend transaction; concurrent registrations cannot cross the cap.
|
|
- Oversized and malformed registration input returns exact `invalid_client_metadata` bytes without a house envelope.
|
|
</behavior>
|
|
<action>D-01: use only `crypto/rand`, `crypto/sha256`, `crypto/subtle`, `encoding/base64`, `encoding/json`, `net/http`, and `net/url`; add no dependency. D-03: model configurable TTLs, cap 200, stale age 24h, issuer/resource/endpoints, and `RegisterMaxBytes: 65536`. D-05: keep all protocol rules in wristband and import no fonoteka/GORM code. D-06: use a local no-newline exact JSON writer with no response hooks. D-07: use only the transaction-scoped interfaces. D-17: expose expired-row and unconsented-client sweep operations without timers/goroutines. D-21: apply `http.MaxBytesReader` before JSON decode. Strip control characters and cap names at 120 characters.</action>
|
|
<verify>
|
|
<automated>go test ./wristband -run 'Test(Metadata|Register|Registration)' -count=1</automated>
|
|
</verify>
|
|
<done>Framework discovery and DCR tests pass with exact bytes, atomic cap behavior, bounded decoding, hash-only persistence, and no app-tier imports.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| Connector → wristband | Untrusted metadata/DCR requests cross into protocol parsing. |
|
|
| wristband → Backend | Protocol state crosses into an app-provided transaction. |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|-------------|-----------------|
|
|
| T-08-DCR-FLOOD | Denial of Service | register handler/store | mitigate | 64 KiB cap, serialized client cap, stale sweep, concurrency tests. |
|
|
| T-08-SECRET-TIMING | Information Disclosure | crypto/client secret | mitigate | Fixed SHA-256 transforms and `subtle.ConstantTimeCompare`. |
|
|
| T-08-REQUEST-LEAK | Information Disclosure | handler/tests | mitigate | Hash-only records and no sensitive-value logging. |
|
|
| T-08-SC | Tampering | dependencies | mitigate | No package install; stdlib-only import audit. |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- `go test ./wristband -count=1`
|
|
- `go list -deps ./wristband | rg 'fonoteka|gorm.io'` returns no matches.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Exact metadata and DCR behavior is green in a self-contained framework package.
|
|
- RED verification cannot pass on mere file presence, compile errors, missing tests, or unrelated failures.
|
|
- DCR is bounded, concurrency-safe, and secret-safe before app integration.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/08-oauth2-1-authorization-server/08-01-SUMMARY.md` when done.
|
|
</output>
|