Skip to content

fix(aver-server): shared store, consent recovery, OAuth hardening - #7

Open
5queezer wants to merge 2 commits into
masterfrom
fix/server-hardening
Open

fix(aver-server): shared store, consent recovery, OAuth hardening#7
5queezer wants to merge 2 commits into
masterfrom
fix/server-hardening

Conversation

@5queezer

@5queezer 5queezer commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Fixes the server-hardening code-review findings against crates/aver-server. Re-verified each finding against master after 35a36be (token TTLs + loopback CORS already landed upstream; AuthDb::revoke_consent already revoked tokens at the DB layer — the missing pieces were the HTTP route and the skip-loop gate).

Findings → fixes

1. Per-session Store broke single-writer (MAJOR)

build_router now opens AverTools once at startup and the session factory clones the same Arc<Mutex<AverTools>> into AverMcpService::from_shared_tools (http.rs, mcp.rs). rusqlite connections are Send-not-Sync, so the existing Arc<Mutex<_>> pattern (same as AuthDb) is used. Per-session state stays limited to auth/scope request extensions. No busy_timeout added — that belongs to the core PR.

  • Test: tests/shared_store.rs — two concurrent MCP sessions write claims and recall sees both; mcp.rs unit test services_sharing_tools_observe_each_others_writes (threaded writers).

2. Consent empty-grant trap (MAJOR)

Zero-scope approvals recorded granted_scopes="" and consent_covers([], []) == true skipped the consent screen forever, minting tokens that fail every require_scope. Conservative fix (chosen over mapping empty→all SUPPORTED, which would grant more than asked): the skip path now requires a non-empty grant (consent.rs), so the screen re-renders and record_consent's upsert overwrites the empty row. New POST /oauth/consent/revoke route (session-cookie + origin validated) exposes the previously unreachable AuthDb::revoke_consent.

  • Tests: empty_scope_approval_does_not_trap_user_in_skip_loop (regression for the stuck loop), revoke_route_revokes_consent_and_tokens_and_allows_reconsent. Existing approve_..._skips_screen updated to grant claims:read (it previously pinned the trap).

3. alpha validated then discarded (MAJOR)

recall no longer advertises alpha (removed from the MCP schema and tools::RecallParams). Threading it was not viable: hybrid recall needs an EmbeddingClient and aver-server has none (aver-core built without the ollama feature; wiring one would break the offline/deterministic test rule). aver-core's recall_hybrid_claims_with_alpha is untouched for CLI/BEAM use. Instructions card needed no change (it never mentioned alpha).

  • Tests: alpha fields removed from tests/tools.rs / tests/scope_mcp.rs; obsolete recall_tool_rejects_alpha_outside_unit_interval deleted.

4. OAuth gaps (MAJOR)

  • Token response now includes expires_in (ACCESS_TOKEN_TTL_SECS, now pub).
  • Refresh tokens rotate on use; presenting a rotated/revoked refresh token revokes the whole (user, client) token family (RFC 6819 §5.2.2.3 reuse detection). tests/refresh_and_cors.rs updated (it pinned reuse) + new refresh_token_reuse_revokes_token_family.
  • Consent revocation invalidating tokens: already implemented by upstream in AuthDb::revoke_consent — now reachable via the revoke route (covered end-to-end by the route test).

