refactor: debloat the auth and server layers #206

Merged
Grandiras merged 3 commits from issue-204-auth-server-debloat into main 2026-10-05 12:40:09 +00:00
Owner

Closes #204.

A debloat of src/ATProtoNet/Auth and src/ATProtoNet.Server (outside Spaces/), with no behaviour or public API change (PublicAPI.*.txt untouched): net −203 lines across 28 files. A duplicate scan and a dead-member scan found nothing real to remove, so this is mostly owner-style compression.

  • Braces. About 75 blocks with a single guard statement lose their braces. No condition or check changed.
  • Forwarding fields become get-only internal properties (OAuthClient.Discovery/HttpClient, RefreshCoordinator, SessionManager.Store).
  • Primary constructors, on internal types only (AtProtoServiceAuthHandler, XrpcEndpointRegistration, KeyedLock<T>.Lease).
  • OAuthClient form callbacks. The five () => { form; AddClientAuthentication; return form; } lambdas are gone, and PostFormWithDpopAsync takes (form, clientKey, audience). AddClientAuthentication runs at the top of each attempt and overwrites client_id/client_assertion_type/client_assertion, so the use_dpop_nonce retry still sends a fresh assertion with a new jti, as before. The DPoP proof is still generated per attempt with the current nonce.
  • Unused usings removed.

Security-adjacent spots to review are the guard brace removals in ServiceAuthVerifier, SessionManager, AtProtoClientFactory, DPoPNonceCache and AtProtoSessionJson, plus the OAuthClient form change above.

Build has 0 warnings; 4964 tests pass.

🤖 Generated with Claude Code

Closes #204. A debloat of `src/ATProtoNet/Auth` and `src/ATProtoNet.Server` (outside `Spaces/`), with no behaviour or public API change (`PublicAPI.*.txt` untouched): net −203 lines across 28 files. A duplicate scan and a dead-member scan found nothing real to remove, so this is mostly owner-style compression. - **Braces.** About 75 blocks with a single guard statement lose their braces. No condition or check changed. - **Forwarding fields** become get-only internal properties (`OAuthClient.Discovery`/`HttpClient`, `RefreshCoordinator`, `SessionManager.Store`). - **Primary constructors**, on internal types only (`AtProtoServiceAuthHandler`, `XrpcEndpointRegistration`, `KeyedLock<T>.Lease`). - **`OAuthClient` form callbacks.** The five `() => { form; AddClientAuthentication; return form; }` lambdas are gone, and `PostFormWithDpopAsync` takes `(form, clientKey, audience)`. `AddClientAuthentication` runs at the top of *each attempt* and overwrites `client_id`/`client_assertion_type`/`client_assertion`, so the `use_dpop_nonce` retry still sends a fresh assertion with a new jti, as before. The DPoP proof is still generated per attempt with the current nonce. - **Unused usings** removed. Security-adjacent spots to review are the guard brace removals in `ServiceAuthVerifier`, `SessionManager`, `AtProtoClientFactory`, `DPoPNonceCache` and `AtProtoSessionJson`, plus the `OAuthClient` form change above. Build has 0 warnings; 4964 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
refactor: debloat the auth and server layers (closes #204)
All checks were successful
CI / pds-integration (pull_request) Successful in 44s
CI / build-and-test (pull_request) Successful in 1m14s
2da7172c0b
Drop braces around single-statement bodies, replace forwarding fields with
get-only properties, use primary constructors on internal types, and pass the
OAuth form fields to the DPoP POST helper instead of a builder callback.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-52-33-370Z.md) — updating every ~5 minutes while the review runs.
opencode-reviewer approved these changes 2026-10-05 02:55:51 +00:00
Dismissed
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

