Skip to content

refactor(axum): split web integration and add auth context middleware - #8

Merged
inayayousfi merged 4 commits into
mainfrom
refactor/axum-modules-auth-middleware
Jun 10, 2026
Merged

refactor(axum): split web integration and add auth context middleware#8
inayayousfi merged 4 commits into
mainfrom
refactor/axum-modules-auth-middleware

Conversation

@inayayousfi

@inayayousfi inayayousfi commented Mar 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • split the Axum web integration out of src/web_axum.rs into focused modules for routing, handlers, models, middleware, and responses
  • add auth-context middleware plus AuthenticatedUser extractors so routes can access the current user from a bearer token
  • preserve and fix crate documentation so doctest examples compile cleanly again across the codebase

Details

  • adds a protected GET /me endpoint backed by the authenticated user context
  • keeps the public Axum API centered on get_authen_axum_router and start_server
  • cherry-picks the doctest documentation fix commit from main into this branch

Verification

  • cargo check --features web
  • cargo test --doc

Summary by CodeRabbit

  • Breaking Changes

    • Plain password construction now takes an owned String; password manager APIs now surface AuthError consistently.
  • New Features

    • New modular HTTP server layer with routes for signup, login, token ops, health, and full OAuth2 flows.
    • Request authentication middleware and handy authenticated-user extractors.
  • Improvements

    • Unified JSON error responses and clearer handler behaviors.
  • Documentation

    • Updated examples across credentials, password, token, and OAuth docs to show async usage and clearer imports.

@coderabbitai

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@inayayousfi, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 6 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 53c49e93-fcb1-4d4d-b28c-eabb72d7e732

📥 Commits

Reviewing files that changed from the base of the PR and between 8b2be94 and 68cf259.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • examples/web_server/src/main.rs
  • src/web_axum/handlers/token.rs
📝 Walkthrough

Walkthrough

Splits the prior monolithic Axum web server into a modular web layer (app, handlers, middleware, models, response), adds DTOs and error mapping, updates Cargo/features and example main, and standardizes doc examples and small core API/FromStr changes.

Changes

Web axum modularization

Layer / File(s) Summary
App and router setup
src/web_axum/app.rs
Adds start_server and get_authen_axum_router, router construction, default bind address, and middleware wiring.
Credential auth handlers
src/web_axum/handlers/auth.rs
Adds signup() and login() handlers mapping AuthService errors to ApiError and returning JSON responses.
OAuth handlers
src/web_axum/handlers/oauth.rs
Adds authorize(), callback(), signup(), login() for OAuth2 flows; percent-encodes tokens into frontend redirect fragment.
Token, system, models & response
src/web_axum/handlers/token.rs, src/web_axum/handlers/system.rs, src/web_axum/models.rs, src/web_axum/response.rs, src/web_axum/middleware.rs
Adds refresh/validate/health/me endpoints, request/response models and conversions, ApiResult/ApiError with IntoResponse, and auth-context middleware/extractors.
Cargo feature and example
Cargo.toml, examples/web_server/src/main.rs
Adds optional percent-encoding dep and axum feature wiring; example main now returns std::io::Result<()> and forwards start_server result.

Docs & core tweaks

Layer / File(s) Summary
OAuth manager docs and minor impl change
src/core/oauth/manager.rs
Standardizes doc examples to rust,no_run/ignore, uses explicit imports and config literals, and makes an inherent method call explicit in OAuth2Service impl.
OAuth store docs and parsing
src/core/oauth/store.rs
Standardizes doc examples and adds impl FromStr for OAuth2Provider to parse lowercase provider names and return a clear error string.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Middleware as Auth Middleware
  participant AuthService
  participant UserDb as User Store
  participant Handler as Request Handler

  Client->>Middleware: HTTP Request + Bearer Token
  Middleware->>Middleware: Parse Authorization Header
  alt token present
    Middleware->>AuthService: get_user_from_token(token)
    AuthService->>UserDb: Lookup/Create user
    UserDb-->>AuthService: User or Error
    AuthService-->>Middleware: User or Error
    Middleware->>Handler: Insert AuthenticatedUser or AuthenticationFailure in extensions
    Handler-->>Client: 200 OK or 401 Unauthorized
  else no token
    Middleware->>Handler: continue without user
    Handler-->>Client: 401 Unauthorized or 200 (if optional)
  end
