| Crates.io | oauth2-passkey-axum |
| lib.rs | oauth2-passkey-axum |
| version | 0.1.3 |
| created_at | 2025-06-22 17:37:33.467026+00 |
| updated_at | 2025-07-11 18:44:14.212431+00 |
| description | Axum integration for oauth2-passkey authentication library |
| homepage | https://github.com/ktaka-ccmp/oauth2-passkey |
| repository | https://github.com/ktaka-ccmp/oauth2-passkey |
| max_upload_size | |
| id | 1721801 |
| size | 273,117 |
Axum web framework integration for the oauth2-passkey authentication library.
This crate provides ready-to-use Axum handlers, middleware, and UI components for OAuth2 and passkey authentication in your Axum web applications.
Try out demo to get familiarize yourself with the usage of the library.
Add to your Cargo.toml:
[dependencies]
oauth2-passkey-axum = "0.1"
# Required: Base URL of your application
ORIGIN=https://yourdomain.com
# Database configuration
GENERIC_DATA_STORE_TYPE=sqlite
GENERIC_DATA_STORE_URL=sqlite:data/auth.db
# Cache configuration
GENERIC_CACHE_STORE_TYPE=redis
GENERIC_CACHE_STORE_URL=redis://localhost:6379
# OAuth2 providers
OAUTH2_GOOGLE_CLIENT_ID=your_google_client_id
OAUTH2_GOOGLE_CLIENT_SECRET=your_google_client_secret
# Optional: Server secret (for token signing)
AUTH_SERVER_SECRET=your_32_character_secret_key_here
See dot.env.example for available options.
use axum::{Router, response::Html};
use oauth2_passkey_axum::{oauth2_passkey_router, init, O2P_ROUTE_PREFIX};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize authentication (reads configuration from environment variables)
init().await?;
// Create your application router
let app = Router::new()
.route("/", axum::routing::get(|| async { Html("Hello World!") }))
// Add authentication routes (default: /o2p, configurable via O2P_ROUTE_PREFIX env var)
.nest(O2P_ROUTE_PREFIX.as_str(), oauth2_passkey_router())
.merge(/* other routes */);
// Start server
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
Protect your routes with authentication middleware:
use oauth2_passkey_axum::{is_authenticated_user_redirect, AuthUser};
use axum::{middleware::from_fn, routing::get, Router, response::Html, Extension};
async fn protected_handler(Extension(user): Extension<AuthUser>) -> Html<String> {
Html(format!("Hello, {}! You are authenticated.", user.account))
}
let app = Router::new()
.route("/protected", get(protected_handler).route_layer(from_fn(is_authenticated_user_redirect));
Alternatively, use the AuthUser extractor directly (no middleware needed):
use oauth2_passkey_axum::AuthUser;
use axum::{routing::get, Router, response::Html};
async fn protected_handler(user: AuthUser) -> Html<String> {
Html(format!("Hello, {}! You are authenticated.", user.account))
}
let app = Router::new()
.route("/protected", get(protected_handler));
default = ["admin-ui", "user-ui"] - Enable all UI componentsadmin-ui - Include admin interface for user managementuser-ui - Include user authentication pagesDisable features you don't need:
[dependencies]
oauth2-passkey-axum = { version = "0.1", default-features = false, features = ["user-ui"] }
When you include oauth2_passkey_router(), all authentication endpoints are available under /o2p by default.
You can change this prefix by setting the O2P_ROUTE_PREFIX environment variable.
OAuth2:
GET /o2p/oauth2/google - Start Google OAuth2 loginGET|POST /o2p/oauth2/authorized - OAuth2 callback handlerPasskey:
POST /o2p/passkey/register/start - Begin passkey registrationPOST /o2p/passkey/register/finish - Complete registrationPOST /o2p/passkey/auth/start - Begin passkey authenticationPOST /o2p/passkey/auth/finish - Complete authenticationSession Management:
GET /o2p/user/info - Get current user data (JSON)GET /o2p/user/csrf_token - Get CSRF tokenGET /o2p/user/logout - End user sessionUser Interface (requires user-ui feature):
GET /o2p/ui/login - Login pageGET /o2p/ui/summary - User dashboardAdmin Interface (requires admin-ui feature):
GET /o2p/admin/list_users - User management (admin only)GET /o2p/admin/user/{user_id} - User details (admin only)User Account:
PUT /o2p/user/update - Update user accountDELETE /o2p/user/delete - Delete own accountCredentials:
GET /o2p/passkey/credentials - List passkey credentialsGET /o2p/oauth2/accounts - List OAuth2 accountsDELETE /o2p/passkey/credentials/{id} - Remove passkeyDELETE /o2p/oauth2/accounts/{provider}/{id} - Remove OAuth2 account| Middleware | User Data | Error Response | Use Case |
|---|---|---|---|
is_authenticated_redirect |
❌ | Redirect | Browser pages |
is_authenticated_401 |
❌ | HTTP 401 | API endpoints |
is_authenticated_user_redirect |
✅ | Redirect | Browser pages with user info |
is_authenticated_user_401 |
✅ | HTTP 401 | API endpoints with user info |
Quick Guide:
*_user_* variants*_redirect | API? → Use *_401*_user_* variants example:
When you use a *_user_* middleware, the authenticated user is injected as an Extension<AuthUser>. You can then access user info in your handler like this:
use axum::{
Extension,
response::{Html, IntoResponse},
};
use oauth2_passkey_axum::AuthUser;
async fn protected(Extension(user): Extension<AuthUser>) -> impl IntoResponse {
Html(format!("<h1>Hello, {}!</h1>", user.account))
}
This pattern is useful when you want both authentication enforcement and access to user data in your handler—such as for rendering templates or personalized responses.
This library includes built-in CSRF protection for all authenticated routes. CSRF tokens are automatically:
AuthUser extractor (user.csrf_token)/o2p/user/csrf_token endpointBasic Usage:
// Include in headers (automatically verified)
fetch('/api/protected', {
method: 'POST',
headers: {'X-CSRF-Token': csrfToken},
credentials: 'include'
})
For detailed implementation guide including form-based CSRF and manual verification, see our CSRF Protection Guide.
This crate is built on top of oauth2-passkey. See that crate's documentation for core authentication concepts and advanced usage.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.