fix: DPoP nonce handling, did:web resolution, PAR request_uri encoding (P1-2, P1-3, P1-4, P2-3) #97
No reviewers
Labels
No labels
auth
bug
composer
correctness
database
durability
enhancement
feeds
interactions
notifications
performance
reliability
safety
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
starsetbyte/peregrine!97
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/p1-oauth-did-correctness"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes P1-2, P1-3, P1-4, P2-3 from the 2026-07-11 adversarial delta review.
P1-3 / P1-4 — did:web resolution + DID document caching (commit
5e4efff)HandleResolver.buildWebDidUrlnow does spec-compliantdid:webURL construction (path DIDs no longer append/.well-known/did.jsonafter the path; percent-encoded ports are decoded and validated viaHttpUrl).forceRefresh;refreshSessionforce-revalidates so a PDS migration/rotation is no longer ignored until process death.P1-2 / P2-3 — DPoP nonce retries + PAR request_uri encoding (commits d3cffd0..6fddb50)
use { }) before retrying, and refresh no longer recurses unbounded — one explicit bounded retry.@Ignored nonce-retry tests are unignored and now assert exactly two requests, nonce presence on the retry, and response closure.request_urinow go throughHttpUrl.Builder.addQueryParameterinstead of string concatenation, so reserved characters in the request_uri can't change query meaning.Two trivial nits from task review (unused
Ignoreimport, KDoc indentation) fixed in0408422.Gate:
./gradlew testDebugUnitTest lintDebug— 0 failures, 0 lint errors.Merge order: this is branch A of four (A → B → C → D per the review's dependency note — DM Phase A work should not proceed until A and C are merged).
Review pass 1 — revise
P1 — successful DPoP responses discard the server nonce.
executeWithNonceDetection()returns only the body on success (AtProtoOAuthClient.kt:365-375), so the PAR nonce is not carried intoPendingAuthand the first token request is constructed astokenRequest(null)(:154-168,:229-243). The AT Protocol OAuth profile requires mandatory server-issued nonce tracking. Preserve the successful response header, use the PAR nonce for the first token exchange, persist rotated token/refresh nonces, and add success-path/missing-header tests.P1 — did:web documents are consumed without binding
doc.idto the requested DID.HandleResolver.fetchDidDocument()parses and caches any successful JSON document (HandleResolver.kt:220-237), then resolves its PDS service. Reject missing/mismatchedidbefore caching or reading services; add negative MockWebServer coverage.P2 — reject IP-literal did:web authorities. The current
buildWebDidUrl()accepts them throughHttpUrl.Builder.host()(:261-276), while the did:web specification prohibits IP addresses.Targeted OAuth/HandleResolver unit suites compile and pass locally, but they do not exercise these required cases.
Merged to main (
87933e8)Review pass 2 — remediation verified
5906047)Tests: AtProtoOAuthClientTest, HandleResolverTest, PendingAuthStoreTest all pass.
Content from
fix/p1-oauth-did-correctnessshipped tomainvia a local--no-ffmerge, which is why Forgejo shows this closed-unmerged rather than merged. Reconciled indocs/reviews/2026-07-25-branch-pr-reconciliation.md, which landed onmainvia #103. Branch deleted as part of that reconciliation.Pull request closed