Skip to main content

AuthDispatcher

Struct AuthDispatcher 

Source
pub struct AuthDispatcher { /* private fields */ }
Expand description

Central dispatcher for JWT and opaque token validation

Orchestrates key providers and claims plugins to validate tokens using a single configured plugin.

Implementations§

Source§

impl AuthDispatcher

Source

pub fn new( validation_config: ValidationConfig, config: &AuthConfig, registry: &PluginRegistry, ) -> Result<Self, ConfigError>

Create a new dispatcher with validation config and plugin.

§Errors

Returns ConfigError::UnknownPlugin if the configured provider is not in the registry.

Examples found in repository?
examples/dispatcher_usage.rs (line 171)
127async fn main() -> Result<(), Box<dyn std::error::Error>> {
128    let mut plugins = PluginRegistry::default();
129    plugins.register("demo", Arc::new(DemoClaimsPlugin));
130
131    let mut plugin_configs = HashMap::new();
132    plugin_configs.insert(
133        "demo".to_owned(),
134        PluginConfig::Oidc {
135            tenant_claim: "tenants".to_owned(),
136            roles_claim: "roles".to_owned(),
137        },
138    );
139
140    let config = AuthConfig {
141        mode: AuthModeConfig {
142            provider: "demo".to_owned(),
143        },
144        issuers: vec!["https://issuer.local".to_owned()],
145        audiences: vec!["demo-api".to_owned()],
146        plugins: plugin_configs,
147        ..AuthConfig::default()
148    };
149
150    let validation = ValidationConfig {
151        allowed_issuers: config.issuers.clone(),
152        allowed_audiences: config.audiences.clone(),
153        leeway_seconds: config.leeway_seconds,
154        require_uuid_subject: true,
155        require_uuid_tenants: true,
156    };
157
158    let subject = Uuid::new_v4();
159    let tenant = Uuid::new_v4();
160    let expires_at = OffsetDateTime::now_utc() + Duration::minutes(15);
161
162    let raw_claims = serde_json::json!({
163        "iss": "https://issuer.local",
164        "sub": subject.to_string(),
165        "aud": ["demo-api"],
166        "exp": expires_at.unix_timestamp(),
167        "tenant_id": tenant.to_string(),
168        "roles": ["viewer:read"]
169    });
170
171    let dispatcher = AuthDispatcher::new(validation, &config, &plugins)?
172        .with_key_provider(Arc::new(StaticKeyProvider::new(raw_claims)));
173
174    let claims = dispatcher.validate_jwt("demo-token").await?;
175    let perm_list = if claims.permissions.is_empty() {
176        "none".to_owned()
177    } else {
178        claims
179            .permissions
180            .iter()
181            .map(|p| format!("{}:{}", p.resource_pattern(), p.action()))
182            .collect::<Vec<_>>()
183            .join(", ")
184    };
185    println!(
186        "Validated token for subject {} with permissions {}",
187        claims.subject, perm_list
188    );
189
190    Ok(())
191}
Source

pub fn with_key_provider(self, provider: Arc<dyn KeyProvider>) -> Self

Add a key provider

Examples found in repository?
examples/dispatcher_usage.rs (line 172)
127async fn main() -> Result<(), Box<dyn std::error::Error>> {
128    let mut plugins = PluginRegistry::default();
129    plugins.register("demo", Arc::new(DemoClaimsPlugin));
130
131    let mut plugin_configs = HashMap::new();
132    plugin_configs.insert(
133        "demo".to_owned(),
134        PluginConfig::Oidc {
135            tenant_claim: "tenants".to_owned(),
136            roles_claim: "roles".to_owned(),
137        },
138    );
139
140    let config = AuthConfig {
141        mode: AuthModeConfig {
142            provider: "demo".to_owned(),
143        },
144        issuers: vec!["https://issuer.local".to_owned()],
145        audiences: vec!["demo-api".to_owned()],
146        plugins: plugin_configs,
147        ..AuthConfig::default()
148    };
149
150    let validation = ValidationConfig {
151        allowed_issuers: config.issuers.clone(),
152        allowed_audiences: config.audiences.clone(),
153        leeway_seconds: config.leeway_seconds,
154        require_uuid_subject: true,
155        require_uuid_tenants: true,
156    };
157
158    let subject = Uuid::new_v4();
159    let tenant = Uuid::new_v4();
160    let expires_at = OffsetDateTime::now_utc() + Duration::minutes(15);
161
162    let raw_claims = serde_json::json!({
163        "iss": "https://issuer.local",
164        "sub": subject.to_string(),
165        "aud": ["demo-api"],
166        "exp": expires_at.unix_timestamp(),
167        "tenant_id": tenant.to_string(),
168        "roles": ["viewer:read"]
169    });
170
171    let dispatcher = AuthDispatcher::new(validation, &config, &plugins)?
172        .with_key_provider(Arc::new(StaticKeyProvider::new(raw_claims)));
173
174    let claims = dispatcher.validate_jwt("demo-token").await?;
175    let perm_list = if claims.permissions.is_empty() {
176        "none".to_owned()
177    } else {
178        claims
179            .permissions
180            .iter()
181            .map(|p| format!("{}:{}", p.resource_pattern(), p.action()))
182            .collect::<Vec<_>>()
183            .join(", ")
184    };
185    println!(
186        "Validated token for subject {} with permissions {}",
187        claims.subject, perm_list
188    );
189
190    Ok(())
191}
Source