This is safe to merge. Verified the security-adjacent changes specifically:

  • OAuth form/callback change: AddClientAuthentication now runs at the top of every attempt in SendFormWithDpopAsync, overwriting client_id, client_assertion_type and client_assertion before the request is built each iteration — so the use_dpop_nonce retry still sends a fresh assertion with a new jti, matching the old per-attempt buildForm() behavior. All five call sites pass the same client key and issuer the inline lambdas did. The DPoP proof is still generated per attempt with the current nonce.
  • Primary constructors (AtProtoServiceAuthHandler, XrpcEndpointRegistration, KeyedLock<T>.Lease): the AuthenticationHandler base invocation matches the old one; the XrpcEndpointRegistration parameters only feed property initializers so nothing is captured long-lived; the Lease does exactly the old entry.Semaphore.Release(); owner.Forget(key, entry) with the same dispose-once guard.
  • Forwarding fields → get-only properties (OAuthClient.Discovery/HttpClient, RefreshCoordinator, SessionManager.Store): assigned once in construction and used read-only throughout; all former _store/_refreshCoordinator/_discovery/_httpClient references were rewritten consistently, including the Store! path that relies on the same non-null invariant as before.
  • Guard brace removals in ServiceAuthVerifier, SessionManager, AtProtoClientFactory, DPoPNonceCache, AtProtoSessionJson and the Server layer: each was a single-statement block (including the conditional throw expression inside return); the await using lease scopes in SessionManager and the leaseHeld if/else remain correct.
  • Removed usings confirmed unused (Microsoft.Extensions.Hosting in AtProtoBuilderExtensions, Microsoft.Extensions.DependencyInjection.Extensions in AtProtoServiceAuthentication).

No behavior or public API change detected; PublicAPI.*.txt untouched as claimed.


⏱ 3m 17s · 9 steps · $0.0147 · tokens: 53.7k in / 1.7k out (+143.7k cache-read) · Go: 5h 1% · wk 0% · mo 91%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. This is safe to merge. Verified the security-adjacent changes specifically: - **OAuth form/callback change**: `AddClientAuthentication` now runs at the top of every attempt in `SendFormWithDpopAsync`, overwriting `client_id`, `client_assertion_type` and `client_assertion` before the request is built each iteration — so the `use_dpop_nonce` retry still sends a fresh assertion with a new jti, matching the old per-attempt `buildForm()` behavior. All five call sites pass the same client key and issuer the inline lambdas did. The DPoP proof is still generated per attempt with the current nonce. - **Primary constructors** (`AtProtoServiceAuthHandler`, `XrpcEndpointRegistration`, `KeyedLock<T>.Lease`): the `AuthenticationHandler` base invocation matches the old one; the `XrpcEndpointRegistration` parameters only feed property initializers so nothing is captured long-lived; the `Lease` does exactly the old `entry.Semaphore.Release(); owner.Forget(key, entry)` with the same dispose-once guard. - **Forwarding fields → get-only properties** (`OAuthClient.Discovery`/`HttpClient`, `RefreshCoordinator`, `SessionManager.Store`): assigned once in construction and used read-only throughout; all former `_store`/`_refreshCoordinator`/`_discovery`/`_httpClient` references were rewritten consistently, including the `Store!` path that relies on the same non-null invariant as before. - **Guard brace removals** in `ServiceAuthVerifier`, `SessionManager`, `AtProtoClientFactory`, `DPoPNonceCache`, `AtProtoSessionJson` and the Server layer: each was a single-statement block (including the conditional `throw` expression inside `return`); the `await using` lease scopes in `SessionManager` and the `leaseHeld` if/else remain correct. - **Removed usings** confirmed unused (`Microsoft.Extensions.Hosting` in `AtProtoBuilderExtensions`, `Microsoft.Extensions.DependencyInjection.Extensions` in `AtProtoServiceAuthentication`). No behavior or public API change detected; `PublicAPI.*.txt` untouched as claimed. --- ⏱ 3m 17s · 9 steps · $0.0147 · tokens: 53.7k in / 1.7k out (+143.7k cache-read) · Go: 5h 1% · wk 0% · mo 91% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-52-33-370Z.md) <!-- opencode-reviewed-head:2da7172c0b7618a6c0e6023d00a692b9eb666b7e --> <!-- opencode-verdict:approve -->
Collaborator

🤖 Resolving the merge conflicts — the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the fix runs.

🤖 Resolving the merge conflicts — the reply will link the full session transcript. <!-- opencode-run:conflict --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-55-51-621Z.md) — updating every ~5 minutes while the fix runs.
Collaborator

