Skip to main content

ServiceBuilder

Struct ServiceBuilder 

Source
pub struct ServiceBuilder<S: Storage, C: Clock> { /* private fields */ }
Available on crate feature http only.
Expand description

Builds the router. Construct, attach the seams the interactive endpoints need, then build.

§The interactive endpoints refuse until they are wired

with_subject_resolver alone is NOT enough to run an authorization server safely, and this is the one thing to read in this file. It answers “who is this user”; RFC 6749 s10.12 also demands “did the user knowingly agree”. Wire with_approval_resolver and with_csrf_tokens too, or the authorization endpoint and the device verification form refuse rather than guessing that silence means yes.

Implementations§

Source§

impl<S: Storage + 'static, C: Clock + 'static> ServiceBuilder<S, C>

Source

pub fn new(server: Arc<AuthorizationServer<S, C>>) -> Self

Start from a running server. Arc rather than ownership so the host keeps its handle for administration (client registration, sweeping) while the router serves the same instance.

Source

pub fn with_subject_resolver<F>(self, resolver: F) -> Self
where F: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static,

Supply the host’s answer to “who is the logged-in user for this request”.

The authorization endpoint cannot mint a code without a resource owner, and the device verification page cannot approve a grant without one. This crate has no login UI and no session model by design, so without a resolver both endpoints refuse with 403 rather than inventing a user.

This resolver is IDENTITY ONLY. It does not express approval; see with_approval_resolver.

Source

pub fn with_approval_resolver<F>(self, resolver: F) -> Self
where F: Fn(&ApprovalRequest<'_>) -> ApprovalDecision + Send + Sync + 'static,

Supply the host’s answer to “has this user knowingly agreed to this exact request”.

RFC 6749 s10.12 requires the AS to “ensure that the malicious client cannot obtain authorization without the awareness and explicit consent of the resource owner”. An authorization endpoint that mints a code as soon as it knows who the user is satisfies neither half: any cross-site top-level navigation makes a logged-in user’s browser hand a registered client a code they never asked to issue. PKCE and exact redirect-URI matching bound WHO may redeem that code; they say nothing about whether it should have existed.

With NO resolver the authorization endpoint refuses with 403 and issues nothing. That is deliberate and it is a behaviour change: a host that previously wired only with_subject_resolver was running an AUTO-APPROVING authorization server, and the fix is to say what the approval step is rather than to leave it implied.

Return ApprovalDecision::Respond to render a consent screen and finish the flow on a later request; return ApprovalDecision::Approve only once the user has actually agreed.

Source

pub fn with_authentication_reporter<F>(self, reporter: F) -> Self
where F: Fn(&HeaderMap) -> Option<Authentication> + Send + Sync + 'static,

Available on crate feature consent only.

Supply the host’s answer to “when, and how, did you authenticate this user”.

REQUIRED for RFC 9470 step-up authentication and useless without it. A client answering a resource server’s insufficient_user_authentication challenge repeats its authorization request with acr_values and/or max_age; this server enforces those against whatever the reporter returns, and a host with no reporter wired fails every such request. That is the correct answer rather than a bug: an authorization server that cannot say when the user logged in cannot honestly claim they logged in recently.

Ordinary requests, which carry neither parameter, are unaffected whether this is wired or not.

The report is taken at FACE VALUE. This crate cannot authenticate anyone and has nothing to check it against; see the crate::consent module docs.

Source

pub fn with_csrf_tokens<I, V>(self, issue: I, consume: V) -> Self
where I: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static, V: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static,

Supply the host’s session-bound CSRF token for the device verification form.

issue is called when the form is RENDERED: it mints a token, binds it to whatever session the request carries, and returns it to be embedded in the form. consume is called when the form is SUBMITTED: it returns the token that session was last issued AND invalidates it, which is what makes the token single use. The router compares the submitted token with the consumed one in constant time; a mismatch, or either hook answering None, is a refusal.

Why this is a seam and not something the library does: approving a device grant binds a third party’s grant to the logged-in user, so RFC 6749 s10.12’s CSRF requirement applies with full force, and the countermeasure has to be bound to the SESSION. This crate has no session store and will not grow one. With no hooks the verification endpoint renders no form and approves nothing, because a form that works and is forgeable is worse than no form at all.

Source

pub fn dangerously_disable_verification_protections(self) -> Self

Turn OFF the device verification form’s CSRF token requirement, its Origin check, and its affirmative-action requirement.

FOR NON-BROWSER TEST HARNESSES ONLY. On an endpoint a browser can reach this re-enables the complete RFC 6749 s10.12 cross-site forced-approval chain: an attacker starts a device grant for a client they control, gets any authenticated victim’s browser to POST the user_code, and polls out an access token and a refresh token for the victim’s account. That is account takeover, and it needs no interaction beyond loading a page.

It exists because this crate’s black-box conformance harness drives the verification endpoint with an HTTP client and no browser session, so it cannot hold a CSRF token. It is spelled this loudly so that it is greppable, and so that no production host reaches for it without having read what it does.

Source

pub fn build(self) -> Result<AuthorizationService<S, C>, ServiceError>

Derive the routes from the metadata document and build the service.

§Errors

ServiceError when the configuration advertises something this service cannot serve.

Auto Trait Implementations§

§

impl<S, C> !RefUnwindSafe for ServiceBuilder<S, C>

§

impl<S, C> !UnwindSafe for ServiceBuilder<S, C>

§

impl<S, C> Freeze for ServiceBuilder<S, C>

§

impl<S, C> Send for ServiceBuilder<S, C>

§

impl<S, C> Sync for ServiceBuilder<S, C>

§

impl<S, C> Unpin for ServiceBuilder<S, C>

§

impl<S, C> UnsafeUnpin for ServiceBuilder<S, C>

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<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

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