Loading
sequenceDiagram
  participant Client
  participant OAuthHandler as OAuth Callback
  participant AuthService
  participant OAuthProvider as OAuth Provider
  participant Frontend as Frontend

  Client->>OAuthHandler: GET /oauth/{provider}/callback?code=...&state=...
  OAuthHandler->>AuthService: login(LoginMethod::OAuth2{provider, code})
  AuthService->>OAuthProvider: exchange code for tokens / user info
  OAuthProvider-->>AuthService: tokens + user info
  AuthService->>AuthService: create/update user, generate TokenPair
  AuthService-->>OAuthHandler: TokenPair
  OAuthHandler->>Frontend: Redirect with percent-encoded tokens in fragment
  Frontend-->>Client: token handling page
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit hops through modular glen,
Routes and middleware stitched again,
Tokens tucked safe in fragments bright,
Docs polished, examples light,
New warren born — the server's then!

🚥 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 accurately describes the main refactoring work: splitting Axum web integration into modules and adding authentication context middleware.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/axum-modules-auth-middleware

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 and usage tips.

@inayayousfi inayayousfi self-assigned this Mar 7, 2026

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/oauth/manager.rs (1)

594-599: ⚠️ Potential issue | 🔴 Critical

Infinite recursion in trait implementation.

The get_redirect_frontend_uri trait method calls self.get_redirect_frontend_uri(provider), which resolves to the same trait method, causing infinite recursion at runtime. The intent appears to be calling the inherent method defined at lines 617-622.

