How to Implement Authentication in Rust (JWT, Sessions)
Use the jsonwebtoken crate with Axum's FromRequestParts extractor to validate tokens in the Authorization header.
use axum::{
extract::FromRequestParts,
http::{request::Parts, StatusCode},
response::{IntoResponse, Response},
Router,
};
use axum_extra::{
headers::{authorization::Bearer, Authorization},
TypedHeader,
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use std::sync::LazyLock;
static KEYS: LazyLock<DecodingKey> = LazyLock::new(|| DecodingKey::from_secret("secret".as_bytes()));
#[derive(Debug, Deserialize, Serialize)]
struct Claims {
sub: String,
exp: i64,
}
impl<S> FromRequestParts<S> for Claims
where
S: Send + Sync,
{
type Rejection = AuthError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let TypedHeader(Authorization(bearer)) = parts
.extract::<TypedHeader<Authorization<Bearer>>>()
.await
.map_err(|_| AuthError::InvalidToken)?;
let mut validation = Validation::default();
validation.validate_exp = true;
let token_data = decode::<Claims>(bearer.token(), &KEYS, &validation)
.map_err(|_| AuthError::InvalidToken)?;
Ok(token_data.claims)
}
}
#[derive(Debug)]
enum AuthError {
InvalidToken,
}
impl IntoResponse for AuthError {
fn into_response(self) -> Response {
(StatusCode::UNAUTHORIZED, "Invalid token").into_response()
}
}
#[tokio::main]
async fn main() {
let app = Router::new().route(
"/protected",
axum::routing::get(|claims: Claims| async move { format!("Hello {}", claims.sub) }),
);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
For sessions, use tower-sessions middleware to store state in Redis or memory instead of tokens.