Skip to main content

OAuthValidatorBuilder

Struct OAuthValidatorBuilder 

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

Builds an OAuthValidator with options for its metadata and JWKS fetches; get one from OAuthValidator::builder.

With no option set, OAuthValidatorBuilder::build behaves exactly like OAuthValidator::new (which is implemented as that). Every option is checked by build, never earlier, and a refused one is a ValidatorError — nothing is silently ignored. Calling an option twice replaces the earlier value, except OAuthValidatorBuilder::add_root_certificate_pem, which adds.

Every protection a fetched key set gets still applies whatever is set here: the redirect policy, the https and allow_insecure_http rules, the response size cap, the key-count cap and per-key algorithm narrowing.

Debug never prints the proxy URL (it may carry a credential) or the initial key set, only whether they are set.

§Examples

An authorization server behind a private CA, reached through a proxy:

use std::sync::Arc;
use std::time::Duration;

use oauth_resource_server::{KeyNaming, OAuthConfig, OAuthValidator};

let resolved = OAuthConfig {
    enabled: true,
    issuer: "https://idp.internal.example.com/".into(),
    audience: "example-api".into(),
    resource: "https://api.example.com/".into(),
    required_scope: Some("api:read".into()),
    ..OAuthConfig::default()
}
.resolve(KeyNaming::Dotted("oauth"))?
.expect("OAuth is enabled");

let ca = std::fs::read("/etc/ssl/private-ca.pem")?;
let validator = Arc::new(
    OAuthValidator::builder(&resolved)
        .add_root_certificate_pem(&ca)
        .proxy("http://proxy.example.com:3128")
        .fetch_timeout(Duration::from_secs(5))
        .build()?,
);
validator.spawn_background_refresh();

Implementations§

Source§

impl OAuthValidatorBuilder

Source

pub fn add_root_certificate_pem(self, pem: &[u8]) -> Self

Trust the certificate(s) in pem as TLS root certificates for the metadata and JWKS fetches, in addition to the roots the TLS feature already trusts (the Mozilla set for rustls-tls, the OS store for rustls-tls-native-roots and native-tls) — never instead of them. For an authorization server behind a private or internal CA. When native-tls and a rustls feature are both enabled, reqwest uses native-tls, so the OS store is what these are added to.

pem may hold one certificate or a bundle of several (every CERTIFICATE block is added). Call it again to add another file. It works with every TLS feature.

§Security

A root added here can vouch for ANY host name, not only the authorization server’s, for this validator’s fetches: whoever holds the CA’s private key can serve signing keys this validator trusts. Add the CA that actually issues the authorization server’s certificate, and nothing broader.

Pass only real CA certificates. Under rustls a trust anchor keeps only its subject, public key and name constraints — its basicConstraints and keyUsage are ignored — so a CA:FALSE leaf certificate passed here becomes an anchor that can issue for any host.

§Errors

None here; OAuthValidatorBuilder::build returns ValidatorError::InvalidRootCertificate when pem holds no certificate, one the TLS backend cannot parse or use, or any private-key block (-----BEGIN … PRIVATE KEY-----): a resource server never needs a private key, so one here is a mistake, not ignored.

§Examples
use oauth_resource_server::OAuthValidator;

let validator = OAuthValidator::builder(resolved)
    .add_root_certificate_pem(&std::fs::read("/etc/ssl/private-ca.pem")?)
    .build()?;
Source

pub fn proxy(self, url: impl Into<String>) -> Self