🐛 Proposed fix to call the inherent method
     async fn get_redirect_frontend_uri(
         &self,
         provider: OAuth2Provider,
     ) -> Result<String, AuthError> {
-        self.get_redirect_frontend_uri(provider)
+        OAuth2Manager::get_redirect_frontend_uri(self, provider)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/oauth/manager.rs` around lines 594 - 599, The trait impl for
get_redirect_frontend_uri currently recursively calls itself; change it to call
the inherent (concrete) implementation instead — replace the
self.get_redirect_frontend_uri(provider) call inside the trait impl with a
direct call to the concrete type's inherent method (e.g.,
MyManagerType::get_redirect_frontend_uri(self, provider) using the actual
struct/type name that defines the inherent method) so the trait forwards to the
defined implementation rather than recursing.
🧹 Nitpick comments (11)
src/core/hash/argon2.rs (1)

39-42: Keep this doctest executable.

Argon2Hasher::new() is side-effect free, so no_run only drops cheap runtime coverage here. I’d keep this as a normal rust doctest and re-run cargo test --doc locally to confirm it still passes.

Suggested diff
-    /// ```rust,no_run
+    /// ```rust
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/hash/argon2.rs` around lines 39 - 42, The doctest for Argon2Hasher
currently uses `no_run`, which suppresses execution; change the doc comment
block in src/core/hash/argon2.rs so the doctest is a normal executable Rust doc
test (remove `no_run`) for the `Argon2Hasher::new()` example, then run `cargo
test --doc` locally to verify it passes; ensure the example remains side-effect
free and compiles when run as a doc test.
src/core/user/persistence/in_memory.rs (1)

27-30: Remove the no_run directive to enable doctest execution.

The import path authen::core::user::persistence::InMemoryUserRepo is correct. The no_run directive is unnecessary—InMemoryUserRepo::new() is a simple constructor with no side effects or external dependencies, making this example safe to compile and execute as a runnable doctest.

Suggested change
-    /// ```rust,no_run
+    /// ```rust
     /// use authen::core::user::persistence::InMemoryUserRepo;
     /// let repo = InMemoryUserRepo::new();
     /// ```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/user/persistence/in_memory.rs` around lines 27 - 30, Update the
doctest for InMemoryUserRepo by removing the `no_run` directive so the snippet
runs as a normal doctest: change the doc block from "```rust,no_run" to
"```rust" for the example that imports
`authen::core::user::persistence::InMemoryUserRepo` and calls
`InMemoryUserRepo::new()`, ensuring the `InMemoryUserRepo::new` constructor is
compiled and executed by cargo test.
src/core/policy/mod.rs (1)

64-64: Minor inconsistency in doctest directives.

The method-level example uses no_run while the module-level example at line 9 uses plain rust. Both examples are self-contained and should compile/run correctly. Consider using consistent doctest directives across the module unless there's a specific reason for the difference.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/policy/mod.rs` at line 64, The doctest directives are inconsistent:
the method-level example uses "```rust,no_run" while the module-level example
uses "```rust"; pick one directive and make them consistent across this module
(either change the method-level block in mod.rs from "```rust,no_run" to
"```rust" or change the module-level example to include ",no_run"), ensuring
both examples remain self-contained and compile/run as intended; locate the
fenced code blocks in src/core/policy/mod.rs (the module-level example and the
method-level example) and update the triple-backtick directives to the chosen
consistent form.
src/core/oauth/manager.rs (1)

374-388: French comments in English codebase.

The comments on lines 374 and 386 are in French ("Dans ton code Rust, assure-toi de dédupliquer les scopes" and "Déduplication des scopes"). Consider translating to English for consistency with the rest of the codebase.

♻️ Suggested translation
-        // Dans ton code Rust, assure-toi de dédupliquer les scopes
+        // Collect all scopes from defaults, additional parameters, and config
         let mut all_scopes = provider
             .default_scopes()
             .into_iter()
             .map(|s| s.to_string())
             .collect::<Vec<_>>();

         if let Some(additional_scopes) = scopes {
             all_scopes.extend(additional_scopes);
         }
         all_scopes.extend(config.additional_scopes.clone());

-        // Déduplication des scopes
+        // Deduplicate scopes
         all_scopes.sort();
         all_scopes.dedup();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/oauth/manager.rs` around lines 374 - 388, Replace the two French
comments around the scope-collection/dedup block with concise English
equivalents; update the comment before building all_scopes (near
provider.default_scopes(), scopes, config.additional_scopes) to something like
"Collect default and additional scopes" and replace the "Déduplication des
scopes" comment above all_scopes.sort() / all_scopes.dedup() with "Deduplicate
scopes", ensuring comment style and tone match the surrounding English codebase.
src/web_axum/handlers/token.rs (2)

24-27: Expired/invalid refresh tokens mapped to 400 may be better as 401.

Similar to login, an expired or invalid refresh token is an authentication failure rather than a malformed request. Consider using ApiError::unauthorized for token-related auth failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/handlers/token.rs` around lines 24 - 27, The refresh token
failure is an authentication issue but currently maps to ApiError::bad_request;
update the error mapping in the refresh path so failures from
auth_service.refresh_access_token(&payload.refresh_token).await are converted to
ApiError::unauthorized instead of ApiError::bad_request (i.e., replace the
map_err target to ApiError::unauthorized or map the underlying error to an
unauthorized ApiError), keeping the same call to
auth_service.refresh_access_token and preserving the tokens variable.

32-48: Consider returning {valid: false} for invalid tokens instead of an error response.

Currently, an invalid/expired token triggers an error (400 response), while only valid tokens reach the success path with valid: true. This is technically correct but clients may expect a 200 response with {valid: false} for token inspection use cases. If this is intentional, the API documentation should clarify this behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/handlers/token.rs` around lines 32 - 48, The validate function
currently converts any token validation failure into an ApiError::bad_request;
change it so validate_access_token errors are handled by returning a successful
200 response with ValidateTokenResponse.valid = false instead. Concretely, in
validate (and where you currently call AuthService::validate_access_token and
map_err(ApiError::bad_request)), match the Result from validate_access_token: on
Ok(claims) return the existing Json(ValidateTokenResponse { valid: true,
subject: claims.get_subject().to_string(), expiration: claims.get_expiration()
}), and on Err(_) return Ok(Json(ValidateTokenResponse { valid: false, subject:
/* empty string or appropriate default */, expiration: /* None or default
matching the type */ })). Remove mapping to ApiError::bad_request for token
validation failures so clients receive a 200 with valid:false (adjust the
default subject/expiration values to match ValidateTokenResponse field types).
src/web_axum/response.rs (1)

33-38: The internal method hardcodes a misleading error prefix.

The "Configuration error:" prefix is hardcoded but internal() may be used for non-configuration errors (database failures, network issues, etc.). Consider removing the prefix or providing a more generic one.

Proposed fix
     pub fn internal(error: impl ToString) -> Self {
         Self::new(
             StatusCode::INTERNAL_SERVER_ERROR,
-            format!("Configuration error: {}", error.to_string()),
+            format!("Internal server error: {}", error.to_string()),
         )
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/response.rs` around lines 33 - 38, The internal method currently
prepends a misleading "Configuration error:" prefix; update the implementation
of Response::internal (the internal function that calls Self::new with
StatusCode::INTERNAL_SERVER_ERROR) to remove that hardcoded prefix and either
log the raw error string (format!("{}", error.to_string())) or use a generic
prefix like "Internal server error:" so the message is accurate for
database/network/other failures; ensure the change is made in the internal
function that constructs the Response via Self::new and preserves the
StatusCode::INTERNAL_SERVER_ERROR.
src/web_axum/app.rs (1)

39-40: Panics on bind/serve failures are documented but consider structured error handling.

The .unwrap() calls are documented in the function's # Panics section, which is good. For production use, you may want to return a Result to allow callers to handle startup failures gracefully.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/app.rs` around lines 39 - 40, The startup code currently panics
on failures because it uses TcpListener::bind(...).await.unwrap() and
serve(...).await.unwrap(); change the function signature to return a Result
(e.g., Result<(), anyhow::Error> or axum-compatible error) and replace the
unwrap() calls with the ? operator (or map_err to convert into your chosen error
type) so bind and serve errors are propagated to the caller instead of
panicking; update any call sites to handle or propagate the returned Result
accordingly.
src/web_axum/handlers/auth.rs (1)

22-28: Consider distinguishing signup error types for clearer API responses.

All signup failures (including "user already exists") are mapped to 400 Bad Request. While acceptable, you may want to distinguish user-exists errors (potentially 409 Conflict) from other validation failures for better client-side handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/handlers/auth.rs` around lines 22 - 28, The signup handler
currently maps all signup failures from auth_service.signup(...) (called with
SignupMethod::Credentials) to ApiError::bad_request, which hides distinct error
cases like "user already exists"; update the error handling of the await call to
inspect the specific error returned by auth_service.signup (e.g., match on an
AuthError or SignupError variant that indicates user already exists) and map
that case to an appropriate API error (e.g., ApiError::conflict or an
ApiError::already_exists) while keeping other validation or generic errors
mapped to ApiError::bad_request so clients can distinguish 409 Conflict from
other 400 errors.
src/web_axum/handlers/oauth.rs (1)

122-132: Consider using a standard URL encoding library instead of custom implementation.

The custom url_encode function is correct and handles RFC 3986 unreserved characters properly, but using an established library like urlencoding or percent_encoding would eliminate maintenance burden. Both crates are well-tested and handle edge cases comprehensively.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/handlers/oauth.rs` around lines 122 - 132, The custom url_encode
function implements RFC3986-style percent-encoding but should be replaced with a
standard crate to reduce maintenance and edge-case bugs: remove or deprecate the
url_encode function in src/web_axum/handlers/oauth.rs and switch callers to use
a tested crate such as percent_encoding or urlencoding (e.g.,
percent_encoding::percent_encode_str with an appropriate ASCII set for
unreserved characters), add the chosen crate to Cargo.toml, and ensure the new
call produces the same RFC3986 unreserved-character behavior as the original
implementation.
src/web_axum/models.rs (1)

187-197: Consider deriving FromStr on OAuth2Provider for improved maintainability.

The hardcoded provider list requires manual updates when new providers are added to the OAuth2Provider enum. Using FromStr or a derive crate like strum would keep the parse logic automatically aligned with enum variants.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/web_axum/models.rs` around lines 187 - 197, The parse_oauth_provider
function duplicates variant matching and should be replaced by
implementing/deriving std::str::FromStr for the OAuth2Provider enum (or using
the strum derive) and mapping parse errors to your ApiError; add a FromStr impl
(or #[derive(EnumString)] via strum) on OAuth2Provider that returns a clear
parse error, then change parse_oauth_provider to call
value.parse::<OAuth2Provider>().map_err(|e|
ApiError::bad_request(AuthError::InvalidInput(format!("Unsupported OAuth2
provider: {value}: {e}")))); this removes the hardcoded list and keeps parse
logic in the enum type itself.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/web_axum/handlers/auth.rs`:
- Around line 39-45: The current handler maps all errors from auth_service.login
to ApiError::bad_request, but authentication failures
(AuthError::InvalidCredentials) should return HTTP 401; update the error
handling around auth_service.login (the call to AuthService::login/login method)
to match on the returned AuthError and map AuthError::InvalidCredentials to
ApiError::unauthorized (or the equivalent 401 ApiError), while mapping other
variants to ApiError::bad_request so only actual credential failures produce
401.

In `@src/web_axum/handlers/oauth.rs`:
- Line 69: The handler currently returns a permanent redirect via
Redirect::permanent(&redirect_url) which yields a cached 301; change this to a
temporary redirect to avoid client caching of OAuth callbacks—replace
Redirect::permanent with Redirect::temporary(&redirect_url) (or
Redirect::to(&redirect_url) for a 303) wherever the
Redirect::permanent(&redirect_url) expression appears in the oauth callback
handler (the return expression that produces
Ok(Redirect::permanent(&redirect_url))).

---

Outside diff comments:
In `@src/core/oauth/manager.rs`:
- Around line 594-599: The trait impl for get_redirect_frontend_uri currently
recursively calls itself; change it to call the inherent (concrete)
implementation instead — replace the self.get_redirect_frontend_uri(provider)
call inside the trait impl with a direct call to the concrete type's inherent
method (e.g., MyManagerType::get_redirect_frontend_uri(self, provider) using the
actual struct/type name that defines the inherent method) so the trait forwards
to the defined implementation rather than recursing.

---

Nitpick comments:
In `@src/core/hash/argon2.rs`:
- Around line 39-42: The doctest for Argon2Hasher currently uses `no_run`, which
suppresses execution; change the doc comment block in src/core/hash/argon2.rs so
the doctest is a normal executable Rust doc test (remove `no_run`) for the
`Argon2Hasher::new()` example, then run `cargo test --doc` locally to verify it
passes; ensure the example remains side-effect free and compiles when run as a
doc test.

In `@src/core/oauth/manager.rs`:
- Around line 374-388: Replace the two French comments around the
scope-collection/dedup block with concise English equivalents; update the
comment before building all_scopes (near provider.default_scopes(), scopes,
config.additional_scopes) to something like "Collect default and additional
scopes" and replace the "Déduplication des scopes" comment above
all_scopes.sort() / all_scopes.dedup() with "Deduplicate scopes", ensuring
comment style and tone match the surrounding English codebase.

In `@src/core/policy/mod.rs`:
- Line 64: The doctest directives are inconsistent: the method-level example
uses "```rust,no_run" while the module-level example uses "```rust"; pick one
directive and make them consistent across this module (either change the
method-level block in mod.rs from "```rust,no_run" to "```rust" or change the
module-level example to include ",no_run"), ensuring both examples remain
self-contained and compile/run as intended; locate the fenced code blocks in
src/core/policy/mod.rs (the module-level example and the method-level example)
and update the triple-backtick directives to the chosen consistent form.

In `@src/core/user/persistence/in_memory.rs`:
- Around line 27-30: Update the doctest for InMemoryUserRepo by removing the
`no_run` directive so the snippet runs as a normal doctest: change the doc block
from "```rust,no_run" to "```rust" for the example that imports
`authen::core::user::persistence::InMemoryUserRepo` and calls
`InMemoryUserRepo::new()`, ensuring the `InMemoryUserRepo::new` constructor is
compiled and executed by cargo test.

In `@src/web_axum/app.rs`:
- Around line 39-40: The startup code currently panics on failures because it
uses TcpListener::bind(...).await.unwrap() and serve(...).await.unwrap(); change
the function signature to return a Result (e.g., Result<(), anyhow::Error> or
axum-compatible error) and replace the unwrap() calls with the ? operator (or
map_err to convert into your chosen error type) so bind and serve errors are
propagated to the caller instead of panicking; update any call sites to handle
or propagate the returned Result accordingly.

In `@src/web_axum/handlers/auth.rs`:
- Around line 22-28: The signup handler currently maps all signup failures from
auth_service.signup(...) (called with SignupMethod::Credentials) to
ApiError::bad_request, which hides distinct error cases like "user already
exists"; update the error handling of the await call to inspect the specific
error returned by auth_service.signup (e.g., match on an AuthError or
SignupError variant that indicates user already exists) and map that case to an
appropriate API error (e.g., ApiError::conflict or an ApiError::already_exists)
while keeping other validation or generic errors mapped to ApiError::bad_request
so clients can distinguish 409 Conflict from other 400 errors.

In `@src/web_axum/handlers/oauth.rs`:
- Around line 122-132: The custom url_encode function implements RFC3986-style
percent-encoding but should be replaced with a standard crate to reduce
maintenance and edge-case bugs: remove or deprecate the url_encode function in
src/web_axum/handlers/oauth.rs and switch callers to use a tested crate such as
percent_encoding or urlencoding (e.g., percent_encoding::percent_encode_str with
an appropriate ASCII set for unreserved characters), add the chosen crate to
Cargo.toml, and ensure the new call produces the same RFC3986
unreserved-character behavior as the original implementation.

In `@src/web_axum/handlers/token.rs`:
- Around line 24-27: The refresh token failure is an authentication issue but
currently maps to ApiError::bad_request; update the error mapping in the refresh
path so failures from
auth_service.refresh_access_token(&payload.refresh_token).await are converted to
ApiError::unauthorized instead of ApiError::bad_request (i.e., replace the
map_err target to ApiError::unauthorized or map the underlying error to an
unauthorized ApiError), keeping the same call to
auth_service.refresh_access_token and preserving the tokens variable.
- Around line 32-48: The validate function currently converts any token
validation failure into an ApiError::bad_request; change it so
validate_access_token errors are handled by returning a successful 200 response
with ValidateTokenResponse.valid = false instead. Concretely, in validate (and
where you currently call AuthService::validate_access_token and
map_err(ApiError::bad_request)), match the Result from validate_access_token: on
Ok(claims) return the existing Json(ValidateTokenResponse { valid: true,
subject: claims.get_subject().to_string(), expiration: claims.get_expiration()
}), and on Err(_) return Ok(Json(ValidateTokenResponse { valid: false, subject:
/* empty string or appropriate default */, expiration: /* None or default
matching the type */ })). Remove mapping to ApiError::bad_request for token
validation failures so clients receive a 200 with valid:false (adjust the
default subject/expiration values to match ValidateTokenResponse field types).

In `@src/web_axum/models.rs`:
- Around line 187-197: The parse_oauth_provider function duplicates variant
matching and should be replaced by implementing/deriving std::str::FromStr for
the OAuth2Provider enum (or using the strum derive) and mapping parse errors to
your ApiError; add a FromStr impl (or #[derive(EnumString)] via strum) on
OAuth2Provider that returns a clear parse error, then change
parse_oauth_provider to call value.parse::<OAuth2Provider>().map_err(|e|
ApiError::bad_request(AuthError::InvalidInput(format!("Unsupported OAuth2
provider: {value}: {e}")))); this removes the hardcoded list and keeps parse
logic in the enum type itself.

In `@src/web_axum/response.rs`:
- Around line 33-38: The internal method currently prepends a misleading
"Configuration error:" prefix; update the implementation of Response::internal
(the internal function that calls Self::new with
StatusCode::INTERNAL_SERVER_ERROR) to remove that hardcoded prefix and either
log the raw error string (format!("{}", error.to_string())) or use a generic
prefix like "Internal server error:" so the message is accurate for
database/network/other failures; ensure the change is made in the internal
function that constructs the Response via Self::new and preserves the
StatusCode::INTERNAL_SERVER_ERROR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bc8d6971-de81-41d5-9249-b84a9f0e554b

📥 Commits

Reviewing files that changed from the base of the PR and between dce4381 and 5afd4ae.

📒 Files selected for processing (23)
  • src/core/credentials/mod.rs
  • src/core/hash/argon2.rs
  • src/core/oauth/manager.rs
  • src/core/oauth/store.rs
  • src/core/password/argon2.rs
  • src/core/password/mod.rs
  • src/core/policy/mod.rs
  • src/core/token/jwt.rs
  • src/core/token/mod.rs
  • src/core/user/mod.rs
  • src/core/user/persistence/in_memory.rs
  • src/lib.rs
  • src/web_axum.rs
  • src/web_axum/app.rs
  • src/web_axum/handlers/auth.rs
  • src/web_axum/handlers/mod.rs
  • src/web_axum/handlers/oauth.rs
  • src/web_axum/handlers/system.rs
  • src/web_axum/handlers/token.rs
  • src/web_axum/middleware.rs
  • src/web_axum/mod.rs
  • src/web_axum/models.rs
  • src/web_axum/response.rs
💤 Files with no reviewable changes (1)
  • src/web_axum.rs

Comment thread src/web_axum/handlers/auth.rs Outdated
Comment thread src/web_axum/handlers/oauth.rs Outdated
- centralize OAuth provider parsing and fragment encoding
- map signup/login/token errors to clearer HTTP responses
- return server startup errors instead of panicking

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/web_server/src/main.rs (1)

105-118: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the doctest to match the updated API.

The doctest example has two issues:

  1. Missing the second parameter to start_server (should be start_server(auth_service, None).await)
  2. Not handling the std::io::Result<()> returned by start_server

This will cause the doctest to fail compilation.

🔧 Proposed doctest fix
 /// ```rust
 /// use authen::{AuthService, web_axum::start_server};
 /// use std::sync::Arc;
 ///
 /// #[tokio::main]
-/// async fn main() {
+/// async fn main() -> std::io::Result<()> {
 ///     env_logger::init();
 ///     let auth_service = Arc::new(AuthService::default());
 ///     #[cfg(feature = "axum")]
-///     start_server(auth_service).await;
+///     start_server(auth_service, None).await?;
 ///     #[cfg(not(feature = "web"))]
 ///     println!("Please enable the 'web' feature to run the web server example.");
+///     Ok(())
 /// }
 /// ```
🤖 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 `@examples/web_server/src/main.rs` around lines 105 - 118, Update the doctest
to match the new start_server signature and return type: change the async main
signature to return std::io::Result<()> and call start_server with the second
argument (e.g., None) and propagate its Result with ?; finally return Ok(()) at
the end. This affects the doctest lines that reference
start_server(auth_service) — replace them with start_server(auth_service,
None).await? and change async fn main() to async fn main() ->
std::io::Result<()> and add Ok(()) before the function end.
🧹 Nitpick comments (1)
Cargo.toml (1)

38-38: Consider bumping percent-encoding to the latest patch; no known CVEs for 2.3.1

percent-encoding = { version = "2.3.1", optional = true } pins an older release—latest is 2.3.2.

No RustSec advisory is associated with percent-encoding 2.3.1, and GitHub Advisory Database searches don’t show a vulnerability specifically tied to that crate/version.

🤖 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 `@Cargo.toml` at line 38, The dependency entry percent-encoding = { version =
"2.3.1", optional = true } is pinned to an older patch; update it to
percent-encoding = { version = "2.3.2", optional = true } in Cargo.toml and then
run cargo update for that package (or regenerate Cargo.lock) so the lockfile
reflects the bump; confirm no other dependency constraints prevent the update
and run tests/build to verify compatibility.
🤖 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 `@examples/web_server/src/main.rs`:
- Around line 137-138: Update the outdated doc comment for the function that now
returns std::io::Result<()> (the program entry/server startup function, e.g.,
main) so it no longer claims it will panic if the Tokio runtime cannot be
started; instead state that startup errors are propagated as a Result and
returned to the caller. Mention the new return type (std::io::Result<()>) and
that errors from starting the Tokio runtime or server are returned, not
panicked, and remove the earlier "Panics" section.

In `@src/web_axum/handlers/token.rs`:
- Around line 38-49: The handler currently treats all errors from
auth_service.validate_access_token as a simple "valid: false"; change the Err
branch to distinguish validation errors from system errors by pattern-matching
the auth service error (from validate_access_token): for expected validation
failures (e.g., AuthError::InvalidToken, AuthError::Expired or whatever variant
your auth error type uses) return Ok(Json(ValidateTokenResponse { valid: false,
subject: String::new(), expiration: 0 })), but for unexpected/system errors
return Err(ApiError::internal(format!("token validation service error: {}",
err))) so the caller sees a 5xx and operators can detect service issues. Ensure
you reference auth_service.validate_access_token, ValidateTokenResponse and
ApiError::internal when implementing the change.

---

Outside diff comments:
In `@examples/web_server/src/main.rs`:
- Around line 105-118: Update the doctest to match the new start_server
signature and return type: change the async main signature to return
std::io::Result<()> and call start_server with the second argument (e.g., None)
and propagate its Result with ?; finally return Ok(()) at the end. This affects
the doctest lines that reference start_server(auth_service) — replace them with
start_server(auth_service, None).await? and change async fn main() to async fn
main() -> std::io::Result<()> and add Ok(()) before the function end.

---

Nitpick comments:
In `@Cargo.toml`:
- Line 38: The dependency entry percent-encoding = { version = "2.3.1", optional
= true } is pinned to an older patch; update it to percent-encoding = { version
= "2.3.2", optional = true } in Cargo.toml and then run cargo update for that
package (or regenerate Cargo.lock) so the lockfile reflects the bump; confirm no
other dependency constraints prevent the update and run tests/build to verify
compatibility.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f26db2d0-b2f2-46a0-af04-60d5ad18ec0b

📥 Commits

Reviewing files that changed from the base of the PR and between 5afd4ae and 8b2be94.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • examples/web_server/src/main.rs
  • src/core/oauth/manager.rs
  • src/core/oauth/store.rs
  • src/web_axum/app.rs
  • src/web_axum/handlers/auth.rs
  • src/web_axum/handlers/oauth.rs
  • src/web_axum/handlers/token.rs
  • src/web_axum/models.rs
  • src/web_axum/response.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/web_axum/response.rs
  • src/web_axum/handlers/auth.rs
  • src/web_axum/handlers/oauth.rs
  • src/web_axum/app.rs
  • src/web_axum/models.rs
  • src/core/oauth/manager.rs

Comment thread examples/web_server/src/main.rs Outdated
Comment thread src/web_axum/handlers/token.rs
- treat invalid, expired, and validation token errors as non-fatal
- return web server startup errors from the example instead of panicking
- refresh `percent-encoding` and lockfile dependencies
@inayayousfi
inayayousfi merged commit 7e9ec92 into main Jun 10, 2026
3 checks passed
@inayayousfi
inayayousfi deleted the refactor/axum-modules-auth-middleware branch June 10, 2026 07:29
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