pub struct RequireScopes { /* private fields */ }tower only.Expand description
A route-level scope requirement: a tower::Layer for the routes (or
services) that need more than the authentication layer in front of them
requires — say, a write scope on the routes that write. It adds no key
cache and no validation of its own: it reads the credential that layer
accepted from the request’s extensions and checks it against its scopes
(all-of, the same matching as the validator’s own check,
AuthorizedToken::require_scopes).
Place it INSIDE (behind) an authentication layer — the axum AuthLayer
or an HttpAuthLayer, which both mark every request they pass:
| The request… | Answer |
|---|---|
| carries an OAuth token with every scope | served |
| carries an OAuth token missing one | 403, with the layer’s refusal body and a challenge naming the layer’s scopes followed by these (crate::refusal_for_scopes’s, for the same scopes) |
| carries a static token | 403 the same way — a static token has no scopes — unless static_token_bypasses_scopes |
carries no credential (an optional layer passed it through) | the layer’s own 401 and challenge |
passed an allow_unauthenticated layer with no credential | 401 with crate::DEFAULT_STATIC_CHALLENGE, logged at error |
| passed NO authentication layer (mounted outside it) | 500, empty body, logged at error — never served |
An empty requirement serves every request the layer let through (a 500
without a layer all the same). Nothing in the credential or the scopes
reaches the response beyond the challenge; refusals are logged under
oauth_resource_server::http_layer (403 at info, with the required and
present scopes).
The credential judged is the innermost Credential a layer accepted.
A layer checks static tokens first, so a request presenting both a
static token and an OAuth token (in two sources) is judged by the static
token.
The same type is oauth_resource_server::axum::RequireScopes. Under
axum, the layer’s refusal body comes from its on_reject; behind an
HttpAuthLayer, from its on_reject
when the response body types match, else ResBody::default().
§Examples
Behind an HttpAuthLayer, on any tower stack:
use std::sync::Arc;
use http::{Request, Response};
use oauth_resource_server::OAuthValidator;
use oauth_resource_server::http_layer::{HttpAuthLayer, RequireScopes};
use tower::{ServiceBuilder, service_fn};
let writes = ServiceBuilder::new()
// Outermost first: authenticate, then require the write scope.
.layer(HttpAuthLayer::builder().oauth(oauth).build().unwrap())
.layer(RequireScopes::new(["docs:write"]))
.service(service_fn(|_request: Request<String>| async {
Ok::<_, std::convert::Infallible>(Response::new(String::from("written")))
}));Under axum (feature axum, as oauth_resource_server::axum::RequireScopes),
.route_layer(RequireScopes::new(["docs:write"])) on the routes that
need it, before (so inside) .route_layer(auth).
§Panics
RequireScopes::new panics when a scope is not an RFC 6749 §3.3
scope-token (empty, or holding a space, ", \, a control or non-ASCII
character): no token can carry one, so the route could never be reached.
Use it for scopes written as literals in code; for scopes read from
configuration use RequireScopes::try_new, which returns the error
instead.
Implementations§
Source§impl RequireScopes
impl RequireScopes
Sourcepub fn new(scopes: impl IntoIterator<Item = impl Into<String>>) -> Self
pub fn new(scopes: impl IntoIterator<Item = impl Into<String>>) -> Self
Require every scope in scopes (deduplicated). For literals in code;
see RequireScopes::try_new for scopes from configuration.
§Panics
When a scope is not an RFC 6749 §3.3 scope-token; see the type’s docs.
Sourcepub fn try_new(
scopes: impl IntoIterator<Item = impl Into<String>>,
) -> Result<Self, InvalidScope>
pub fn try_new( scopes: impl IntoIterator<Item = impl Into<String>>, ) -> Result<Self, InvalidScope>
RequireScopes::new for scopes read from configuration: the same
requirement, or the first scope that is not a scope-token.
§Errors
InvalidScope, naming the offending scope.
§Examples
use oauth_resource_server::http_layer::RequireScopes;
assert!(RequireScopes::try_new(["docs:write"]).is_ok());
let err = RequireScopes::try_new(["docs:write", "two words"]).unwrap_err();
assert_eq!(err.scope(), "two words");Sourcepub fn static_token_bypasses_scopes(self) -> Self
pub fn static_token_bypasses_scopes(self) -> Self
Let a static token through instead of refusing it with 403.
§Security
A static token then reaches these routes whatever they require — the static token is treated as holding every scope. Use it only where the static token is meant to be a full-access key.