Send the metadata and JWKS fetches through the proxy at url (http://host:port, or https:// for a TLS connection to the proxy itself; user:password@ in it is sent as proxy Basic auth). https fetches are tunnelled with CONNECT, so TLS still runs end to end to the authorization server and its certificate is still checked.

Without this, non-loopback fetches use reqwest’s own proxy handling, untouched: the HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY environment variables and, on macOS and Windows when reqwest’s system-proxy feature is on in the build, the system settings. Setting a proxy here turns all of those off, NO_PROXY included. Either way, a fetch of a loopback URL (localhost, *.localhost, 127.0.0.0/8, ::1) uses a separate client with no proxy at all: a loopback URL names this host, which through a proxy it would not, and a plain-http loopback fetch would cross the network in cleartext. That client resolves every name to ::1/127.0.0.1 itself, never through DNS, and a fetch it starts may not be redirected off loopback, so this proxy is never skipped for a non-loopback host. (A redirect from a non-loopback URL to a loopback one stays in the client the fetch started with; see the README’s security model.)

§Security

The URL may carry a credential, so it never appears in a log line or Debug other than redacted (***@), and a refused one never appears in the error at all. A plain-http proxy on a non-loopback host needs no opt-in: an https fetch through it is a CONNECT tunnel it cannot read or alter, and a plain-http fetch already needed allow_insecure_http for its own URL. A credential in such a proxy URL, though, would cross the network in cleartext, so that is refused unless allow_insecure_http is set (and then logged as a warn) — use an https:// proxy URL for a proxy that authenticates.

§Errors

None here; OAuthValidatorBuilder::build returns ValidatorError::InvalidProxy unless url is an absolute http or https URL with a host and nothing after the port (no path, query or fragment), with no space, control or non-ASCII character, and — when plain http on a non-loopback host — no credential unless allow_insecure_http is set. SOCKS proxies are not supported.

§Examples
use oauth_resource_server::OAuthValidator;

let validator = OAuthValidator::builder(&resolved)
    .proxy("http://proxy.example.com:3128")
    .build()
    .unwrap();
Source

pub fn fetch_timeout(self, timeout: Duration) -> Self

How long one metadata or JWKS request may take, connection through the last byte of the body; DEFAULT_FETCH_TIMEOUT (10 s) unless set.

A refresh holds the refresh lock for as long as its requests take (discovery can chain three), and a request whose key is not cached waits behind it, so this is also how long a stalled authorization server can hold up such a request.

§Errors

None here; OAuthValidatorBuilder::build returns ValidatorError::FetchTimeoutOutOfRange outside MIN_FETCH_TIMEOUT..=MAX_FETCH_TIMEOUT (1 s to 60 s), zero included.

§Examples
use std::time::Duration;

use oauth_resource_server::OAuthValidator;

let validator = OAuthValidator::builder(&resolved)
    .fetch_timeout(Duration::from_secs(3))
    .build()
    .unwrap();
Source

pub fn initial_jwks(self, json: &str) -> Self

Start with the keys in json, a JWK Set ({"keys": [...]}), already loaded: tokens they signed validate before — or without — any fetch. For a warm start through an authorization-server outage, or a key set shipped alongside the application. Read one from a file with std::fs::read_to_string and pass the text.

The set goes through exactly the checks a fetched one does: the 256 KiB size cap, at most 64 keys considered, and per-key narrowing (no HMAC oct key, no use: enc or key_ops-without-verify key, each key limited to the configured algorithms its own type and alg allow; an unusable entry is skipped).

The seeded keys are refreshed like fetched ones: the first refresh that succeeds (from OAuthValidator::spawn_background_refresh, OAuthValidator::refresh_now or an unknown kid) replaces them with the authorization server’s set, and a failed one keeps them. Until then, OAuthValidator::key_set_status reports them in keys (so OAuthValidator::is_ready is true from the start), with last_attempt and last_success still None: no fetch has happened, and last_success only ever means the authorization server answered. keys > 0 with last_success == None is how to tell seeded keys apart. Seeded keys count as held, so a failed background pass retries on the held-keys schedule (a minute, backing off to an hour), not the 5-second keyless one.

§Security

Seeded keys are trusted exactly like keys the authorization server served — until a refresh succeeds, even one it has since withdrawn. Seed only a key set taken from the authorization server itself.

§Errors

None here; OAuthValidatorBuilder::build returns ValidatorError::InvalidInitialJwks when json is over the size cap, not JSON, not a JWK Set, or holds no key usable under the configured algorithms.

§Examples
use oauth_resource_server::OAuthValidator;

// `jwks`: a JWK Set saved from the authorization server's jwks_uri.
let validator = OAuthValidator::builder(&resolved)
    .initial_jwks(jwks)
    .build()
    .unwrap();
assert!(validator.is_ready());
assert_eq!(validator.key_set_status().last_success, None);
Source

pub fn build(self) -> Result<OAuthValidator, ValidatorError>

Build the validator.

Does no network I/O, like OAuthValidator::new: keys are fetched on first use or by OAuthValidator::spawn_background_refresh (unless seeded). Logs the same startup warnings new does.

§Errors

Every error OAuthValidator::new returns, checked first; then ValidatorError::FetchTimeoutOutOfRange, ValidatorError::InvalidRootCertificate, ValidatorError::InvalidProxy or ValidatorError::InvalidInitialJwks for a refused option (the first one found, in that order).

Trait Implementations§

Source§

impl Clone for OAuthValidatorBuilder

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for OAuthValidatorBuilder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
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: Sized + 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: Sized + 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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<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