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, oauth2};
use aliri_tower::Oauth2Authorizer;
use axum::{
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Server, Router,
};
use std::net::SocketAddr;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct CustomClaims {
iss: jwt::Issuer,
aud: jwt::Audiences,
sub: jwt::Subject,
scope: oauth2::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 oauth2::HasScope for CustomClaims {
fn scope(&self) -> &oauth2::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 server = Server::bind(&SocketAddr::new([0, 0, 0, 0].into(), 3000))
.serve(router.into_make_service())
.await
.unwrap();
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