pub struct TestAuthority { /* private fields */ }testing only.Expand description
A self-consistent fake authorization server for a consumer’s tests: a
loopback HTTP server that serves OpenID Connect discovery, RFC 8414
discovery and a JWKS, with an issuer and jwks_uri that agree with each
other, plus everything needed to build a matching config and mint matching
tokens.
It publishes the throwaway RSA, P-256 and Ed25519 keys, so a token signed
with any algorithm TokenBuilder::alg accepts validates. Every published
key carries its own alg (one JWK per RSA algorithm, kids such as
test-key-a for RS256 and test-key-a-ps256), so a validator never logs
the alg-less-key warning for this JWKS.
The server runs until the TestAuthority is dropped, which stops its accept
loop.
§Security
Test-only: the keys are public. See the module docs.
§Examples
use oauth_resource_server::OAuthValidator;
use oauth_resource_server::testing::TestAuthority;
let authority = TestAuthority::start().await;
// Adjust anything before the config is resolved.
let config = authority.config(|c| c.required_scopes = vec!["notes:write".into()]);
let validator = OAuthValidator::new(&config).unwrap();
let ok = authority.token().scopes(["notes:write"]).sign();
assert!(validator.validate(&ok).await.is_ok());
assert_eq!(authority.jwks_fetches(), 1); // the JWKS was fetched onceImplementations§
Source§impl TestAuthority
impl TestAuthority
Sourcepub const RESOURCE: &'static str = "https://api.example.test/"
pub const RESOURCE: &'static str = "https://api.example.test/"
The protected resource config uses:
https://api.example.test/.
Sourcepub async fn start() -> Self
pub async fn start() -> Self
Start the fake authority on a loopback port. Its issuer is its own base
URL (http://127.0.0.1:<port>); discovery is served at both
/.well-known/openid-configuration and
/.well-known/oauth-authorization-server, and the JWKS at /jwks.
§Panics
If no loopback port can be bound. Must be called inside a tokio runtime.
Sourcepub fn issuer(&self) -> &str
pub fn issuer(&self) -> &str
The issuer this authority stamps into discovery and, by default, into
tokens: http://127.0.0.1:<port>.
Sourcepub fn jwks_fetches(&self) -> usize
pub fn jwks_fetches(&self) -> usize
How many times the JWKS has been fetched.
Sourcepub fn discovery_fetches(&self) -> usize
pub fn discovery_fetches(&self) -> usize
How many times a discovery document (OpenID Connect or RFC 8414) has
been fetched. Zero when the config sets jwks_uri.
Sourcepub fn set_response_delay(&self, delay: Duration)
pub fn set_response_delay(&self, delay: Duration)
Stall every response by delay, to test a slow authority. Applies to
connections accepted after the call; Duration::ZERO turns it off.
Sourcepub fn rotate_key(&self)
pub fn rotate_key(&self)
Rotate the RSA signing key: from now on token signs RSA
tokens with the other throwaway key (KEY_B_PEM after the first
rotation, KEY_A_PEM again after the second), and the JWKS is
republished with both RSA keys, as a real authority does while
tokens signed with the old key are still in flight. The P-256 and
Ed25519 keys are unaffected. Follow with
withdraw_old_key to model an authority that
retires the old key.
A token built before the rotation keeps the old key (the builder
captures the key when token is called). A running
validator learns the new key on an unknown-kid refetch (at most one per
60 seconds) or
OAuthValidator::refresh_now.
Sourcepub fn withdraw_old_key(&self)
pub fn withdraw_old_key(&self)
Withdraw the previous RSA key that rotate_key left
published: the JWKS then holds only the active RSA key. A validator that
has the old key cached keeps trusting it until its next successful
refresh (refresh_now or the
hourly background pass); after that, tokens signed with the old key
fail. A no-op when nothing is retained.
Sourcepub fn config(
&self,
adjust: impl FnOnce(&mut OAuthConfig),
) -> ResolvedOAuthConfig
pub fn config( &self, adjust: impl FnOnce(&mut OAuthConfig), ) -> ResolvedOAuthConfig
A ResolvedOAuthConfig for this authority, with neutral defaults:
issuerandjwks_uriare this authority’s own;audienceisAUDIENCE(https://api.example.test/audience) andresourceRESOURCE(https://api.example.test/);api:read(SCOPE) is the one required scope;- settings are named
oauth.*in errors (KeyNaming::Dotted); - everything else is the crate default, so
require_at_jwtis off.
The authority is plain http on loopback, which
OAuthConfig::resolve accepts without allow_insecure_http. adjust
edits the OAuthConfig before it is resolved: set require_at_jwt,
add audiences, clear jwks_uri to exercise discovery, and so on.
§Panics
If the adjusted config does not resolve (the message is the
ConfigError text, every problem at once), or
adjust sets enabled to false. Acceptable in a test helper; use
OAuthConfig::resolve directly to assert on a refusal.
§Examples
use oauth_resource_server::testing::TestAuthority;
let authority = TestAuthority::start().await;
let config = authority.config(|c| {
c.require_at_jwt = true;
c.jwks_uri = None; // find the keys through discovery instead
});
assert_eq!(config.issuer, authority.issuer());
assert!(config.jwks_uri.is_none());Sourcepub fn token(&self) -> TokenBuilder
pub fn token(&self) -> TokenBuilder
Start a TokenBuilder whose defaults produce a token the
config validator accepts: this authority’s issuer,
AUDIENCE, subject test-user, scope
SCOPE, valid for an hour, typ: at+jwt, signed RS256
with the currently active RSA key.