5. Minor batch

  • /oauth/token returns RFC 6749 §5.2 JSON errors (invalid_grant / invalid_request / unsupported_grant_type) — oauth_token_route_returns_rfc6749_json_errors.
  • 401s carry WWW-Authenticate: Bearer (RFC 6750 §3) — asserted in http_routes.rs.
  • tracing_unavailable_warn stub removed: authenticate_request returns Result; auth-DB failures surface as HTML 500.
  • let _ = ALTER TABLE migrations → apply_column_migration: ignores only the duplicate-column error (rusqlite 0.32 maps it to ErrorCode::Unknown/ext 1 + message; comment explains), propagates everything else; expiry backfills propagate too.
  • PKCE verification reuses the constant-time constant_time_eq (moved to oauth.rs, shared with the CSRF check).
  • Scope validation rejects empty path segments (///, a//b) and caps length at 256 bytes — rejects_empty_path_segments, rejects_overlong_scope.
  • json_tool_result maps serialization failure to McpError instead of an empty success block.
  • Stale origin.rs module doc corrected; consolidate scope doc reworded to match the "all"-only implementation; "nomic-embed-text" is now DEFAULT_EMBEDDING_MODEL; AVER_PORT parse has .context("invalid AVER_PORT").

6. adapters.rs wiring

Still test-only (only tests/adapter_boundaries.rs references it; no production path in main.rs/http.rs/mcp.rs). Left as-is — intentional ADR-0016 boundary.

Verification

  • cargo fmt applied.
  • cargo clippy -p aver-server --all-targets -- -D warnings — clean.
  • cargo test -p aver-server172 passed, 0 failed, 0 ignored across all test binaries (deterministic/offline, no #[ignore]).

Docs: README endpoint list + consent/token flow paragraph updated (rotation, reuse detection, expires_in, revoke route, empty-grant behavior). No doc/adr/*.md edits; no autoresearch.jsonl changes; aver-core untouched (privacy filtering / log-first ordering preserved).

Summary by CodeRabbit

  • New Features

    • Added browser-based consent revocation.
    • Added refresh-token rotation with reuse detection and token-family revocation.
    • MCP sessions now share stored data consistently.
    • Added RFC-compliant OAuth token errors and bearer authentication challenges.
  • Bug Fixes

    • Fixed zero-scope consent flows so the consent screen appears when required.
    • Improved handling of authentication, migration, scope validation, and token expiry errors.
  • Documentation

    • Documented consent revocation, zero-scope approvals, and refresh-token behavior.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@5queezer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15a903da-5050-4154-b998-08d093f4b239

📥 Commits

Reviewing files that changed from the base of the PR and between 146d9bd and 6b2d90a.

📒 Files selected for processing (17)
  • README.md
  • crates/aver-server/src/auth.rs
  • crates/aver-server/src/config.rs
  • crates/aver-server/src/consent.rs
  • crates/aver-server/src/http.rs
  • crates/aver-server/src/mcp.rs
  • crates/aver-server/src/oauth.rs
  • crates/aver-server/src/origin.rs
  • crates/aver-server/src/scope_resolution.rs
  • crates/aver-server/src/tools.rs
  • crates/aver-server/tests/http_routes.rs
  • crates/aver-server/tests/oauth_consent_flow.rs
  • crates/aver-server/tests/refresh_and_cors.rs
  • crates/aver-server/tests/scope_mcp.rs
  • crates/aver-server/tests/scope_resolution.rs
  • crates/aver-server/tests/shared_store.rs
  • crates/aver-server/tests/tools.rs
📝 Walkthrough

Walkthrough

OAuth token rotation and consent revocation are added, MCP sessions share one tools store, and scope and recall contracts are tightened. Error responses, authentication handling, documentation, and integration coverage are updated accordingly.

Changes

OAuth integrity

Layer / File(s) Summary
Token lifecycle and protocol responses
crates/aver-server/src/auth.rs, crates/aver-server/src/http.rs, crates/aver-server/src/config.rs, crates/aver-server/tests/http_routes.rs, crates/aver-server/tests/refresh_and_cors.rs, README.md
Token migrations and expiry reporting are updated; refresh tokens rotate with reuse detection and family revocation; token endpoint errors use RFC 6749 JSON responses.
Consent authentication and revocation
crates/aver-server/src/consent.rs, crates/aver-server/src/oauth.rs, crates/aver-server/src/http.rs, crates/aver-server/src/origin.rs, crates/aver-server/tests/oauth_consent_flow.rs, crates/aver-server/tests/http_routes.rs, README.md
Consent authentication errors propagate explicitly, CSRF and PKCE comparisons use constant-time equality, zero-scope grants keep the consent screen active, and POST /oauth/consent/revoke is wired and tested.

Shared MCP storage

Layer / File(s) Summary
Shared MCP service construction and behavior
crates/aver-server/src/http.rs, crates/aver-server/src/mcp.rs
MCP sessions use a shared synchronized tools store, recall no longer accepts alpha, and serialization failures return MCP internal errors.
Shared-session integration coverage
crates/aver-server/tests/shared_store.rs
Independent MCP sessions perform concurrent writes and verify that both claims are visible through recall.

Scope and tool contracts

Layer / File(s) Summary
Scope validation boundaries
crates/aver-server/src/scope_resolution.rs, crates/aver-server/tests/scope_resolution.rs
Scope resolution rejects empty path segments and values longer than 256 bytes while accepting the 256-byte boundary.
Recall and vector tool contracts
crates/aver-server/src/tools.rs, crates/aver-server/src/mcp.rs, crates/aver-server/tests/scope_mcp.rs, crates/aver-server/tests/tools.rs
The recall alpha field and processing are removed, vector ingestion uses a shared model constant, and affected tool tests are updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OAuthToken
  participant AuthDb
  participant TokenFamily
  Client->>OAuthToken: Submit authorization_code or refresh_token grant
  OAuthToken->>AuthDb: Validate grant and load token state
  AuthDb-->>OAuthToken: Return authorization or refresh data
  OAuthToken->>TokenFamily: Rotate token or revoke reused family
  TokenFamily-->>OAuthToken: Return token pair or error
  OAuthToken-->>Client: Return JSON token response
Loading

Possibly related PRs

  • 5queezer/aver#1: Modifies the OAuth consent flow around scope selection and consent decision handling.

Poem

Tokens turn, old secrets fall,
Consent guards the station wall.
Shared tools remember every claim,
Scope paths stand precise and tame.
The system holds. No breach remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main aver-server changes: shared MCP storage, consent revocation/recovery, and OAuth hardening.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/server-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
crates/aver-server/src/oauth.rs (1)

11-24: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Do not maintain a home-grown constant-time primitive.

Use a vetted implementation already approved by the project, such as a ConstantTimeEq abstraction, instead of making two security boundaries depend on this local loop. The subtle crate provides that dedicated contract. (docs.rs)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aver-server/src/oauth.rs` around lines 11 - 24, Replace the local
constant_time_eq byte loop with the project-approved subtle::ConstantTimeEq
implementation, updating callers in the PKCE and consent-flow comparisons to use
its constant-time equality contract and convert the result to bool as needed.
Remove the home-grown helper while preserving the existing equality behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aver-server/src/auth.rs`:
- Around line 577-582: Update the refresh-token model and rotation flow around
the revoked-token handling in the authentication implementation to persist a
unique family identifier, inherit the existing identifier when issuing rotated
tokens, and assign a new identifier for a fresh login or consent generation.
Change revoke_token_family and its callers to target that family identifier
rather than the broad user/client pair, ensuring reuse revokes only the affected
lineage while preserving current-token behavior.
- Line 531: Update refresh_access_token() so token revocation and
issue_token_pair() persistence execute within one database transaction. Make
revocation conditional on the presented token, require exactly one affected row
before minting, and commit both the revocation and replacement-row inserts
together so any failure rolls back the entire rotation.

In `@crates/aver-server/src/consent.rs`:
- Around line 490-495: Update the error branches in the authentication paths,
including the branches around html_error at the shown locations, to log the
detailed anyhow error server-side and pass a fixed generic message to html_error
instead of interpolating err. Apply the same behavior to all referenced paths
while preserving the existing HTTP status and response structure.
- Around line 989-995: Update AuthDb::revoke_consent in auth.rs to execute the
client_consents, access_tokens, and refresh_tokens updates within a single
database transaction, committing only after all succeed and rolling back on any
failure. Keep the existing error propagation used by the caller in consent.rs.

In `@crates/aver-server/src/http.rs`:
- Around line 452-457: Update the token response around the JSON containing
access_token and refresh_token to include Cache-Control: no-store and Pragma:
no-cache headers, while preserving the existing response body and status
behavior.
- Around line 423-429: Update the "authorization_code" validation branch in the
token request handler to include request.redirect_uri.is_empty() alongside the
existing required-field checks. Return token_error with BAD_REQUEST and
"invalid_request" before database exchange when redirect_uri is omitted, while
preserving validation for the other fields.
- Around line 414-417: Update the oauth_token extractor handling so form parsing
failures, including invalid content types, malformed encoding, and missing
required fields, are handled inside oauth_token rather than short-circuiting.
Route every such failure through token_error and return the token JSON contract
with error set to invalid_request, while preserving normal TokenRequest
processing.

---

Nitpick comments:
In `@crates/aver-server/src/oauth.rs`:
- Around line 11-24: Replace the local constant_time_eq byte loop with the
project-approved subtle::ConstantTimeEq implementation, updating callers in the
PKCE and consent-flow comparisons to use its constant-time equality contract and
convert the result to bool as needed. Remove the home-grown helper while
preserving the existing equality behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdb3d5cd-d82d-4dc5-a676-525b844513ea

📥 Commits

Reviewing files that changed from the base of the PR and between f84fd7e and 146d9bd.

📒 Files selected for processing (17)
  • README.md
  • crates/aver-server/src/auth.rs
  • crates/aver-server/src/config.rs
  • crates/aver-server/src/consent.rs
  • crates/aver-server/src/http.rs
  • crates/aver-server/src/mcp.rs
  • crates/aver-server/src/oauth.rs
  • crates/aver-server/src/origin.rs
  • crates/aver-server/src/scope_resolution.rs
  • crates/aver-server/src/tools.rs
  • crates/aver-server/tests/http_routes.rs
  • crates/aver-server/tests/oauth_consent_flow.rs
  • crates/aver-server/tests/refresh_and_cors.rs
  • crates/aver-server/tests/scope_mcp.rs
  • crates/aver-server/tests/scope_resolution.rs
  • crates/aver-server/tests/shared_store.rs
  • crates/aver-server/tests/tools.rs
💤 Files with no reviewable changes (2)
  • crates/aver-server/tests/scope_mcp.rs
  • crates/aver-server/tests/tools.rs

Comment thread crates/aver-server/src/auth.rs
Comment thread crates/aver-server/src/auth.rs
Comment thread crates/aver-server/src/consent.rs
Comment thread crates/aver-server/src/consent.rs
Comment thread crates/aver-server/src/http.rs Outdated
Comment thread crates/aver-server/src/http.rs
Comment thread crates/aver-server/src/http.rs Outdated
@5queezer

Copy link
Copy Markdown
Owner Author

Addressed all seven review findings and rebased onto current master at a3a9966.

Highlights: refresh rotation is transactional and family-scoped; browser database details are logged server-side but replaced with fixed client-safe errors; consent plus access/refresh revocation is atomic; malformed token forms and missing redirect_uri map to OAuth invalid_request; successful token responses add Cache-Control: no-store and Pragma: no-cache. Added regression coverage for family isolation, rollback-on-revocation failure, generic browser errors, malformed forms, redirect validation, and cache headers.

Validation passed: format; aver-server Clippy with all targets/features and -D warnings; 10 HTTP-route, 13 OAuth-consent, 6 refresh/CORS, and 12 scope-resolution tests.

— Hermes Agent

5queezer and others added 2 commits July 27, 2026 13:14
- Share one Store across MCP sessions: build_router opens AverTools once
  and hands every session the same Arc<Mutex<_>>, preserving the
  single-writer invariant (per-session Store::open raced log rotation
  and claim-id pre-allocation on the same memory_dir).
- Consent empty-grant trap: zero-scope approvals no longer skip the
  consent screen (consent_covers([],[]) == true trapped users forever),
  and POST /oauth/consent/revoke now exposes AuthDb::revoke_consent so
  users can revoke + re-consent over HTTP.
- recall drops the advertised-but-discarded alpha parameter; hybrid
  weighting needs an embedding client the server does not have.
- Token endpoint: RFC 6749 expires_in, section 5.2 JSON error bodies;
  refresh tokens rotate on use with RFC 6819 reuse detection (family
  revocation); 401s carry WWW-Authenticate: Bearer.
- Hardening: auth-DB upsert failures surface as 500 instead of a
  swallowed stub; ALTER TABLE migrations only ignore duplicate-column
  errors; PKCE verify reuses the constant-time comparison; scope
  validation rejects empty path segments and caps length at 256;
  serialization failures map to McpError instead of an empty success
  block; stale origin/consolidate docs fixed; embedding-model label is
  a named const; AVER_PORT parse errors carry context.
@5queezer
5queezer force-pushed the fix/server-hardening branch from a3a9966 to 6b2d90a Compare July 27, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant