| Crates.io | axum-firebase-middleware |
| lib.rs | axum-firebase-middleware |
| version | 0.1.0 |
| created_at | 2025-08-01 09:34:21.708607+00 |
| updated_at | 2025-08-01 09:34:21.708607+00 |
| description | Axum middleware for authenticating Firebase ID tokens |
| homepage | |
| repository | https://github.com/ekosutrisno/axum-firebase-middleware |
| max_upload_size | |
| id | 1776329 |
| size | 82,893 |
A production-ready Firebase JWT authentication middleware for Axum web applications. This crate provides secure token validation with automatic public key caching, comprehensive error handling, and built-in security features.
Add this to your Cargo.toml:
[dependencies]
axum-firebase-middleware = "0.1.0"
axum = "0.8"
tokio = { version = "1.0", features = ["full"] }
Basic usage:
use axum::{
routing::get,
Router,
Extension,
Json,
middleware::from_fn_with_state,
};
use axum_firebase_middleware::{firebase_auth_middleware, FirebaseConfig, FirebaseClaims};
use serde_json::json;
// Your protected handler
async fn protected_handler(
Extension(claims): Extension<FirebaseClaims>
) -> Json<serde_json::Value> {
Json(json!({
"message": "Successfully authenticated!",
"user_id": claims.user_id,
"email": claims.email,
"email_verified": claims.email_verified
}))
}
// Public handler (no authentication required)
async fn public_handler() -> Json<serde_json::Value> {
Json(json!({
"message": "This endpoint is public"
}))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging
env_logger::init();
// Create Firebase configuration
let firebase_config = FirebaseConfig::new("your-firebase-project-id".to_string())?
.with_cache_duration(3600)? // Cache keys for 1 hour
.with_max_token_age(std::time::Duration::from_secs(24 * 3600)); // 24 hour max token age
// Build the application with protected and public routes
let app = Router::new()
.route("/public", get(public_handler))
.nest("/api/v1",
Router::new()
.route("/protected", get(protected_handler))
.route_layer(from_fn_with_state(
firebase_config.clone(),
firebase_auth_middleware
))
)
.with_state(firebase_config);
// Run the server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("Server running on http://0.0.0.0:3000");
axum::serve(listener, app).await?;
Ok(())
}
Get your Firebase project ID from the Firebase Console:
On your client (web, mobile app), authenticate users and get ID tokens:
// Web example using Firebase SDK
import { getAuth, signInWithEmailAndPassword, getIdToken } from 'firebase/auth';
const auth = getAuth();
const userCredential = await signInWithEmailAndPassword(auth, email, password);
const idToken = await getIdToken(userCredential.user);
// Use this token in Authorization header
fetch('/api/v1/protected', {
headers: {
'Authorization': `Bearer ${idToken}`
}
});
The middleware automatically validates tokens and provides claims:
async fn user_profile(
Extension(claims): Extension<FirebaseClaims>
) -> Json<serde_json::Value> {
Json(json!({
"user_id": claims.user_id,
"email": claims.email,
"email_verified": claims.email_verified,
"auth_time": claims.auth_time,
"sign_in_provider": claims.firebase.sign_in_provider
}))
}
let config = FirebaseConfig::new("project-id".to_string())?
.with_cache_duration(1800)?; // Cache for 30 minutes
let config = FirebaseConfig::new("project-id".to_string())?
.with_max_token_age(Duration::from_secs(12 * 3600)); // 12 hours max
The middleware returns appropriate HTTP status codes:
401 Unauthorized: Invalid, expired, or malformed tokens503 Service Unavailable: Firebase key service unavailable429 Too Many Requests: Rate limiting (if implemented)413 Payload Too Large: Request body too large500 Internal Server Error: Configuration errorsCustom error handling:
use axum_firebase_middleware::FirebaseAuthError;
match validate_firebase_token(&token, &config).await {
Ok(claims) => {
// Token is valid, use claims
println!("User ID: {}", claims.user_id);
}
Err(FirebaseAuthError::InvalidTokenFormat(msg)) => {
println!("Invalid token format: {}", msg);
}
Err(FirebaseAuthError::ValidationFailed(msg)) => {
println!("Token validation failed: {}", msg);
}
Err(e) => {
println!("Other error: {}", e);
}
}
jsonwebtoken crateThe crate uses the log crate for structured logging:
// Initialize logging in your main function
env_logger::init();
// The middleware will log:
// - INFO: Successful key refreshes
// - WARN: Authentication failures, key fetch issues
// - DEBUG: Cache hits, successful validations
// - ERROR: Configuration issues, repeated failures
Log levels:
DEBUG: Cache operations, successful validationsINFO: Key refresh operationsWARN: Authentication failures, recoverable errorsERROR: Configuration issues, persistent failuresContributions are welcome! Please feel free to submit a Pull Request.
cargo testcargo fmtcargo clippyThis project is licensed under either of:
at your option.