| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- //! `test(e2e_cimd)` end-to-end coverage of Client ID Metadata Documents
- //! (CIMD draft §3/§4.1/§5) through the real router and the injected fetcher
- //! seam (`AppState::cimd_fetcher`).
- //!
- //! Every world installs its own counting fetcher, so the cache behavior is
- //! observed rather than assumed: one fetch for the first resolution, zero for
- //! every cached/registered/inactive resolution within the TTL. Each invalid
- //! document row gets its own world (and its own URL) so the short-lived
- //! `cimd:invalid:` marker cannot replay across rows.
- //!
- //! The fetcher is the ONLY document source: no test performs a real network
- //! call.
- use std::{
- future::Future,
- sync::{
- Arc,
- atomic::{AtomicUsize, Ordering},
- },
- };
- use base64::Engine as _;
- use d_oauth::{
- db::{TokenEndpointAuthMethod, client_repo},
- protocol::cimd::{CimdDocument, CimdError, CimdFetchFuture, CimdFetcher, CimdLimits},
- };
- use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
- use ring::signature::{Ed25519KeyPair, KeyPair as _};
- use serde_json::{Value, json};
- use time::OffsetDateTime;
- #[allow(dead_code)]
- #[path = "e2e/harness.rs"]
- mod harness;
- #[path = "e2e/dpop.rs"]
- mod dpop;
- #[allow(dead_code)]
- #[path = "e2e/browser.rs"]
- mod browser;
- use browser::{
- Browser, SESSION_COOKIE, cookie_value, get_consent, hidden_input, location_of, oauth2_pkce,
- post_consent, post_login, query_value, seed_user, totp_now,
- };
- use harness::{
- TestError, World, attach_redirect, ensure, fail, json_body, open_rate_limit, seed_client,
- seed_scope,
- };
- /// Registration-free client identifier: an `https://` document URL.
- const CLIENT_URL: &str = "https://cimd.example.test/client.json";
- /// Loopback redirect declared by the fixture documents (RFC 8252 §7.3).
- const REDIRECT: &str = "http://127.0.0.1:9/cb";
- /// Catalog scope the fixture documents and users are entitled to.
- const SCOPE: &str = "read";
- /// `state` echoed from authorize to the code redirect.
- const STATE: &str = "cimd-e2e-state";
- /// Start a world with a counting fetcher and the CIMD feature gate.
- async fn cimd_world(fetcher: CimdFetcher, cimd_enabled: bool) -> Result<World, TestError> {
- World::start_with_cimd_fetcher(
- open_rate_limit(),
- json!({"cimd_enabled": cimd_enabled, "allow_localhost_redirect": true}),
- fetcher,
- )
- .await
- }
- /// Box one fetch so the higher-ranked [`CimdFetcher`] alias accepts the
- /// closure (mirrors the module-private helper production uses).
- fn boxed<'a>(
- future: impl Future<Output = Result<CimdDocument, CimdError>> + Send + 'a,
- ) -> CimdFetchFuture<'a> {
- Box::pin(future)
- }
- /// A document fetcher bound to `expected_url`: a request for any other URL
- /// fails, so URL drift cannot pass the fetch-count assertions.
- fn document_fetcher(expected_url: &str, document: CimdDocument) -> (CimdFetcher, Arc<AtomicUsize>) {
- let expected_url = expected_url.to_owned();
- let calls = Arc::new(AtomicUsize::new(0));
- let counter = Arc::clone(&calls);
- let fetcher: CimdFetcher = Arc::new(move |url: &str, _limits: CimdLimits| {
- let document = document.clone();
- let counter = Arc::clone(&counter);
- let matches = url == expected_url;
- boxed(async move {
- if !matches {
- return Err(CimdError::Fetch);
- }
- counter.fetch_add(1, Ordering::SeqCst);
- Ok(document)
- })
- });
- (fetcher, calls)
- }
- /// A fetcher that counts every call and always fails. Used to prove that a
- /// path never reaches the fetch at all.
- fn failing_fetcher() -> (CimdFetcher, Arc<AtomicUsize>) {
- let calls = Arc::new(AtomicUsize::new(0));
- let counter = Arc::clone(&calls);
- let fetcher: CimdFetcher = Arc::new(move |_url: &str, _limits: CimdLimits| {
- let counter = Arc::clone(&counter);
- boxed(async move {
- counter.fetch_add(1, Ordering::SeqCst);
- Err(CimdError::Fetch)
- })
- });
- (fetcher, calls)
- }
- /// A valid `none` metadata document.
- fn none_document(url: &str, dpop_bound: bool) -> CimdDocument {
- CimdDocument {
- client_id: url.to_owned(),
- redirect_uris: vec![REDIRECT.to_owned()],
- token_endpoint_auth_method: Some("none".to_owned()),
- grant_types: Some(vec![
- "authorization_code".to_owned(),
- "refresh_token".to_owned(),
- ]),
- scope: Some(SCOPE.to_owned()),
- jwks: None,
- jwks_uri: None,
- client_name: None,
- dpop_bound_access_tokens: Some(dpop_bound),
- }
- }
- /// A valid `private_key_jwt` metadata document carrying an inline JWKS.
- fn private_key_document(url: &str, jwks: Value) -> CimdDocument {
- CimdDocument {
- client_id: url.to_owned(),
- redirect_uris: vec![REDIRECT.to_owned()],
- token_endpoint_auth_method: Some("private_key_jwt".to_owned()),
- grant_types: Some(vec![
- "authorization_code".to_owned(),
- "refresh_token".to_owned(),
- ]),
- scope: Some(SCOPE.to_owned()),
- jwks: Some(jwks),
- jwks_uri: None,
- client_name: None,
- dpop_bound_access_tokens: Some(false),
- }
- }
- /// An Ed25519 signer plus the matching inline JWKS for one document.
- struct DocKey {
- /// Signing key for request objects and client assertions.
- encoding: EncodingKey,
- /// Public JWKS the document publishes.
- jwks: Value,
- }
- impl DocKey {
- /// Generate a fresh Ed25519 key pair and its JWKS.
- fn generate(kid: &str) -> Result<Self, TestError> {
- let rng = ring::rand::SystemRandom::new();
- let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng)
- .map_err(|error| fail(format!("ed25519 keygen failed: {error}")))?;
- let pair = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())
- .map_err(|error| fail(format!("ed25519 reload failed: {error}")))?;
- let x = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(pair.public_key().as_ref());
- Ok(Self {
- encoding: EncodingKey::from_ed_der(pkcs8.as_ref()),
- jwks: json!({
- "keys": [{ "kty": "OKP", "crv": "Ed25519", "x": x, "kid": kid }]
- }),
- })
- }
- /// Sign one EdDSA JWT with `kid`.
- fn sign(&self, kid: &str, claims: &Value) -> Result<String, TestError> {
- let mut header = Header::new(Algorithm::EdDSA);
- header.kid = Some(kid.to_owned());
- encode(&header, claims, &self.encoding).map_err(|error| fail(format!("jwt encode: {error}")))
- }
- /// A signed RFC 9101 request object bound to `client_id`/`issuer`.
- fn request_object(
- &self,
- kid: &str,
- client_id: &str,
- issuer: &str,
- extra: Value,
- ) -> Result<String, TestError> {
- let now = OffsetDateTime::now_utc().unix_timestamp();
- let mut claims = json!({
- "iss": client_id,
- "aud": issuer,
- "exp": now + 300,
- "iat": now,
- });
- let object = claims
- .as_object_mut()
- .ok_or_else(|| fail("request claims must be an object"))?;
- let extra_object = extra
- .as_object()
- .ok_or_else(|| fail("request extras must be an object"))?;
- for (key, value) in extra_object {
- object.insert(key.clone(), value.clone());
- }
- self.sign(kid, &claims)
- }
- /// A `private_key_jwt` client assertion (RFC 7523 §3).
- fn assertion(
- &self,
- kid: &str,
- client_id: &str,
- audience: &str,
- jti: &str,
- ) -> Result<String, TestError> {
- let now = OffsetDateTime::now_utc().unix_timestamp();
- self.sign(
- kid,
- &json!({
- "iss": client_id,
- "sub": client_id,
- "aud": audience,
- "jti": jti,
- "exp": now + 300,
- "iat": now,
- }),
- )
- }
- }
- /// Build a plain-query `/authorize` path (public `none` clients).
- fn authorize_path(client_id: &str, challenge: &str) -> String {
- format!(
- "/authorize?client_id={}&redirect_uri={}&response_type=code&scope={}&state={}\
- &code_challenge={}&code_challenge_method=S256",
- browser::encoded(client_id),
- browser::encoded(REDIRECT),
- browser::encoded(SCOPE),
- browser::encoded(STATE),
- browser::encoded(challenge),
- )
- }
- /// Assert an authorize error page renders `expected`.
- fn ensure_error_page(
- status: reqwest::StatusCode,
- body: &str,
- expected: &str,
- ) -> Result<(), TestError> {
- ensure(
- status == reqwest::StatusCode::BAD_REQUEST,
- format!("{expected} must render 400, got {status}: {body}"),
- )?;
- ensure(
- body.contains(&format!("<h1>{expected}</h1>")),
- format!("page must name {expected}: {body}"),
- )
- }
- /// Drive login → consent approval for a [`Browser`] already parked on the
- /// login page and return the issued authorization code.
- async fn approve(browser: &Browser, expected_state: &str) -> Result<String, TestError> {
- let code_2fa = totp_now(&browser.totp_secret).await?;
- let (status, headers, body) = post_login(browser, &browser.password, &code_2fa).await?;
- ensure(
- status == reqwest::StatusCode::SEE_OTHER,
- format!("login must 303 to consent: {status} {body}"),
- )?;
- let session = cookie_value(&headers, SESSION_COOKIE)?;
- let (status, _, body) = get_consent(browser, &session).await?;
- ensure(
- status == reqwest::StatusCode::OK,
- format!("consent must render: {status} {body}"),
- )?;
- let bound = hidden_input(&body, "request_id")?;
- ensure(
- bound == browser.request_id,
- "consent must serve the bound authorization request",
- )?;
- let consent_csrf = hidden_input(&body, "csrf")?;
- let (status, headers, _) =
- post_consent(browser, &session, &bound, &consent_csrf, "approve").await?;
- ensure(
- status == reqwest::StatusCode::SEE_OTHER,
- format!("consent approval must 303 to the redirect: {status}"),
- )?;
- let location = location_of(&headers)?;
- ensure(
- location.starts_with(REDIRECT),
- format!("code must return to the document redirect, got {location}"),
- )?;
- ensure(
- query_value(&location, "state")? == expected_state,
- "code redirect must echo state",
- )?;
- query_value(&location, "code")
- }
- /// Happy path: a `none` CIMD document carries the full browser flow through
- /// authorization-code redemption, and a second authorize inside the cache TTL
- /// performs no second fetch.
- #[tokio::test]
- async fn e2e_cimd_authorize_login_consent_token() -> Result<(), TestError> {
- let (fetcher, calls) = document_fetcher(CLIENT_URL, none_document(CLIENT_URL, true));
- let world = cimd_world(fetcher, true).await?;
- let browser = browser::begin_browser_for(
- world,
- CLIENT_URL,
- REDIRECT,
- SCOPE,
- "cimd-happy@example.test",
- "correct-horse-28-cimd-happy",
- )
- .await?;
- ensure(
- calls.load(Ordering::SeqCst) == 1,
- format!(
- "first authorize must fetch once, got {}",
- calls.load(Ordering::SeqCst)
- ),
- )?;
- let code = approve(&browser, browser::STATE).await?;
- // CIMD draft §4.1: a DPoP-bound document binds both the code and the token.
- let proof = browser.dpop.proof(
- "POST",
- &format!("{}/token", browser.world.state.config.issuer),
- )?;
- let (status, _, body) = browser
- .world
- .post_form(
- "/token",
- &[
- ("grant_type", "authorization_code"),
- ("code", code.as_str()),
- ("redirect_uri", REDIRECT),
- ("client_id", CLIENT_URL),
- ("code_verifier", browser.verifier.as_str()),
- ],
- None,
- &[("dpop", proof.as_str())],
- )
- .await?;
- ensure(
- status == reqwest::StatusCode::OK,
- format!("code redemption must mint: {status} {body}"),
- )?;
- let tokens = json_body(&body)?;
- ensure(
- tokens["token_type"] == "DPoP",
- format!("a DPoP-bound document must mint DPoP tokens, got {tokens}"),
- )?;
- ensure(
- tokens["access_token"]
- .as_str()
- .is_some_and(|token| !token.is_empty()),
- "redemption must return an access token",
- )?;
- ensure(
- tokens["refresh_token"]
- .as_str()
- .is_some_and(|token| !token.is_empty()),
- "the document grants refresh_token, so one must be returned",
- )?;
- ensure(
- tokens["scope"] == SCOPE,
- format!("tokens must carry the document scope, got {tokens}"),
- )?;
- // A second authorize inside the 15-minute TTL is a cache hit: zero fetches.
- let (challenge, _) = oauth2_pkce();
- let proof = browser.dpop.proof(
- "GET",
- &format!("{}/authorize", browser.world.state.config.issuer),
- )?;
- let (status, headers, body) = browser
- .world
- .get(
- &authorize_path(CLIENT_URL, &challenge),
- &[("dpop", proof.as_str())],
- )
- .await?;
- ensure(
- status == reqwest::StatusCode::SEE_OTHER,
- format!("cached second authorize must 303: {status} {body}"),
- )?;
- ensure(
- location_of(&headers)?.starts_with("/login?request_id="),
- "cached second authorize must reach login",
- )?;
- let count = calls.load(Ordering::SeqCst);
- ensure(
- count == 1,
- format!("second authorize within the TTL must add zero fetches, got {count}"),
- )?;
- Ok(())
- }
- /// A `private_key_jwt` document is confidential: a plain query authorize is
- /// rejected with `invalid_request`, exactly like a registered confidential
- /// client (RFC 9101 §4, `tests/jar.rs:229-251`).
- #[tokio::test]
- async fn e2e_cimd_private_key_jwt_requires_par_or_jar() -> Result<(), TestError> {
- let key = DocKey::generate("cimd-doc-key-plain")?;
- let (fetcher, calls) = document_fetcher(
- CLIENT_URL,
- private_key_document(CLIENT_URL, key.jwks.clone()),
- );
- let world = cimd_world(fetcher, true).await?;
- seed_scope(&world.pool, SCOPE).await?;
- let (challenge, _) = oauth2_pkce();
- let (status, _, body) = world
- .get(&authorize_path(CLIENT_URL, &challenge), &[])
- .await?;
- ensure_error_page(status, &body, "invalid_request")?;
- let count = calls.load(Ordering::SeqCst);
- ensure(
- count == 1,
- format!("the rejected authorize must fetch exactly once, got {count}"),
- )?;
- Ok(())
- }
- /// The same `private_key_jwt` document authorizes, logs in, consents, and
- /// redeems when the request object is signed with the document key; the token
- /// request authenticates with a document-key assertion. One fetch total.
- #[tokio::test]
- async fn e2e_cimd_private_key_jwt_par_jar_positive() -> Result<(), TestError> {
- const KID: &str = "cimd-doc-key-jar";
- const URL: &str = "https://cimd.example.test/private-key-jar.json";
- let key = DocKey::generate(KID)?;
- let (fetcher, calls) = document_fetcher(URL, private_key_document(URL, key.jwks.clone()));
- let world = cimd_world(fetcher, true).await?;
- seed_scope(&world.pool, SCOPE).await?;
- let email = "cimd-jar@example.test";
- let password = "correct-horse-28-cimd-jar";
- let totp_secret = seed_user(&world, email, password, SCOPE).await?;
- let (challenge, verifier) = oauth2_pkce();
- let issuer = world.state.config.issuer.clone();
- let request = key.request_object(
- KID,
- URL,
- &issuer,
- json!({
- "client_id": URL,
- "response_type": "code",
- "redirect_uri": REDIRECT,
- "scope": SCOPE,
- "state": STATE,
- "code_challenge": challenge,
- "code_challenge_method": "S256",
- }),
- )?;
- let path = format!(
- "/authorize?client_id={}&request={}",
- browser::encoded(URL),
- browser::encoded(&request)
- );
- let (status, headers, body) = world.get(&path, &[]).await?;
- ensure(
- status == reqwest::StatusCode::SEE_OTHER,
- format!("signed request object must authorize: {status} {body}"),
- )?;
- let request_id = query_value(&location_of(&headers)?, "request_id")?;
- let (status, headers, body) = world
- .get(&format!("/login?request_id={request_id}"), &[])
- .await?;
- ensure(
- status == reqwest::StatusCode::OK,
- format!("login page must render: {status} {body}"),
- )?;
- let presession = cookie_value(&headers, SESSION_COOKIE)?;
- let login_csrf = hidden_input(&body, "csrf")?;
- let browser = Browser {
- world,
- dpop: browser::DpopKey::generate()?,
- verifier,
- request_id,
- presession,
- session: None,
- login_csrf,
- email: email.to_owned(),
- password: password.to_owned(),
- totp_secret,
- };
- let code = approve(&browser, STATE).await?;
- let assertion = key.assertion(KID, URL, &format!("{issuer}/token"), "cimd-jar-jti-1")?;
- let (status, _, body) = browser
- .world
- .post_form(
- "/token",
- &[
- ("grant_type", "authorization_code"),
- ("code", code.as_str()),
- ("redirect_uri", REDIRECT),
- ("client_id", URL),
- ("code_verifier", browser.verifier.as_str()),
- (
- "client_assertion_type",
- "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
- ),
- ("client_assertion", assertion.as_str()),
- ],
- None,
- &[],
- )
- .await?;
- ensure(
- status == reqwest::StatusCode::OK,
- format!("JAR + assertion redemption must mint: {status} {body}"),
- )?;
- let tokens = json_body(&body)?;
- ensure(
- tokens["access_token"]
- .as_str()
- .is_some_and(|token| !token.is_empty()),
- "redemption must return an access token",
- )?;
- let count = calls.load(Ordering::SeqCst);
- ensure(
- count == 1,
- format!("the whole JAR flow must fetch exactly once, got {count}"),
- )?;
- Ok(())
- }
- /// Every invalid document fails closed with its typed error, exactly one
- /// fetch per row. One world per row: a shared Redis prefix would let the
- /// two-second `cimd:invalid:` marker replay the first row's verdict.
- #[tokio::test]
- async fn e2e_cimd_invalid_documents_fail_closed() -> Result<(), TestError> {
- let mut jwks_uri = none_document("https://cimd.example.test/jwks-uri.json", false);
- jwks_uri.jwks_uri = Some("https://keys.example.test/jwks.json".to_owned());
- let mut unsupported = none_document("https://cimd.example.test/unsupported-method.json", false);
- unsupported.token_endpoint_auth_method = Some("client_secret_basic".to_owned());
- let mut wildcard = none_document("https://cimd.example.test/wildcard.json", false);
- wildcard.redirect_uris = vec!["https://app.example.test/*".to_owned()];
- let mut mismatch = none_document("https://cimd.example.test/mismatch.json", false);
- mismatch.client_id = "https://cimd.example.test/different.json".to_owned();
- let rows = [
- (
- "https://cimd.example.test/jwks-uri.json",
- jwks_uri,
- "invalid_client_metadata",
- ),
- (
- "https://cimd.example.test/unsupported-method.json",
- unsupported,
- "invalid_client_metadata",
- ),
- (
- "https://cimd.example.test/wildcard.json",
- wildcard,
- "invalid_redirect_uri",
- ),
- (
- "https://cimd.example.test/mismatch.json",
- mismatch,
- "invalid_client_metadata",
- ),
- ];
- for (url, document, expected) in rows {
- let (fetcher, calls) = document_fetcher(url, document);
- let world = cimd_world(fetcher, true).await?;
- let (challenge, _) = oauth2_pkce();
- let (status, _, body) = world.get(&authorize_path(url, &challenge), &[]).await?;
- ensure_error_page(status, &body, expected)?;
- let count = calls.load(Ordering::SeqCst);
- ensure(
- count == 1,
- format!("{url} must fetch exactly once, got {count}"),
- )?;
- }
- Ok(())
- }
- /// Registered rows always win over CIMD: an active row never fetches, an
- /// inactive row is dead with zero fetches (todo 5 fail-closed), and a disabled
- /// feature keeps the normal unknown-client error with zero fetches.
- #[tokio::test]
- async fn e2e_cimd_registered_row_wins_and_inactive_is_dead() -> Result<(), TestError> {
- // Active registered row with an `https://` id: zero fetches.
- const ACTIVE_URL: &str = "https://cimd.example.test/registered.json";
- let (fetcher, active_calls) = document_fetcher(ACTIVE_URL, none_document(ACTIVE_URL, true));
- let world = cimd_world(fetcher, true).await?;
- seed_client(
- &world.pool,
- &world.state.keys,
- ACTIVE_URL,
- None,
- &["authorization_code", "refresh_token"],
- SCOPE,
- TokenEndpointAuthMethod::None,
- )
- .await?;
- attach_redirect(&world.pool, &world.state.keys, ACTIVE_URL, REDIRECT).await?;
- let browser = browser::begin_browser_for(
- world,
- ACTIVE_URL,
- REDIRECT,
- SCOPE,
- "cimd-registered@example.test",
- "correct-horse-28-cimd-row",
- )
- .await?;
- ensure(
- !browser.request_id.is_empty(),
- "the registered row must authorize to login",
- )?;
- let active_count = active_calls.load(Ordering::SeqCst);
- ensure(
- active_count == 0,
- format!("an active registered row must not fetch, got {active_count}"),
- )?;
- // Inactive registered row with an `https://` id: dead, zero fetches.
- const INACTIVE_URL: &str = "https://cimd.example.test/inactive.json";
- let (fetcher, inactive_calls) = failing_fetcher();
- let world = cimd_world(fetcher, true).await?;
- seed_client(
- &world.pool,
- &world.state.keys,
- INACTIVE_URL,
- None,
- &["authorization_code"],
- SCOPE,
- TokenEndpointAuthMethod::None,
- )
- .await?;
- ensure(
- client_repo::deactivate(&world.pool, INACTIVE_URL).await?,
- "deactivate must flip the row",
- )?;
- let (challenge, _) = oauth2_pkce();
- let (status, _, body) = world
- .get(&authorize_path(INACTIVE_URL, &challenge), &[])
- .await?;
- ensure_error_page(status, &body, "invalid_client")?;
- let inactive_count = inactive_calls.load(Ordering::SeqCst);
- ensure(
- inactive_count == 0,
- format!("an inactive row must never fall through to CIMD, got {inactive_count}"),
- )?;
- // CIMD disabled: an unknown `https://` id is the normal unknown client.
- const UNKNOWN_URL: &str = "https://cimd.example.test/disabled.json";
- let (fetcher, disabled_calls) = failing_fetcher();
- let world = cimd_world(fetcher, false).await?;
- let (challenge, _) = oauth2_pkce();
- let (status, _, body) = world
- .get(&authorize_path(UNKNOWN_URL, &challenge), &[])
- .await?;
- ensure_error_page(status, &body, "invalid_client")?;
- let disabled_count = disabled_calls.load(Ordering::SeqCst);
- ensure(
- disabled_count == 0,
- format!("a disabled CIMD feature must not fetch, got {disabled_count}"),
- )?;
- Ok(())
- }
|