Auth Bypass in Axum with Openid Connect
Auth Bypass in Axum with Openid Connect — how this specific combination creates or exposes the vulnerability
When integrating OpenID Connect (OIDC) with the Axum web framework in Rust, authentication bypass risks arise primarily from incomplete validation of identity tokens and missing authorization checks between authentication and authorization layers. Axum does not enforce authentication or authorization by itself; these are implemented via middleware or extractors that developers add to the application. If the OIDC integration does not rigorously validate ID tokens—such as skipping nonce verification, ignoring issuer validation, or failing to check token expiration—an attacker can present an arbitrary or unsigned token and be accepted as an authenticated user.
OIDC relies on an identity provider (IdP) issuing signed tokens (typically JWTs). In Axum, developers often use an OIDC integration library that provides an extractor or middleware to validate tokens. A common misconfiguration is to trust the presence of a token claim (such as sub) without ensuring the token was issued for the intended audience (aud) or without verifying the token signature against the IdP’s JWKS endpoint. If signature validation is omitted or performed incorrectly, an attacker can forge a token with a valid sub claim, and Axum will treat the request as authenticated.
Another bypass scenario occurs when route protection is inconsistently applied. For example, some routes may use an OIDC guard while others do not, or a developer applies authentication middleware to a subset of routes but forgets nested or chained routes. In Axum, this can manifest as a route that uses Router::route_layer with an authentication requirement being overridden by a more permissive layer applied later in the builder chain. An attacker can then access unprotected endpoints that should require a valid session, effectively bypassing authentication.
Session fixation and insecure token storage on the client side can also lead to bypasses. If Axum-based applications store session identifiers in insecure cookies (missing Secure and HttpOnly flags) or accept token material from query parameters, tokens can be leaked via referrers or browser history. Compromised tokens allow an attacker to impersonate a user even when OIDC token validation is otherwise correctly implemented.
Finally, authorization confusion between authentication and authorization can lead to vertical or horizontal privilege escalation. Authentication confirms identity via OIDC, but authorization must still enforce role- or scope-based access controls. If Axum handlers rely solely on the presence of an authenticated identity without checking scopes or roles contained in the token claims, an authenticated user can perform actions reserved for higher-privileged users. This is an authentication bypass at the authorization boundary: the system recognizes who you are but fails to verify whether you are allowed to perform the requested action.
Openid Connect-Specific Remediation in Axum — concrete code fixes
To securely integrate OpenID Connect with Axum, validate tokens at the middleware or extractor level and enforce authorization checks consistently across all protected routes. Use a well-maintained OIDC library such as openidconnect with Axum extractors to ensure token signature, issuer, audience, expiration, and nonce are verified before allowing access.
First, configure an OIDC client with strict validation parameters. The following example shows a secure setup using the openidconnect crate with Axum extractors:
use axum::{routing::get, Router};
use openidconnect::{reqwest::http_client, AuthUrl, ClientId, ClientSecret, CsrfToken, IssuerUrl, RedirectUrl, Scope, TokenResponse, Nonce, OidcClient};
use std::net::SocketAddr;
async fn build_oidc_client() -> OidcClient {
let issuer = IssuerUrl::new("https://accounts.google.com".to_string()).expect("valid issuer");
let client_id = ClientId::new("your-client-id".to_string());
let client_secret = ClientSecret::new("your-client-secret".to_string());
let redirect_url = RedirectUrl::new("http://localhost:3000/callback".to_string()).expect("valid redirect URL");
OidcClient::new(
issuer,
client_id,
Some(client_secret),
redirect_url,
http_client,
)
.expect("valid OIDC client")
.with_csrf_token(CsrfToken::new_random)
.with_nonce(Nonce::new_random)
.build()
}
#[tokio::main]
async fn main() {
let oidc_client = build_oidc_client();
let app = Router::new()
.route("/login", get(|| async { "Login route, initiate OIDC flow" }))
.route("/callback", get(|code: String, state: String| async move {
// Exchange code for tokens with strict validation
let token_set = oidc_client.exchange_code(code).request(http_client).expect("token exchange");
let id_token = token_set.id_token().expect("ID token present");
let claims = id_token.claims(&oidc_client.id_token_verifier()).expect("valid claims");
// Ensure audience matches client ID
assert_eq!(claims.aud().as_str(), "your-client-id");
// Ensure nonce matches to prevent replay
assert!(claims.nonce().is_some());
// Proceed with session creation or role-based authorization
format("Authenticated user: {}", claims.subject().as_str())
}));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
Second, enforce authorization in every handler by inspecting token claims rather than relying on a simple authenticated flag. Scopes and roles should be validated explicitly:
use axum::{extract::Extension, http::StatusCode, response::IntoResponse};
use openidconnect::Claims;
async fn admin_handler(
Extension(claims): Extension, // claims include roles/scopes
) -> Result {
let scopes: Vec<&str> = claims.scopes().iter().map(|s| s.as_str()).collect();
if !scopes.contains(&"admin") {
return Err((StatusCode::FORBIDDEN, "insufficient scope".to_string()));
}
Ok("admin resource")
}
Third, ensure all OIDC-related routes use consistent middleware and that no route inadvertently skips validation. Avoid route-level overrides that weaken protections. For applications requiring continuous monitoring, the Pro plan can integrate with CI/CD pipelines to scan configurations and detect missing authorization checks before deployment.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |