pub struct HttpAuthLayerBuilder<R = EmptyRefusal> { /* private fields */ }tower only.Expand description
Builder for an enforcing HttpAuthLayer; see HttpAuthLayer::builder.
Implementations§
Source§impl<R> HttpAuthLayerBuilder<R>
impl<R> HttpAuthLayerBuilder<R>
Sourcepub fn static_token(self, token: impl Into<String>) -> Self
pub fn static_token(self, token: impl Into<String>) -> Self
Accept this static token (compared in constant time). An empty string counts as no token.
§Security
Setting the token here bypasses accept_static_bearer, which only
crate::static_token_policy reads; with OAuth configured, prefer
HttpAuthLayerBuilder::build_with_decision. The token’s length is not
hidden by the comparison, and Debug output shows it as <redacted>.
Sourcepub fn optional_static_token(self, token: Option<String>) -> Self
pub fn optional_static_token(self, token: Option<String>) -> Self
HttpAuthLayerBuilder::static_token when Some.
Sourcepub fn static_tokens(self, tokens: StaticTokens) -> Self
pub fn static_tokens(self, tokens: StaticTokens) -> Self
Accept every token in tokens (each compared in constant time, every
entry every time; see StaticTokens), replacing a set given
earlier. On a match the request’s extensions get
Credential::StaticToken and the StaticTokenMatch naming the
entry’s label.
Combines with the other static-token settings exactly as the axum
layer’s AuthLayerBuilder::static_tokens does: with
static_token, both are accepted (a secret in
both counts once, under the set’s label); with
build_with_decision, see that method.
An empty set counts as no static token, so it does not satisfy the
fail-closed build on its own.
§Security
Like static_token, this bypasses
accept_static_bearer unless the layer is built with
build_with_decision. Debug output
shows the count and labels, never a secret.
§Examples
use oauth_resource_server::StaticTokens;
use oauth_resource_server::http_layer::HttpAuthLayer;
let tokens = StaticTokens::new()
.with(Some("current"), "example-key-old")
.and_then(|t| t.with(Some("next"), "example-key-new"))
.unwrap();
let auth = HttpAuthLayer::builder().static_tokens(tokens).build().unwrap();Sourcepub fn optional_static_tokens(self, tokens: Option<StaticTokens>) -> Self
pub fn optional_static_tokens(self, tokens: Option<StaticTokens>) -> Self
HttpAuthLayerBuilder::static_tokens when Some; None clears a
set given earlier.
Sourcepub fn oauth(self, validator: Arc<OAuthValidator>) -> Self
pub fn oauth(self, validator: Arc<OAuthValidator>) -> Self
Accept OAuth access tokens this validator accepts.
Sourcepub fn optional_oauth(self, validator: Option<Arc<OAuthValidator>>) -> Self
pub fn optional_oauth(self, validator: Option<Arc<OAuthValidator>>) -> Self
HttpAuthLayerBuilder::oauth when Some.
Sourcepub fn sources(
self,
sources: impl IntoIterator<Item = CredentialSource>,
) -> Self
pub fn sources( self, sources: impl IntoIterator<Item = CredentialSource>, ) -> Self
Where to read credentials from, replacing the default
[CredentialSource::authorization_bearer()]. Every source is checked,
whatever the others hold.
Sourcepub fn static_challenge(self, challenge: Option<HeaderValue>) -> Self
pub fn static_challenge(self, challenge: Option<HeaderValue>) -> Self
The WWW-Authenticate challenge every 401 carries when NO OAuth
validator is configured; with one, the validator’s challenges are used
and this is ignored. Default: crate::DEFAULT_STATIC_CHALLENGE.
None sends no challenge at all and leaves any WWW-Authenticate an
on_reject callback set untouched. That departs from
RFC 9110 §15.5.2 (a 401 MUST carry a challenge); use it only to keep an
existing API’s responses unchanged.
Sourcepub fn optional(self) -> Self
pub fn optional(self) -> Self
Let a request that presents NO credential through, unauthenticated, with
nothing inserted into its extensions; a credential that is presented but
refused is refused exactly as without this. “No credential” is decided
exactly as by the axum layer’s optional() (see its documentation): every
value of every configured source header absent or blank, where a value
that is not visible ASCII, a non-blank later value of a repeated header,
a DPoP-scheme value and a tab-separated Bearer token all count as
presented. Any Credential/AuthorizedToken an outer layer
inserted is removed first.
§Security
Not a way around the fail-closed build: build still
requires a static token or an OAuth validator. Every handler behind an
optional layer must treat a request with no Credential in its
extensions as unauthenticated.
Sourcepub fn require_scopes(
self,
scopes: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn require_scopes( self, scopes: impl IntoIterator<Item = impl Into<String>>, ) -> Self
Require every scope in scopes (all-of) of every credential this
layer accepts, on top of the validator’s own required_scopes —
replacing scopes given earlier. The same validator, so the same key
cache: no second validator is needed for routes that need more.
Behaves exactly as the axum layer’s AuthLayerBuilder::require_scopes:
an OAuth token missing one is refused with 403 and a challenge naming
the validator’s required scopes followed by these (see
crate::refusal_for_scopes); a static token — it has no scopes — is
refused the same way unless
static_token_bypasses_scopes;
an optional layer still passes a request that
presents nothing. A layer that lets everything through
(HttpAuthLayer::allow_unauthenticated) checks nothing, this
included, which is why build_with_decision
refuses an Unauthenticated decision on a builder with scopes
(AuthLayerError::ScopesWithoutAuthentication) rather than drop them.
For a requirement on some routes only, put a RequireScopes layer
on them instead, behind this one.
§Examples
use std::sync::Arc;
use oauth_resource_server::OAuthValidator;
use oauth_resource_server::http_layer::HttpAuthLayer;
let writes = HttpAuthLayer::builder()
.oauth(oauth)
.require_scopes(["docs:write"])
.build()
.unwrap();Sourcepub fn static_token_bypasses_scopes(self) -> Self
pub fn static_token_bypasses_scopes(self) -> Self
Let a static token pass require_scopes
instead of refusing it with 403: the static token counts as holding
every scope. Without require_scopes it changes nothing.
§Security
Opt in only where the static token is meant to be a full-access key.
Sourcepub fn on_reject<B, F>(self, f: F) -> HttpAuthLayerBuilder<F>
pub fn on_reject<B, F>(self, f: F) -> HttpAuthLayerBuilder<F>
Build a refusal’s response (its body and any extra headers, such as
Content-Type) — for an API whose errors are, say, JSON. Without it a
refusal’s body is ResBody::default().
The callback shapes the response only; it cannot change the outcome.
Whatever it returns, the status is set to RejectContext::status and
WWW-Authenticate to the layer’s challenge, replacing any the callback
set (only with static_challenge(None) and no OAuth are the callback’s
headers left as they are). Never put TokenRejection::Invalid’s
reason in the body.
§Examples
use http::{Response, header::CONTENT_TYPE};
use oauth_resource_server::http_layer::{HttpAuthLayer, RejectContext};
let auth = HttpAuthLayer::builder()
.static_token("example-static-key")
.on_reject(|cx: RejectContext<'_>| {
Response::builder()
.header(CONTENT_TYPE, "application/json")
.body(format!(r#"{{"error":"{}"}}"#, cx.status.as_u16()))
.unwrap()
})
.build()
.unwrap();Sourcepub fn build_with_decision(
self,
decision: StaticTokenDecision,
) -> Result<HttpAuthLayer<R>, AuthLayerError>
pub fn build_with_decision( self, decision: StaticTokenDecision, ) -> Result<HttpAuthLayer<R>, AuthLayerError>
Build the layer a crate::static_token_policy decision calls for,
keeping this builder’s other settings. The decision’s static token (if
any) replaces one set on this builder;
StaticTokenDecision::Unauthenticated yields the
allow_unauthenticated
pass-through.
A static_tokens set follows the decision, as
for the axum layer’s AuthLayerBuilder::build_with_decision: kept, and
merged with the decision’s token, when the decision carries one
(StaticOnly/StaticAndOAuth); dropped with it on StaticIgnored
(accept_static_bearer: false wins); refused alongside OAuthOnly or
Unauthenticated, which were decided without any static token.
§Errors
AuthLayerError::DecisionNeedsOAuth when the decision was made with
OAuth on and no validator was given,
AuthLayerError::DecisionWithoutOAuth when it was made with OAuth off
(including Unauthenticated) and one was given, then
AuthLayerError::DecisionWithoutStaticToken for a non-empty
static_tokens set with an OAuthOnly or Unauthenticated decision,
then AuthLayerError::ScopesWithoutAuthentication for
require_scopes with an Unauthenticated
decision. Otherwise as HttpAuthLayerBuilder::build.
Sourcepub fn build(self) -> Result<HttpAuthLayer<R>, AuthLayerError>
pub fn build(self) -> Result<HttpAuthLayer<R>, AuthLayerError>
Build the layer.
§Errors
AuthLayerError::NoCredential with neither a non-blank static token
(from static_token or a non-empty
static_tokens set) nor an OAuth validator;
AuthLayerError::NoSources with an empty
source list; AuthLayerError::InvalidChallenge when the validator’s
challenge is not a valid header value (only reachable from a
hand-edited resolved config); AuthLayerError::InvalidScope when a
require_scopes entry is not a scope-token;
AuthLayerError::ScopesNeedOAuth for require_scopes with no OAuth
validator and no
static_token_bypasses_scopes.
Trait Implementations§
Source§impl<R> Debug for HttpAuthLayerBuilder<R>
Hand-written so the static token never reaches a log line through {:?}.
impl<R> Debug for HttpAuthLayerBuilder<R>
Hand-written so the static token never reaches a log line through {:?}.