Merge committed. ✅

Conflict: CHANGELOG.md only — both sides had added an entry under the ## [Unreleased] → Changed section: main's "Leaner Lexicon clients" bullet (#196) and this branch's "Leaner auth and server layers" bullet (#204).

Resolution: kept both bullets in the unreleased section.

Verification: full solution builds with 0 errors/0 warnings, and the unit test suite passes (4964 passed, 0 failed). No conflict markers remain; the merge commit is in place and not pushed.

→ Merge resolved and pushed; a re-review follows.


⏱ 1m 20s · 8 steps · $0.0064 · tokens: 24.8k in / 703 out (+66.9k cache-read) · Go: 5h 1% · wk 0% · mo 91%

📜 Full session transcript

Merge committed. ✅ **Conflict:** `CHANGELOG.md` only — both sides had added an entry under the `## [Unreleased] → Changed` section: main's "Leaner Lexicon clients" bullet (#196) and this branch's "Leaner auth and server layers" bullet (#204). **Resolution:** kept both bullets in the unreleased section. **Verification:** full solution builds with 0 errors/0 warnings, and the unit test suite passes (4964 passed, 0 failed). No conflict markers remain; the merge commit is in place and not pushed. → Merge resolved and pushed; a re-review follows. --- ⏱ 1m 20s · 8 steps · $0.0064 · tokens: 24.8k in / 703 out (+66.9k cache-read) · Go: 5h 1% · wk 0% · mo 91% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-55-51-621Z.md) <!-- opencode-session:ses_ef602c456ffelKWYurmbZhCJgC -->
Merge remote-tracking branch 'origin/main' into issue-204-auth-server-debloat
All checks were successful
CI / pds-integration (pull_request) Successful in 37s
CI / build-and-test (pull_request) Successful in 2m25s
6106155969
# Conflicts:
#	CHANGELOG.md
Author
Owner

Independent adversarial review (read-only Sonnet reviewer): no defects found. Every brace removal covers a single-statement body, and no else binding changed. AddClientAuthentication overwrites through the indexer at the top of each attempt, so every attempt, including the use_dpop_nonce retry, gets a fresh assertion (no jti reuse). PAR fields, DPoP/nonce handling and the ServiceAuthVerifier checks are unchanged. Unit suite: 4964 passed.

Independent adversarial review (read-only Sonnet reviewer): **no defects found**. Every brace removal covers a single-statement body, and no `else` binding changed. `AddClientAuthentication` overwrites through the indexer at the top of each attempt, so every attempt, including the `use_dpop_nonce` retry, gets a fresh assertion (no jti reuse). PAR fields, DPoP/nonce handling and the `ServiceAuthVerifier` checks are unchanged. Unit suite: 4964 passed.
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-58-19-543Z.md) — updating every ~5 minutes while the review runs.
opencode-reviewer left a comment

💬 Verdict: comment — the reviewer did not submit a structured review; the raw output follows.

The workspace matches the PR head. Let me verify the security-adjacent spots in their current state, since new commits have landed since my last pass.


⏱ 30m 49s · 6 steps · $0.0131 · tokens: 72.5k in / 385 out (+58.8k cache-read)
📜 Full session transcript

💬 **Verdict: comment** — the reviewer did not submit a structured review; the raw output follows. The workspace matches the PR head. Let me verify the security-adjacent spots in their current state, since new commits have landed since my last pass. --- ⏱ 30m 49s · 6 steps · $0.0131 · tokens: 72.5k in / 385 out (+58.8k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T02-58-19-543Z.md)
Author
Owner

@claude /review

@claude /review
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/731); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-731.md) — updating every ~5 minutes while the task runs.
claude-reviewer approved these changes 2026-10-05 11:50:04 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

