Crate aliri_axum
source ·Expand description
Axum utilities that make it easier to enforce OAuth2 authorization scopes in your application.
§Full Example
use aliri::jwt;
use aliri_clock::UnixTime;
use aliri_oauth2::{Authority, HasScope, Scope};
use aliri_tower::Oauth2Authorizer;
use axum::{
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Router,
};
use std::net::SocketAddr;
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize)]
pub struct CustomClaims {
iss: jwt::Issuer,
aud: jwt::Audiences,
sub: jwt::Subject,
scope: Scope,
}
impl jwt::CoreClaims for CustomClaims {
fn nbf(&self) -> Option<UnixTime> { None }
fn exp(&self) -> Option<UnixTime> { None }
fn aud(&self) -> &jwt::Audiences { &self.aud }
fn iss(&self) -> Option<&jwt::IssuerRef> { Some(&self.iss) }
fn sub(&self) -> Option<&jwt::SubjectRef> { Some(&self.sub) }
}
impl HasScope for CustomClaims {
fn scope(&self) -> &Scope {
&self.scope
}
}
mod scope {
aliri_axum::scope_guards! {
type Claims = super::CustomClaims;
pub scope AdminOnly = "admin";
pub scope List = "list";
pub scope Read = "read";
pub scope Write = "write";
pub scope ReadWrite = "read write";
pub scope ReadOrList = ["read" || "list"];
}
}
async fn admin_action(guard: scope::AdminOnly) -> String {
format!("You're an admin, {}!", guard.claims().sub)
}
async fn create_resource(_: scope::Write) -> Response {
(StatusCode::CREATED, "Created resource").into_response()
}
async fn read_resource(
scope::Read(claims): scope::Read,
Path(id): Path<String>,
) -> String {
format!("{} read resource {id}", claims.sub)
}
async fn construct_authority() -> Result<Authority, Box<dyn std::error::Error>> {
// Construct an authority
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let authority = construct_authority().await?;
let authorizer = Oauth2Authorizer::new()
.with_claims::<CustomClaims>()
.with_terse_error_handler();
// For verbose error handling, use `with_verbose_error_handler()`
// or your own custom handler.
// Build the router
let router = Router::new()
.route("/admin", get(admin_action))
.route("/resource", post(create_resource))
.route("/resource/{id}", get(read_resource))
.layer(authorizer.jwt_layer(authority));
// For verbose scope errors, add the following layer:
// .layer(axum::Extension(aliri_axum::VerboseAuthxErrors));
// Construct the server
let listener = tokio::net::TcpListener::bind(&SocketAddr::new([0, 0, 0, 0].into(), 3000))
.await?;
axum::serve(listener, router).await?;
Ok(())
}
Macros§
- Constructs an extractor that enables easily asserting that a provided token has the expected set of scopes.
- Convenience macro for services that need to define many scopes.
Structs§
- Add this type as an extension to produce verbose errors when authentication or authorization fails
Enums§
- An error indicating that the request could not be authorized
Traits§
- Defines a scope policy for a given endpoint guard