Skip to main content

authenticate

Function authenticate 

Source
pub async fn authenticate<'a>(
    candidates: impl IntoIterator<Item = &'a str>,
    static_token: Option<&str>,
    oauth: Option<&OAuthValidator>,
) -> Result<Credential, TokenRejection>
Expand description

Check every candidate credential against every configured mechanism, and accept if ANY candidate satisfies ANY mechanism.

candidates is every value that could carry a credential — typically the token from Authorization: Bearer <token> and, for an application that also takes one, the value of a raw API-key header — in any order, however many are present. The presence of one candidate never decides whether another is looked at, so an invalid credential in one place cannot hide a valid one in another.

Candidates are used verbatim; an empty or whitespace-only candidate counts as absent. static_token of None or Some("") means no static token is configured (a blank secret must never be matchable); oauth of None means OAuth is off. With neither configured nothing can succeed: this function never passes a request through. Deciding to run without authentication is the caller’s explicit choice, made before calling it (see crate::static_token_policy).

§Order

Every candidate is first compared with the static token, in constant time (subtle; the lengths are not hidden — see StaticTokens’ “Security”, the same comparison over a one-entry set). A match is decisive and needs no network, so the common static-token request never reaches the JWT machinery or depends on the authorization server being up. Only then is every candidate validated through OAuth, in order, until one is accepted — first against the signing keys already held, and only if that accepts none of them is a candidate whose kid is not held allowed to trigger a key refetch. A foreign JWT in one source therefore never makes a request wait on the authorization server when another candidate’s key is already cached.

§Result

Map a refusal to a response the same way the axum layer does: 403 with OAuthValidator::insufficient_scope_challenge for InsufficientScope, 401 with OAuthValidator::invalid_token_challenge for everything else, and (with OAuth configured) the challenge in WWW-Authenticate on both.

§Errors

A TokenRejection, chosen as described under “Result” above. This function logs nothing; logging the refusal is the caller’s job.

§Panics

Outside a Tokio 1.x runtime, when an OAuth candidate’s signing key has to be fetched (see OAuthValidator’s “Runtime” section). A static-token match, or a key already held, needs no runtime.

§Security

The static token is compared in constant time, but its length is not hidden. Candidates are never trimmed or normalized, so a static token is matched only byte for byte.

To accept several static tokens (key rotation, one key per client) and learn which one matched, use authenticate_with_static_tokens: this function is that one over a one-entry set, the same code.

§Examples

use oauth_resource_server::{Credential, TokenRejection, authenticate};

// Static token only (no OAuth validator): a junk value in one header does
// not stop the key in another from being accepted.
let key = Some("example-static-key");
let ok = authenticate(["junk", "example-static-key"], key, None).await;
assert_eq!(ok, Ok(Credential::StaticToken));

assert_eq!(authenticate([" ", ""], key, None).await, Err(TokenRejection::Missing));
assert!(matches!(
    authenticate(["guess"], key, None).await,
    Err(TokenRejection::Invalid(_))
));