This is a behaviour-preserving refactor, and I found no problems.

  • OAuthClient form change: AddClientAuthentication sets client_id, client_assertion_type and client_assertion by indexer. Calling it at the top of each attempt on the shared dictionary therefore overwrites the old values, and the use_dpop_nonce retry still gets a fresh assertion. The DPoP proof is still generated per attempt. The dictionary is not reused across calls, so there is no cross-request leakage.
  • Brace removals: I checked the security-adjacent ones in ServiceAuthVerifier, SessionManager, AtProtoClientFactory, DPoPNonceCache and AtProtoSessionJson. Each guard still covers a single statement (a throw, return or continue). The nested foreach/if and if/else cases without braces also bind correctly.
  • Properties and constructors: The get-only properties and primary constructors keep the same semantics. The XrpcEndpointRegistration primary-constructor properties keep the private-constructor intent, because the type is internal.

Minor, non-blocking style point: the unbraced foreach with a nested if that spans several lines (for example in AtProtoServiceAuthOptions.Validate and OAuthClient) is harder to read and easier to break later. Braces on those would be safer.


🧠 claude-sonnet-5-5 · ⏱ 0m 10s · 3 turns · tokens: 41.3k in / 877 out (+64.8k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. This is a behaviour-preserving refactor, and I found no problems. - **OAuthClient form change:** `AddClientAuthentication` sets `client_id`, `client_assertion_type` and `client_assertion` by indexer. Calling it at the top of each attempt on the shared dictionary therefore overwrites the old values, and the `use_dpop_nonce` retry still gets a fresh assertion. The DPoP proof is still generated per attempt. The dictionary is not reused across calls, so there is no cross-request leakage. - **Brace removals:** I checked the security-adjacent ones in `ServiceAuthVerifier`, `SessionManager`, `AtProtoClientFactory`, `DPoPNonceCache` and `AtProtoSessionJson`. Each guard still covers a single statement (a `throw`, `return` or `continue`). The nested `foreach`/`if` and `if`/`else` cases without braces also bind correctly. - **Properties and constructors:** The get-only properties and primary constructors keep the same semantics. The `XrpcEndpointRegistration` primary-constructor properties keep the private-constructor intent, because the type is internal. Minor, non-blocking style point: the unbraced `foreach` with a nested `if` that spans several lines (for example in `AtProtoServiceAuthOptions.Validate` and `OAuthClient`) is harder to read and easier to break later. Braces on those would be safer. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 10s · 3 turns · tokens: 41.3k in / 877 out (+64.8k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-731.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:610615596923ffe3a5bc86b9d9ab6f4e4b0c3e55 -->
Collaborator

🤖 Resolving the merge conflicts — the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the fix runs.

🤖 Resolving the merge conflicts — the reply will link the full session transcript. <!-- opencode-run:conflict --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T12-09-03-190Z.md) — updating every ~5 minutes while the fix runs.
Collaborator

The merge is committed (a3c80b31). Confirmed the merge base (0ba4a207) predates both the branch's auth/server debloat and main's HTTP/2 identity work; the earlier merge owner (61061559) resolved the previous CHANGELOG only on this branch, and main then landed #198 which had never seen it. The new conflict is again the [Unreleased] / Changed bullet list, and both entries belong in it.

Resolved CHANGELOG.md by keeping both bullets, Leaner auth and server layers first, then Identity fetches use HTTP/2. No other files conflicted; the staged identity-side files are fine as main landed them, since the branch side doesn't touch them.

Verified with a clean build of src/ATProtoNet (0 errors, 0 warnings) and the matching Identity test suite under tests/ATProtoNet.Tests (--filter FullyQualifiedName~Identity: 1084 tests, all passing).

→ Merge resolved and pushed; a re-review follows.


⏱ 26m 10s · 12 steps · $0.0194 · tokens: 95.4k in / 776 out (+133.6k cache-read) · Go: 5h 0% · wk 0% · mo 91%

📜 Full session transcript

The merge is committed (a3c80b31). Confirmed the merge base (0ba4a207) predates both the branch's auth/server debloat and main's HTTP/2 identity work; the earlier merge owner (61061559) resolved the previous CHANGELOG only on this branch, and main then landed #198 which had never seen it. The new conflict is again the `[Unreleased] / Changed` bullet list, and both entries belong in it. Resolved CHANGELOG.md by keeping both bullets, `Leaner auth and server layers` first, then `Identity fetches use HTTP/2`. No other files conflicted; the staged identity-side files are fine as main landed them, since the branch side doesn't touch them. Verified with a clean build of `src/ATProtoNet` (0 errors, 0 warnings) and the matching Identity test suite under `tests/ATProtoNet.Tests` (--filter FullyQualifiedName~Identity: 1084 tests, all passing). → Merge resolved and pushed; a re-review follows. --- ⏱ 26m 10s · 12 steps · $0.0194 · tokens: 95.4k in / 776 out (+133.6k cache-read) · Go: 5h 0% · wk 0% · mo 91% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T12-09-03-190Z.md) <!-- opencode-session:ses_ef4084fceffeKCLAelsMMyfMbo -->
Merge remote-tracking branch 'origin/main' into issue-204-auth-server-debloat
All checks were successful
CI / pds-integration (pull_request) Successful in 27s
CI / build-and-test (pull_request) Successful in 54s
Sync Closures to GitHub / sync-closure (pull_request) Successful in 8s
a3c80b3195
# Conflicts:
#	CHANGELOG.md
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T12-36-13-700Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/736); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-736.md) — updating every ~5 minutes while the task runs.
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