pub fn with_introspection_provider( self, provider: Arc<dyn IntrospectionProvider>, ) -> Self

Add an introspection provider

Source

pub async fn validate_jwt(&self, token: &str) -> Result<Claims, ClaimsError>

Validate a JWT token.

Workflow:

  1. Try each KeyProvider until one successfully validates the signature
  2. Extract issuer from token
  3. Use the configured plugin to normalize claims
  4. Run common validation (issuer, audience, exp, nbf, UUIDs)
  5. Return normalized claims
§Errors

Returns ClaimsError if signature validation, claim normalization, or validation fails.

Examples found in repository?
examples/dispatcher_usage.rs (line 174)
127async fn main() -> Result<(), Box<dyn std::error::Error>> {
128    let mut plugins = PluginRegistry::default();
129    plugins.register("demo", Arc::new(DemoClaimsPlugin));
130
131    let mut plugin_configs = HashMap::new();
132    plugin_configs.insert(
133        "demo".to_owned(),
134        PluginConfig::Oidc {
135            tenant_claim: "tenants".to_owned(),
136            roles_claim: "roles".to_owned(),
137        },
138    );
139
140    let config = AuthConfig {
141        mode: AuthModeConfig {
142            provider: "demo".to_owned(),
143        },
144        issuers: vec!["https://issuer.local".to_owned()],
145        audiences: vec!["demo-api".to_owned()],
146        plugins: plugin_configs,
147        ..AuthConfig::default()
148    };
149
150    let validation = ValidationConfig {
151        allowed_issuers: config.issuers.clone(),
152        allowed_audiences: config.audiences.clone(),
153        leeway_seconds: config.leeway_seconds,
154        require_uuid_subject: true,
155        require_uuid_tenants: true,
156    };
157
158    let subject = Uuid::new_v4();
159    let tenant = Uuid::new_v4();
160    let expires_at = OffsetDateTime::now_utc() + Duration::minutes(15);
161
162    let raw_claims = serde_json::json!({
163        "iss": "https://issuer.local",
164        "sub": subject.to_string(),
165        "aud": ["demo-api"],
166        "exp": expires_at.unix_timestamp(),
167        "tenant_id": tenant.to_string(),
168        "roles": ["viewer:read"]
169    });
170
171    let dispatcher = AuthDispatcher::new(validation, &config, &plugins)?
172        .with_key_provider(Arc::new(StaticKeyProvider::new(raw_claims)));
173
174    let claims = dispatcher.validate_jwt("demo-token").await?;
175    let perm_list = if claims.permissions.is_empty() {
176        "none".to_owned()
177    } else {
178        claims
179            .permissions
180            .iter()
181            .map(|p| format!("{}:{}", p.resource_pattern(), p.action()))
182            .collect::<Vec<_>>()
183            .join(", ")
184    };
185    println!(
186        "Validated token for subject {} with permissions {}",
187        claims.subject, perm_list
188    );
189
190    Ok(())
191}
Source

pub async fn validate_opaque(&self, token: &str) -> Result<Claims, ClaimsError>

Validate an opaque token via introspection

Workflow:

  1. Try each IntrospectionProvider until one succeeds
  2. Extract issuer from introspection response
  3. Use the configured plugin to normalize claims
  4. Run common validation
  5. Return normalized claims
§Errors

Returns ClaimsError if introspection, claim normalization, or validation fails.

Source

pub fn validation_config(&self) -> &ValidationConfig

Get validation config (for inspection/testing)

Source

pub fn plugin(&self) -> &Arc<dyn ClaimsPlugin>

Get the configured authentication plugin (for inspection/testing)

Source

pub async fn refresh_keys(&self) -> Result<(), Vec<ClaimsError>>

Trigger key refresh for all key providers.

§Errors

Returns a vector of ClaimsError if any provider fails to refresh keys.

Trait Implementations§

Source§

impl TokenValidator for AuthDispatcher

Implement TokenValidator trait for AuthDispatcher

Source§

fn validate_and_parse<'life0, 'life1, 'async_trait>( &'life0 self, token: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Claims, AuthError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Validate a JWT token and return normalized claims

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,