Simple API Key Authentication in Axum: Step-by-Step Guide
When building web applications, authentication ensures that only authorized users can access certain resources. In this guide, we will create a simple authentication mechanism using an API key in Axum.
We’ll implement middleware to check the Authorization
header for an API key. If the key is valid, we’ll display a success message; otherwise, we’ll return an Unauthorized
response.
Let’s dive in!
1. Step-by-Step Code Build
We will start with a minimal Axum setup and progressively add the authentication middleware.
Step 1: Set Up the Basic Axum Project
Create a new Axum project:
cargo new axum_auth_example --bin
cd axum_auth_example
Then, add the necessary dependencies to Cargo.toml
:
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
Now, let’s create a basic "Hello, World!" Axum application.
use axum::{
routing::get,
Router,
};
#[tokio::main]
async fn main() {
// build our application with a single route
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
This basic app listens on port 3000 and will display "Hello, World!" at http://localhost:3000
.
Step 2: Adding Authentication Middleware
Now let’s create the middleware that checks for the presence of a valid API key in the Authorization
header.
use axum::{
Router,
http::StatusCode,
routing::get,
response::{IntoResponse, Response},
middleware::{self, Next},
extract::Request,
};
const VALID_API_KEY: &str = "Bearer my_secret_api_key";
async fn auth(req: Request, next: Next) -> Result<Response, StatusCode> {
let auth_header = req.headers()
.get(axum::http::header::AUTHORIZATION)
.and_then(|header| header.to_str().ok());
let auth_header = if let Some(auth_header) = auth_header {
auth_header
} else {
return Err(StatusCode::UNAUTHORIZED);
};
if auth_header == VALID_API_KEY {
// If the API key matches, proceed to the next handler
Ok(next.run(req).await)
} else {
// Otherwise, return Unauthorized
Err(StatusCode::UNAUTHORIZED)
}
}
async fn handler() -> impl IntoResponse {
"You are authenticated!"
}
Here’s what we’re doing in the code:
- API Key Validation: We check the
Authorization
header for the stringBearer my_secret_api_key
. If it matches, the request is authenticated. - Return Message: If the API key is correct, we allow the request to continue to the
handler
, which simply responds with "You are authenticated!". If the key is incorrect or missing, we return a401 Unauthorized
.
Step 3: Apply Middleware to the Routes
Finally, we apply this middleware to our route using route_layer
to ensure every request to the root path is authenticated:
let app = Router::new()
.route("/", get(handler))
.route_layer(middleware::from_fn(auth));
This ensures that every request is checked for the valid API key before it reaches the handler.
2. Concepts and Explanations
Let’s break down the important parts of this example:
-
Middleware in Axum: Middleware in Axum is used to modify the request or response. In our case, we use middleware to check if the request includes a valid API key in the
Authorization
header. If the key matches the expected value, the request continues; otherwise, we reject it with401 Unauthorized
. -
Authorization Header: The
Authorization
header in HTTP requests is commonly used to send credentials. In our example, we expect the header to contain an API key,Bearer my_secret_api_key
. -
Route Layering: We used
route_layer(middleware::from_fn(auth))
to apply theauth
middleware to the route. This ensures that all requests to the root ("/") pass through our authentication check.
3. Challenges
Here’s a challenge for you:
- Challenge: Modify the middleware to accept different API keys or use a more complex key validation (e.g., checking keys from an environment variable or a database). How would you handle storing and rotating keys securely?
Try this challenge to expand your authentication system.
4. Recap and Conclusion
In this tutorial, we’ve created a simple API key authentication middleware in Axum. We started by building a basic Axum app, then added middleware to check the Authorization
header for a valid API key. If the key is valid, we show a success message; otherwise, we respond with 401 Unauthorized
.
This approach is easy to understand and can be extended to more complex authentication strategies as needed.
Next, you could explore more advanced authentication methods such as OAuth or JWT tokens. Thanks for following along—now you’re ready to secure your Axum applications!