The refactor holds up, and I have no blocking issues. I reviewed the diff only; I did not build it or run the tests.

My earlier pass was an approval with one non-blocking style note, so there was nothing to confirm as resolved.

  • OAuthClient form change: AddClientAuthentication now runs at the top of each attempt in SendFormWithDpopAsync. It overwrites client_id, client_assertion_type and client_assertion by indexer, so the use_dpop_nonce retry still sends a fresh assertion with a new jti. The DPoP proof is still generated per attempt with the current nonce. Each call builds its own dictionary, so nothing carries over between requests.
  • Brace removals: I checked the security-adjacent ones in ServiceAuthVerifier, SessionManager, AtProtoClientFactory, DPoPNonceCache and AtProtoSessionJson. Each guard still covers exactly one statement. The nested foreach/if and if/else cases without braces also bind as before.
  • Primary constructors and get-only properties: AtProtoServiceAuthHandler, XrpcEndpointRegistration and KeyedLock<T>.Lease keep the same behaviour. The type-level access is unchanged and the public API files are untouched.

Non-blocking, repeated from last time: the unbraced foreach with a nested multi-line if/throw is easy to break with a later edit. Examples are AtProtoServiceAuthOptions.Validate, ServiceAuthVerifierOptions and OAuthClient.ValidateClientKeys. Keeping braces on those would be safer, but it need not hold up the merge.


🧠 claude-sonnet-5-5 · ⏱ 0m 16s · 3 turns · tokens: 42.2k in / 1.6k out (+65.6k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. The refactor holds up, and I have no blocking issues. I reviewed the diff only; I did not build it or run the tests. My earlier pass was an approval with one non-blocking style note, so there was nothing to confirm as resolved. - **`OAuthClient` form change:** `AddClientAuthentication` now runs at the top of each attempt in `SendFormWithDpopAsync`. It overwrites `client_id`, `client_assertion_type` and `client_assertion` by indexer, so the `use_dpop_nonce` retry still sends a fresh assertion with a new jti. The DPoP proof is still generated per attempt with the current nonce. Each call builds its own dictionary, so nothing carries over between requests. - **Brace removals:** I checked the security-adjacent ones in `ServiceAuthVerifier`, `SessionManager`, `AtProtoClientFactory`, `DPoPNonceCache` and `AtProtoSessionJson`. Each guard still covers exactly one statement. The nested `foreach`/`if` and `if`/`else` cases without braces also bind as before. - **Primary constructors and get-only properties:** `AtProtoServiceAuthHandler`, `XrpcEndpointRegistration` and `KeyedLock<T>.Lease` keep the same behaviour. The type-level access is unchanged and the public API files are untouched. **Non-blocking, repeated from last time:** the unbraced `foreach` with a nested multi-line `if`/`throw` is easy to break with a later edit. Examples are `AtProtoServiceAuthOptions.Validate`, `ServiceAuthVerifierOptions` and `OAuthClient.ValidateClientKeys`. Keeping braces on those would be safer, but it need not hold up the merge. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 16s · 3 turns · tokens: 42.2k in / 1.6k out (+65.6k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-736.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:a3c80b3195cda2cee04d6088100a0ea0266708ff -->
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review of the current head: the behaviour-preserving refactor holds up and is safe to merge. Verified the security-adjacent spots specifically:

  • OAuth form change: AddClientAuthentication runs at the top of each attempt in SendFormWithDpopAsync, overwriting client_id, client_assertion_type and client_assertion, so the use_dpop_nonce retry still sends a fresh assertion with a new jti, exactly as the old per-attempt buildForm() lambdas did. All five call sites pass the same client key and issuer; the DPoP proof is still generated per attempt with the current nonce, and the dictionary is never reused across requests.
  • Guard brace removals in ServiceAuthVerifier (including the conditional return/throw in ReadKeyId), SessionManager (the await using lease scopes remain correctly scoped, including the nested two-condition guard), AtProtoClientFactory, DPoPNonceCache, AtProtoSessionJson and the Server layer: each was a single-statement block; no condition changed.
  • Primary constructors on internal types and the field→get-only property conversions (OAuthClient.Discovery/HttpClient, RefreshCoordinator, SessionManager.Store) preserve construction-time assignment and read-only use; the Store! path keeps the same non-null invariant as before.
  • Removed usings confirmed unused; PublicAPI.*.txt untouched as claimed, so no public API change.

Non-blocking style note: the multi-line unbraced foreach/nested if blocks (e.g. AtProtoServiceAuthOptions.Validate, OAuthClient.FindClientKey) are harder to scan and easier to break with a later edit; braces on those would be more robust, but that is a matter of taste and does not block merging.


⏱ 3m 33s · 5 steps · $0.0085 · tokens: 33.6k in / 1.0k out (+80.9k cache-read) · Go: 5h 0% · wk 0% · mo 91%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review of the current head: the behaviour-preserving refactor holds up and is safe to merge. Verified the security-adjacent spots specifically: - **OAuth form change**: `AddClientAuthentication` runs at the top of *each* attempt in `SendFormWithDpopAsync`, overwriting `client_id`, `client_assertion_type` and `client_assertion`, so the `use_dpop_nonce` retry still sends a fresh assertion with a new jti, exactly as the old per-attempt `buildForm()` lambdas did. All five call sites pass the same client key and issuer; the DPoP proof is still generated per attempt with the current nonce, and the dictionary is never reused across requests. - **Guard brace removals** in `ServiceAuthVerifier` (including the conditional `return`/`throw` in `ReadKeyId`), `SessionManager` (the `await using` lease scopes remain correctly scoped, including the nested two-condition guard), `AtProtoClientFactory`, `DPoPNonceCache`, `AtProtoSessionJson` and the Server layer: each was a single-statement block; no condition changed. - **Primary constructors** on internal types and the field→get-only property conversions (`OAuthClient.Discovery`/`HttpClient`, `RefreshCoordinator`, `SessionManager.Store`) preserve construction-time assignment and read-only use; the `Store!` path keeps the same non-null invariant as before. - **Removed usings** confirmed unused; `PublicAPI.*.txt` untouched as claimed, so no public API change. Non-blocking style note: the multi-line unbraced `foreach`/nested `if` blocks (e.g. `AtProtoServiceAuthOptions.Validate`, `OAuthClient.FindClientKey`) are harder to scan and easier to break with a later edit; braces on those would be more robust, but that is a matter of taste and does not block merging. --- ⏱ 3m 33s · 5 steps · $0.0085 · tokens: 33.6k in / 1.0k out (+80.9k cache-read) · Go: 5h 0% · wk 0% · mo 91% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/ATProto.NET/pr-206/run-2026-10-05T12-36-13-700Z.md) <!-- opencode-reviewed-head:a3c80b3195cda2cee04d6088100a0ea0266708ff --> <!-- opencode-verdict:approve -->
Grandiras deleted branch issue-204-auth-server-debloat 2026-10-05 12:40:09 +00:00
Sign in to join this conversation.
No description provided.