1pub use authentication::{OAuthAuthenticatedClient, OAuthClientAuthenticationOptions};
3mod access_validation;
4mod authentication;
5pub use client_secret::{OAuthClientSecretCipher, OAuthClientSecretHasher};
6mod assertion;
7mod authorization;
8mod claims_request;
9mod client_jwks;
10mod client_secret;
11mod clients;
12mod metadata_document;
13pub use metadata_document::{
14 OAuthClientMetadata, OAuthClientRegistrationResult, RegisterClientMetadataDocumentInput,
15};
16mod consent;
17mod continuation;
18mod credentials;
19mod discovery;
20mod dpop;
21mod extension_authentication;
22mod interaction;
23pub use extension_authentication::{
24 OAuthClientAssertionInput, OAuthClientAuthenticationStrategy,
25 OAuthExtensionClientAuthentication, OAuthVerifiedClientAssertion,
26};
27mod extension_claims;
28pub use extension_claims::{
29 OAuthExtensionClaims, OAuthTokenClaimsContributor, OAuthTokenClaimsInput,
30 OAuthUserInfoClaimsContributor, OAuthUserInfoClaimsInput,
31};
32mod extensions;
33pub use extensions::{
34 OAuthExtensionGrantHandler, OAuthExtensionGrantInput, OAuthMetadataContributor,
35 OAuthMetadataExtensionInput, OAuthMetadataType, OAuthProviderApi, OAuthProviderExtension,
36};
37mod id_token;
38mod issuance;
39pub use issuance::{OAuthTokenGenerator, OAuthTokenIssueParams};
40mod initial_access_token;
41mod introspection;
42pub use initial_access_token::{
43 OAuthInitialAccessTokenAuthorization, OAuthInitialAccessTokenError,
44 OAuthInitialAccessTokenInput, OAuthInitialAccessTokenValidator,
45};
46mod logout;
47mod machine;
48mod post_login;
49mod rate_limit;
50mod refresh;
51pub use rate_limit::{OAuthEndpointRateLimit, OAuthProviderRateLimits};
52mod refresh_format;
53pub use refresh_format::{
54 OAuthDecodedRefreshToken, OAuthRefreshTokenEncryption, OAuthRefreshTokenFormatter,
55};
56mod resource_cache;
57mod resource_policy;
58mod resource_seed;
59pub use resource_seed::{OAuthResourceIdentifierValidator, OAuthResourceSeedMode};
60mod resources;
61mod revocation;
62mod select_account;
63mod session_retirement;
64mod signup;
65pub use post_login::{
66 OAuthConsentReference, OAuthConsentReferenceContext, OAuthInteractionContext,
67 OAuthPostLoginConfig, OAuthPostLoginContext, OAuthPostLoginPolicy,
68};
69pub use select_account::{
70 OAuthSelectAccountConfig, OAuthSelectAccountContext, OAuthSelectAccountPolicy,
71};
72pub use signup::{OAuthSignupConfig, OAuthSignupContext, OAuthSignupPolicy, OAuthSignupRedirect};
73mod subject;
74mod token;
75mod token_storage;
76pub use token_storage::OAuthTokenHasher;
77mod userinfo;
78use alibi_core::endpoint::{
79 EndpointCall, EndpointDefinition, EndpointInput, EndpointResponse, ServerEndpoint,
80};
81use alibi_core::{
82 AuthContext, AuthError, AuthPlugin, AuthRequest, AuthResponse, AuthResult, AuthRoute,
83 AuthSchema, HttpMethod,
84};
85pub use assertion::consume_client_assertion;
86use async_trait::async_trait;
87pub use client_jwks::{
88 OAuthClientMetadataAbortSignal, OAuthClientMetadataFetchOptions,
89 OAuthClientMetadataFetchResponse, OAuthClientMetadataResourceFetch,
90};
91pub use clients::{CreateOAuthClientInput, RegisterOAuthClientInput};
92pub use discovery::OAuthClientDiscovery;
93use serde_json::{Value, json};
94
95#[derive(Debug, Clone)]
98pub struct OAuthClientPrivilegeContext {
99 pub action: &'static str,
100 pub headers: alibi_core::Headers,
101 pub user: Value,
102 pub session: Value,
103}
104#[async_trait]
105pub trait OAuthClientPrivileges: Send + Sync + std::fmt::Debug {
106 async fn allowed(&self, context: &OAuthClientPrivilegeContext) -> AuthResult<bool>;
107}
108#[derive(Debug, Clone)]
110pub struct OAuthResourcePrivilegeContext {
111 pub action: &'static str,
112 pub headers: alibi_core::Headers,
113 pub user: Value,
114 pub session: Value,
115 pub resource_id: Option<String>,
117}
118#[async_trait]
119pub trait OAuthResourcePrivileges: Send + Sync + std::fmt::Debug {
120 async fn allowed(&self, context: &OAuthResourcePrivilegeContext) -> AuthResult<bool>;
121}
122pub trait OAuthClientIdGenerator: Send + Sync + std::fmt::Debug {
124 fn generate(&self) -> AuthResult<String>;
125}
126pub trait OAuthClientSecretGenerator: Send + Sync + std::fmt::Debug {
128 fn generate(&self) -> AuthResult<String>;
129}
130#[async_trait]
136pub trait OAuthRedirectUriValidator: Send + Sync + std::fmt::Debug {
137 async fn validate(
138 &self,
139 redirect_uri: &str,
140 registered_uris: &[String],
141 default_result: bool,
142 ) -> AuthResult<bool>;
143}
144pub struct OAuthRequestUriInput<'a> {
146 pub request_uri: &'a str,
147 pub client_id: &'a str,
148 pub context: &'a alibi_core::CallbackContext,
150}
151pub type OAuthAuthorizationQuery = indexmap::IndexMap<String, alibi_core::utils::json::JsValue>;
153#[async_trait]
159pub trait OAuthRequestUriResolver: Send + Sync + std::fmt::Debug {
160 async fn resolve(
161 &self,
162 input: OAuthRequestUriInput<'_>,
163 ) -> AuthResult<Option<OAuthAuthorizationQuery>>;
164}
165#[derive(Clone, Debug)]
167pub struct OAuthProviderConfig {
168 pub rate_limit: OAuthProviderRateLimits,
170 pub scopes: Vec<String>,
171 pub extensions: Vec<std::sync::Arc<OAuthProviderExtension>>,
172 pub custom_user_info_claims: Option<std::sync::Arc<dyn OAuthUserInfoClaimsContributor>>,
175 pub custom_id_token_claims: Option<std::sync::Arc<dyn OAuthTokenClaimsContributor>>,
178 pub custom_access_token_claims: Option<std::sync::Arc<dyn OAuthTokenClaimsContributor>>,
181 pub custom_token_response_fields: Option<std::sync::Arc<dyn OAuthTokenClaimsContributor>>,
184 pub client_discoveries: Vec<std::sync::Arc<dyn OAuthClientDiscovery>>,
185 pub cached_trusted_clients: std::collections::HashSet<String>,
187 pub client_privileges: Option<std::sync::Arc<dyn OAuthClientPrivileges>>,
191 pub client_credentials_scope_privileges: Option<std::sync::Arc<dyn OAuthClientPrivileges>>,
195 pub client_id_generator: Option<std::sync::Arc<dyn OAuthClientIdGenerator>>,
196 pub client_secret_generator: Option<std::sync::Arc<dyn OAuthClientSecretGenerator>>,
197 pub validate_redirect_uri: Option<std::sync::Arc<dyn OAuthRedirectUriValidator>>,
199 pub request_uri_resolver: Option<std::sync::Arc<dyn OAuthRequestUriResolver>>,
201 pub client_secret_hasher: Option<std::sync::Arc<dyn OAuthClientSecretHasher>>,
202 pub token_hasher: Option<std::sync::Arc<dyn OAuthTokenHasher>>,
204 pub opaque_access_token_generator: Option<std::sync::Arc<dyn OAuthTokenGenerator>>,
205 pub refresh_token_generator: Option<std::sync::Arc<dyn OAuthTokenGenerator>>,
206 pub opaque_access_token_prefix: Option<String>,
208 pub refresh_token_prefix: Option<String>,
210 pub refresh_token_formatter: Option<std::sync::Arc<dyn OAuthRefreshTokenFormatter>>,
212 pub client_secret_cipher: Option<std::sync::Arc<dyn OAuthClientSecretCipher>>,
213 pub client_secret_prefix: Option<String>,
215 pub login_page: String,
216 pub consent_page: String,
217 pub select_account_page: Option<String>,
218 pub select_account: Option<OAuthSelectAccountConfig>,
219 pub signup: Option<OAuthSignupConfig>,
220 pub post_login: Option<OAuthPostLoginConfig>,
221 pub code_expires_in: f64,
222 pub access_token_expires_in: f64,
224 pub m2m_access_token_expires_in: f64,
226 pub id_token_expires_in: f64,
227 pub refresh_token_expires_in: f64,
228 pub scope_expirations: std::collections::HashMap<String, crate::jwt::JwtExpiration>,
231 pub assertion_max_lifetime: f64,
233 pub refresh_token_reuse_interval: f64,
234 pub allow_public_client_prelogin: bool,
235 pub enforce_per_client_resources: bool,
236 pub cached_resources: std::collections::HashSet<String>,
240 pub resources: Vec<Value>,
242 pub resource_seed_mode: OAuthResourceSeedMode,
243 pub identifier_validator: Option<std::sync::Arc<dyn OAuthResourceIdentifierValidator>>,
244 pub resource_privileges: Option<std::sync::Arc<dyn OAuthResourcePrivileges>>,
247 pub advertised_scopes: Option<Vec<String>>,
248 pub advertised_claims: Option<Vec<String>>,
249 pub grant_types: Vec<String>,
250 pub allow_dynamic_client_registration: bool,
251 pub client_registration_require_pkce: Option<bool>,
254 pub client_registration_default_scopes: Option<Vec<String>>,
257 pub client_registration_allowed_scopes: Option<Vec<String>>,
260 pub client_registration_default_resources: Option<Vec<String>>,
263 pub client_registration_allowed_resources: Option<Vec<String>>,
266 pub initial_access_token_validator:
268 Option<std::sync::Arc<dyn OAuthInitialAccessTokenValidator>>,
269 pub allow_unauthenticated_client_registration: bool,
270 pub disable_jwt_plugin: bool,
271 pub pairwise_secret: Option<String>,
272 pub dpop_signing_algorithms: Option<Vec<String>>,
273 pub dpop_proof_max_age_seconds: Option<f64>,
274}
275impl Default for OAuthProviderConfig {
276 fn default() -> Self {
277 Self {
278 rate_limit: OAuthProviderRateLimits::default(),
279 extensions: Vec::new(),
280 custom_user_info_claims: None,
281 custom_id_token_claims: None,
282 custom_access_token_claims: None,
283 custom_token_response_fields: None,
284 client_discoveries: Vec::new(),
285 cached_trusted_clients: std::collections::HashSet::new(),
286 client_privileges: None,
287 client_credentials_scope_privileges: None,
288 client_id_generator: None,
289 client_secret_generator: None,
290 validate_redirect_uri: None,
291 request_uri_resolver: None,
292 client_secret_hasher: None,
293 client_secret_cipher: None,
294 token_hasher: None,
295 opaque_access_token_generator: None,
296 refresh_token_generator: None,
297 opaque_access_token_prefix: None,
298 refresh_token_prefix: None,
299 refresh_token_formatter: None,
300 client_secret_prefix: None,
301 login_page: "/login".into(),
302 consent_page: "/consent".into(),
303 select_account_page: None,
304 select_account: None,
305 signup: None,
306 post_login: None,
307 code_expires_in: 600.0,
308 access_token_expires_in: 3600.0,
309 m2m_access_token_expires_in: 3600.0,
310 id_token_expires_in: 36_000.0,
311 refresh_token_expires_in: 2_592_000.0,
312 scope_expirations: std::collections::HashMap::new(),
313 assertion_max_lifetime: 300.0,
314 refresh_token_reuse_interval: 0.0,
315 allow_public_client_prelogin: false,
316 enforce_per_client_resources: true,
317 cached_resources: std::collections::HashSet::new(),
318 resources: Vec::new(),
319 resource_seed_mode: OAuthResourceSeedMode::default(),
320 identifier_validator: None,
321 resource_privileges: None,
322 scopes: ["openid", "profile", "email", "offline_access"]
323 .map(str::to_owned)
324 .into(),
325 advertised_scopes: None,
326 advertised_claims: None,
327 grant_types: ["authorization_code", "client_credentials", "refresh_token"]
328 .map(str::to_owned)
329 .into(),
330 allow_dynamic_client_registration: false,
331 client_registration_require_pkce: None,
332 client_registration_default_scopes: None,
333 client_registration_allowed_scopes: None,
334 client_registration_default_resources: None,
335 client_registration_allowed_resources: None,
336 initial_access_token_validator: None,
337 allow_unauthenticated_client_registration: false,
338 disable_jwt_plugin: false,
339 pairwise_secret: None,
340 dpop_signing_algorithms: None,
341 dpop_proof_max_age_seconds: None,
342 }
343 }
344}
345#[derive(Clone, Debug, Default)]
346pub struct OAuthProviderPlugin {
347 config: OAuthProviderConfig,
348 client_jwks: std::sync::Arc<client_jwks::Cache>,
349 trusted_clients: std::sync::Arc<discovery::TrustedCache>,
350}
351impl OAuthProviderPlugin {
352 #[must_use]
353 pub fn new() -> Self {
354 Self::default()
355 }
356 #[must_use]
357 pub fn with_config(mut config: OAuthProviderConfig) -> Self {
358 config.scopes = unique(
359 config
360 .scopes
361 .into_iter()
362 .filter(|scope| !scope.is_empty())
363 .collect(),
364 );
365 Self {
366 config,
367 ..Self::default()
368 }
369 }
370 #[must_use]
372 pub fn get_oauth_server_config_endpoint() -> ServerEndpoint<Value> {
373 ServerEndpoint::new("oauth-provider", "getOAuthServerConfig")
374 }
375 #[must_use]
377 pub fn get_open_id_config_endpoint() -> ServerEndpoint<Value> {
378 ServerEndpoint::new("oauth-provider", "getOpenIdConfig")
379 }
380 fn metadata<S: AuthSchema>(&self, ctx: &AuthContext<S>) -> AuthResult<Value> {
381 let jwt = if self.config.disable_jwt_plugin {
382 None
383 } else {
384 Some(
385 ctx.extensions
386 .get::<super::jwt::JwtDiscoveryOptions>()
387 .ok_or_else(|| AuthError::config("jwt_config"))?,
388 )
389 };
390 let base = format!(
391 "{}{}",
392 ctx.config.base_url.trim_end_matches('/'),
393 ctx.config.base_path
394 );
395 let issuer = jwt
396 .as_ref()
397 .and_then(|jwt| jwt.issuer.as_deref())
398 .unwrap_or(&base);
399 let issuer = normalize_issuer(issuer);
400 let grants = self.supported_grant_types();
401 let endpoint_auth = self.supported_auth_methods(false);
402 let mut token_auth = endpoint_auth.clone();
403 if self.config.allow_unauthenticated_client_registration
404 || !self.config.client_discoveries.is_empty()
405 {
406 token_auth.insert(0, "none".into());
407 }
408 let signing = [
409 "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512",
410 "EdDSA",
411 ];
412 let mut output: serde_json::Map<String, Value> = serde_json::from_value(json!({
413 "scopes_supported":self.config.advertised_scopes.as_ref().unwrap_or(&self.config.scopes),
414 "issuer":issuer,"authorization_endpoint":format!("{base}/oauth2/authorize"),"token_endpoint":format!("{base}/oauth2/token"),
415 "introspection_endpoint":format!("{base}/oauth2/introspect"),"revocation_endpoint":format!("{base}/oauth2/revoke"),
416 "response_types_supported":if grants.iter().any(|grant|grant=="authorization_code"){vec!["code"]}else{vec![]},
417 "response_modes_supported":["query"],"grant_types_supported":grants,
418 "token_endpoint_auth_methods_supported":token_auth,"token_endpoint_auth_signing_alg_values_supported":signing,
419 "introspection_endpoint_auth_methods_supported":endpoint_auth,
420 "introspection_endpoint_auth_signing_alg_values_supported":signing,
421 "revocation_endpoint_auth_methods_supported":endpoint_auth,
422 "revocation_endpoint_auth_signing_alg_values_supported":signing,
423 "code_challenge_methods_supported":["S256"],"authorization_response_iss_parameter_supported":true,
424 "dpop_signing_alg_values_supported":self.config.dpop_signing_algorithms.clone().unwrap_or_else(||["EdDSA","ES256","ES512","PS256","RS256"].map(str::to_owned).into()),
425 "backchannel_logout_supported":!self.config.disable_jwt_plugin,"backchannel_logout_session_supported":!self.config.disable_jwt_plugin,
426 }))?;
427 if let Some(jwt) = jwt.as_ref() {
428 _ = output.insert(
429 "jwks_uri".into(),
430 json!(
431 jwt.remote_url
432 .clone()
433 .unwrap_or_else(|| format!("{base}{}", jwt.jwks_path))
434 ),
435 );
436 }
437 if self.config.allow_dynamic_client_registration {
438 _ = output.insert(
439 "registration_endpoint".into(),
440 json!(format!("{base}/oauth2/register")),
441 );
442 }
443 if self.config.scopes.iter().any(|scope| scope == "openid") {
444 _ = output.insert("claims_supported".into(), json!(self.supported_claims()));
445 _ = output.insert("claims_parameter_supported".into(), json!(true));
446 _ = output.insert(
447 "userinfo_endpoint".into(),
448 json!(format!("{base}/oauth2/userinfo")),
449 );
450 _ = output.insert(
451 "subject_types_supported".into(),
452 json!(if self
453 .config
454 .pairwise_secret
455 .as_ref()
456 .is_some_and(|secret| !secret.is_empty())
457 {
458 vec!["public", "pairwise"]
459 } else {
460 vec!["public"]
461 }),
462 );
463 _ = output.insert("acr_values_supported".into(), json!(["0"]));
464 _ = output.insert(
465 "id_token_signing_alg_values_supported".into(),
466 json!(jwt.as_ref().map_or_else(
467 || vec!["HS256".to_owned()],
468 |jwt| jwt.signing_algorithms.clone()
469 )),
470 );
471 _ = output.insert(
472 "end_session_endpoint".into(),
473 json!(format!("{base}/oauth2/end-session")),
474 );
475 _ = output.insert("request_parameter_supported".into(), json!(false));
476 _ = output.insert("request_uri_parameter_supported".into(), json!(false));
477 _ = output.insert(
478 "prompt_values_supported".into(),
479 json!(["login", "consent", "create", "select_account", "none"]),
480 );
481 }
482 let mut contributed = serde_json::Map::new();
483 for discovery in &self.config.client_discoveries {
484 contributed.extend(discovery.discovery_metadata());
485 }
486 contributed.extend(output);
487 self.apply_metadata_extensions(ctx, Value::Object(contributed))
488 }
489}
490fn unique(values: Vec<String>) -> Vec<String> {
491 let mut result = Vec::new();
492 for value in values {
493 if !result.contains(&value) {
494 result.push(value);
495 }
496 }
497 result
498}
499fn normalize_issuer(value: &str) -> String {
500 let Ok(mut url) = url::Url::parse(value) else {
501 return value.into();
502 };
503 let loopback = url.host_str().is_some_and(|host| {
504 host == "localhost" || host == "127.0.0.1" || host == "[::1]" || host == "::1"
505 });
506 if url.scheme() != "https" && !loopback {
507 _ = url.set_scheme("https");
508 }
509 url.set_query(None);
510 url.set_fragment(None);
511 url.to_string().trim_end_matches('/').to_owned()
512}
513#[async_trait]
514impl<S: AuthSchema> AuthPlugin<S> for OAuthProviderPlugin {
515 fn name(&self) -> &'static str {
516 "oauth-provider"
517 }
518 fn rate_limits(&self) -> Vec<alibi_core::PluginRateLimit> {
519 self.config.rate_limit.rules()
520 }
521 fn endpoint_hooks(&self) -> Vec<&dyn alibi_core::endpoint::EndpointHook<S>> {
522 vec![self]
523 }
524 async fn before_request(
525 &self,
526 req: &AuthRequest,
527 ctx: &AuthContext<S>,
528 ) -> AuthResult<Option<alibi_core::BeforeRequestAction>> {
529 let body = req.body_as_json::<alibi_core::utils::json::JsValue>().ok();
530 interaction::admit(
531 body.as_ref().and_then(|body| body.get("oauth_query")),
532 ctx.config.current_secret(),
533 req.extensions(),
534 )?
535 .map(|response| {
536 response
537 .to_response()
538 .map(alibi_core::BeforeRequestAction::Respond)
539 })
540 .transpose()
541 }
542 async fn after_request(
543 &self,
544 req: &AuthRequest,
545 ctx: &AuthContext<S>,
546 response: AuthResponse,
547 ) -> AuthResult<AuthResponse> {
548 let Some(mut resumed) = interaction::resume(self, req, &response.headers, ctx).await?
549 else {
550 return Ok(response);
551 };
552 resumed.merge_headers(response.headers);
553 resumed.to_response()
554 }
555 fn allowed_media_types(&self, route: &AuthRoute) -> Vec<&'static str> {
556 match route.path.as_str() {
557 "/oauth2/authorize"
558 | "/oauth2/end-session/confirm"
559 | "/oauth2/token"
560 | "/oauth2/introspect"
561 | "/oauth2/revoke"
562 | "/oauth2/userinfo" => vec!["application/x-www-form-urlencoded"],
563 "/oauth2/end-session" => vec!["application/json", "application/x-www-form-urlencoded"],
564 _ => vec!["application/json"],
565 }
566 }
567 fn routes(&self) -> Vec<AuthRoute> {
568 vec![
569 AuthRoute::post(
570 "/oauth2/end-session/confirm",
571 "oauth2EndSessionConfirmation",
572 ),
573 AuthRoute::get("/oauth2/end-session", "oauth2EndSession"),
574 AuthRoute::post("/oauth2/end-session", "oauth2EndSession"),
575 AuthRoute::get("/oauth2/userinfo", "oauth2UserInfo"),
576 AuthRoute::post("/oauth2/userinfo", "oauth2UserInfo"),
577 AuthRoute::post("/oauth2/revoke", "oauth2Revoke"),
578 AuthRoute::post("/oauth2/introspect", "oauth2Introspect"),
579 AuthRoute::post("/oauth2/token", "oauth2Token"),
580 AuthRoute::post("/oauth2/create-client", "createOAuthClient"),
581 AuthRoute::get("/oauth2/get-client", "getOAuthClient"),
582 AuthRoute::get("/oauth2/public-client", "getOAuthClientPublic"),
583 AuthRoute::post(
584 "/oauth2/public-client-prelogin",
585 "getOAuthClientPublicPrelogin",
586 ),
587 AuthRoute::get("/oauth2/get-clients", "getOAuthClients"),
588 AuthRoute::post("/oauth2/delete-client", "deleteOAuthClient"),
589 AuthRoute::post("/oauth2/update-client", "updateOAuthClient"),
590 AuthRoute::post("/oauth2/register", "registerOAuthClient"),
591 AuthRoute::post("/oauth2/consent", "oauth2Consent"),
592 AuthRoute::post("/oauth2/continue", "oauth2Continue"),
593 AuthRoute::get("/oauth2/get-consents", "getOAuthConsents"),
594 AuthRoute::get("/oauth2/get-consent", "getOAuthConsent"),
595 AuthRoute::post("/oauth2/update-consent", "updateOAuthConsent"),
596 AuthRoute::post("/oauth2/delete-consent", "deleteOAuthConsent"),
597 AuthRoute::get("/oauth2/authorize", "oauth2Authorize"),
598 AuthRoute::post("/oauth2/authorize", "oauth2Authorize"),
599 AuthRoute::post("/oauth2/client/rotate-secret", "rotateClientSecret"),
600 ]
601 }
602 fn server_endpoints(&self) -> Vec<EndpointDefinition> {
603 vec![
604 crate::endpoint::definition(
605 "oauth2EndSessionConfirmation",
606 "oauth2EndSessionConfirmation",
607 Some("/oauth2/end-session/confirm"),
608 HttpMethod::Post,
609 ),
610 crate::endpoint::definition(
611 "oauth2EndSession",
612 "oauth2EndSession",
613 Some("/oauth2/end-session"),
614 HttpMethod::Get,
615 ),
616 crate::endpoint::definition(
617 "oauth2UserInfo",
618 "oauth2UserInfo",
619 Some("/oauth2/userinfo"),
620 HttpMethod::Get,
621 ),
622 crate::endpoint::definition(
623 "oauth2Revoke",
624 "oauth2Revoke",
625 Some("/oauth2/revoke"),
626 HttpMethod::Post,
627 ),
628 crate::endpoint::definition(
629 "oauth2Introspect",
630 "oauth2Introspect",
631 Some("/oauth2/introspect"),
632 HttpMethod::Post,
633 ),
634 crate::endpoint::definition(
635 "oauth2Token",
636 "oauth2Token",
637 Some("/oauth2/token"),
638 HttpMethod::Post,
639 ),
640 crate::endpoint::definition(
641 "oauth2Continue",
642 "oauth2Continue",
643 Some("/oauth2/continue"),
644 HttpMethod::Post,
645 ),
646 crate::endpoint::definition(
647 "adminUnlinkClientResource",
648 "adminUnlinkClientResource",
649 Some("/admin/oauth2/resources/:identifier/clients/:client_id"),
650 HttpMethod::Delete,
651 ),
652 crate::endpoint::definition(
653 "adminLinkClientResource",
654 "adminLinkClientResource",
655 Some("/admin/oauth2/resources/:identifier/clients/:client_id"),
656 HttpMethod::Post,
657 ),
658 crate::endpoint::definition(
659 "adminDeleteOAuthResource",
660 "adminDeleteOAuthResource",
661 Some("/admin/oauth2/resources/:identifier"),
662 HttpMethod::Delete,
663 ),
664 crate::endpoint::definition(
665 "adminUpdateOAuthResource",
666 "adminUpdateOAuthResource",
667 Some("/admin/oauth2/resources/:identifier"),
668 HttpMethod::Patch,
669 ),
670 crate::endpoint::definition(
671 "adminGetOAuthResource",
672 "adminGetOAuthResource",
673 Some("/admin/oauth2/resources/:identifier"),
674 HttpMethod::Get,
675 ),
676 crate::endpoint::definition(
677 "adminListOAuthResources",
678 "adminListOAuthResources",
679 Some("/admin/oauth2/resources"),
680 HttpMethod::Get,
681 ),
682 crate::endpoint::definition(
683 "adminCreateOAuthResource",
684 "adminCreateOAuthResource",
685 Some("/admin/oauth2/resources"),
686 HttpMethod::Post,
687 ),
688 crate::endpoint::definition(
689 "deleteOAuthConsent",
690 "deleteOAuthConsent",
691 Some("/oauth2/delete-consent"),
692 HttpMethod::Post,
693 ),
694 crate::endpoint::definition(
695 "updateOAuthConsent",
696 "updateOAuthConsent",
697 Some("/oauth2/update-consent"),
698 HttpMethod::Post,
699 ),
700 crate::endpoint::definition(
701 "getOAuthConsent",
702 "getOAuthConsent",
703 Some("/oauth2/get-consent"),
704 HttpMethod::Get,
705 ),
706 crate::endpoint::definition(
707 "getOAuthConsents",
708 "getOAuthConsents",
709 Some("/oauth2/get-consents"),
710 HttpMethod::Get,
711 ),
712 crate::endpoint::definition(
713 "oauth2Consent",
714 "oauth2Consent",
715 Some("/oauth2/consent"),
716 HttpMethod::Post,
717 ),
718 crate::endpoint::definition(
719 "getOAuthClientPublicPrelogin",
720 "getOAuthClientPublicPrelogin",
721 Some("/oauth2/public-client-prelogin"),
722 HttpMethod::Post,
723 ),
724 crate::endpoint::definition(
725 "oauth2Authorize",
726 "oauth2Authorize",
727 Some("/oauth2/authorize"),
728 HttpMethod::Get,
729 ),
730 crate::endpoint::definition(
731 "adminUpdateOAuthClient",
732 "adminUpdateOAuthClient",
733 Some("/admin/oauth2/update-client"),
734 HttpMethod::Patch,
735 ),
736 crate::endpoint::definition(
737 "adminCreateOAuthClient",
738 "adminCreateOAuthClient",
739 Some("/admin/oauth2/create-client"),
740 HttpMethod::Post,
741 ),
742 crate::endpoint::definition(
743 "registerOAuthClient",
744 "registerOAuthClient",
745 Some("/oauth2/register"),
746 HttpMethod::Post,
747 ),
748 crate::endpoint::definition(
749 "updateOAuthClient",
750 "updateOAuthClient",
751 Some("/oauth2/update-client"),
752 HttpMethod::Post,
753 ),
754 crate::endpoint::definition(
755 "rotateClientSecret",
756 "rotateClientSecret",
757 Some("/oauth2/client/rotate-secret"),
758 HttpMethod::Post,
759 ),
760 crate::endpoint::definition(
761 "deleteOAuthClient",
762 "deleteOAuthClient",
763 Some("/oauth2/delete-client"),
764 HttpMethod::Post,
765 ),
766 crate::endpoint::definition(
767 "getOAuthClients",
768 "getOAuthClients",
769 Some("/oauth2/get-clients"),
770 HttpMethod::Get,
771 ),
772 crate::endpoint::definition(
773 "getOAuthClientPublic",
774 "getOAuthClientPublic",
775 Some("/oauth2/public-client"),
776 HttpMethod::Get,
777 ),
778 crate::endpoint::definition(
779 "getOAuthClient",
780 "getOAuthClient",
781 Some("/oauth2/get-client"),
782 HttpMethod::Get,
783 ),
784 crate::endpoint::definition(
785 "createOAuthClient",
786 "createOAuthClient",
787 Some("/oauth2/create-client"),
788 HttpMethod::Post,
789 ),
790 crate::endpoint::definition(
791 "getOAuthServerConfig",
792 "getOAuthServerConfig",
793 Some("/.well-known/oauth-authorization-server"),
794 HttpMethod::Get,
795 ),
796 crate::endpoint::definition(
797 "getOpenIdConfig",
798 "getOpenIdConfig",
799 Some("/.well-known/openid-configuration"),
800 HttpMethod::Get,
801 ),
802 ]
803 }
804 fn validate_endpoint(
805 &self,
806 call: &EndpointCall,
807 _ctx: &AuthContext<S>,
808 ) -> AuthResult<EndpointInput> {
809 Ok(EndpointInput {
810 body: if call.operation_id() == "oauth2EndSessionConfirmation" {
811 Some(crate::endpoint::validate_fields(
812 call.body(),
813 "body",
814 &[crate::authentication_helpers::JsonField {
815 name: "action",
816 kind: crate::authentication_helpers::JsonFieldKind::OneOf(&["confirm"]),
817 required: true,
818 }],
819 )?)
820 } else if call.operation_id() == "oauth2EndSession" {
821 logout::validate(call.body(), "body")?
822 } else if call.operation_id() == "oauth2UserInfo" {
823 call.body()
824 .map(|body| {
825 crate::endpoint::validate_fields(
826 Some(body),
827 "body",
828 &[crate::authentication_helpers::JsonField::string(
829 "access_token",
830 false,
831 )],
832 )
833 })
834 .transpose()?
835 } else if call.operation_id() == "oauth2Continue" {
836 Some(continuation::validate(call.body())?)
837 } else if call.operation_id() == "adminUpdateOAuthResource" {
838 Some(resources::validate_update(call.body())?)
839 } else if call.operation_id() == "adminCreateOAuthResource" {
840 Some(resources::validate_create(call.body())?)
841 } else if call.operation_id() == "deleteOAuthConsent" {
842 Some(consent::validate_id(call.body(), "body")?)
843 } else if call.operation_id() == "updateOAuthConsent" {
844 Some(consent::validate_update(call.body())?)
845 } else if call.operation_id() == "oauth2Consent" {
846 Some(consent::validate(call.body())?)
847 } else if call.operation_id() == "getOAuthClientPublicPrelogin" {
848 Some(clients::validate_prelogin(call.body())?)
849 } else if call.operation_id() == "adminUpdateOAuthClient" {
850 Some(clients::validate_admin_update(call.body())?)
851 } else if call.operation_id() == "adminCreateOAuthClient" {
852 Some(clients::validate_admin_create(call.body())?)
853 } else if call.operation_id() == "updateOAuthClient" {
854 Some(clients::validate_update(call.body())?)
855 } else if matches!(
856 call.operation_id(),
857 "deleteOAuthClient" | "rotateClientSecret"
858 ) {
859 Some(clients::validate_delete(call.body())?)
860 } else if call.operation_id() == "createOAuthClient" {
861 Some(clients::validate(call.body())?)
862 } else {
863 call.body().cloned()
864 },
865 query: if call.operation_id() == "oauth2EndSession" {
866 logout::validate(call.query(), "query")?
867 } else if call.operation_id() == "getOAuthConsent" {
868 Some(consent::validate_id(call.query(), "query")?)
869 } else if matches!(
870 call.operation_id(),
871 "getOAuthClient" | "getOAuthClientPublic"
872 ) {
873 Some(clients::validate_read(call.query())?)
874 } else {
875 call.query().cloned()
876 },
877 })
878 }
879 async fn on_endpoint(
880 &self,
881 call: &EndpointCall,
882 ctx: &AuthContext<S>,
883 ) -> AuthResult<EndpointResponse> {
884 if call.operation_id() == "oauth2EndSessionConfirmation" {
885 return Box::pin(self.confirm_logout(call, ctx)).await;
886 }
887 if call.operation_id() == "oauth2EndSession" {
888 return Box::pin(self.end_session(call.query(), call.body(), call, ctx)).await;
889 }
890 if call.operation_id() == "oauth2UserInfo" {
891 return Box::pin(self.user_info(call.body(), call, ctx)).await;
892 }
893 if call.operation_id() == "oauth2Revoke" {
894 return Box::pin(self.revoke(call.body(), call, ctx)).await;
895 }
896 if call.operation_id() == "oauth2Introspect" {
897 return Box::pin(self.introspect(call.body(), call, ctx)).await;
898 }
899 if call.operation_id() == "oauth2Token" {
900 return Box::pin(self.token(call.body(), call, ctx)).await;
901 }
902 if call.operation_id() == "oauth2Continue" {
903 return Box::pin(self.continue_authorization(call.body_as()?, call, ctx)).await;
904 }
905 if call.operation_id() == "adminUnlinkClientResource" {
906 return Box::pin(self.unlink_resource(call.params(), call, ctx)).await;
907 }
908 if call.operation_id() == "adminLinkClientResource" {
909 return Box::pin(self.link_resource(call.params(), call, ctx)).await;
910 }
911 if call.operation_id() == "adminDeleteOAuthResource" {
912 return Box::pin(self.delete_resource(call.params(), call, ctx)).await;
913 }
914 if call.operation_id() == "adminUpdateOAuthResource" {
915 return Box::pin(self.update_resource(call.params(), call.body_as()?, call, ctx)).await;
916 }
917 if call.operation_id() == "adminGetOAuthResource" {
918 return Box::pin(self.read_resource(call.params(), call, ctx)).await;
919 }
920 if call.operation_id() == "adminListOAuthResources" {
921 return Box::pin(self.list_resources(call, ctx)).await;
922 }
923 if call.operation_id() == "adminCreateOAuthResource" {
924 return Box::pin(self.create_resource(call.body_as()?, call, ctx)).await;
925 }
926 if call.operation_id() == "deleteOAuthConsent" {
927 return Box::pin(self.delete_consent(call.body_as()?, call, ctx)).await;
928 }
929 if call.operation_id() == "updateOAuthConsent" {
930 return Box::pin(self.update_consent(call.body_as()?, call, ctx)).await;
931 }
932 if call.operation_id() == "getOAuthConsent" {
933 return Box::pin(self.read_consent(call.query_as()?, call, ctx)).await;
934 }
935 if call.operation_id() == "getOAuthConsents" {
936 return Box::pin(self.list_consents(call, ctx)).await;
937 }
938 if call.operation_id() == "oauth2Consent" {
939 return Box::pin(self.consent(call.body_as()?, call, ctx)).await;
940 }
941 if call.operation_id() == "getOAuthClientPublicPrelogin" {
942 return Box::pin(self.read_prelogin_client(call.body_as()?, ctx)).await;
943 }
944 if call.operation_id() == "oauth2Authorize" {
945 let value = if call.method() == Some(&HttpMethod::Post) {
946 call.body()
947 } else {
948 call.query()
949 };
950 return Box::pin(self.authorize(value.cloned(), call, ctx)).await;
951 }
952 if call.operation_id() == "adminUpdateOAuthClient" {
953 return Box::pin(self.update_client(call.body_as()?, call, ctx, true)).await;
954 }
955 if call.operation_id() == "adminCreateOAuthClient" {
956 return Box::pin(self.admin_create_client(call.body_as()?, call, ctx)).await;
957 }
958 if call.operation_id() == "registerOAuthClient" {
959 return Box::pin(self.register_client(call.body(), call, ctx)).await;
960 }
961 if call.operation_id() == "updateOAuthClient" {
962 return Box::pin(self.update_client(call.body_as()?, call, ctx, false)).await;
963 }
964 if call.operation_id() == "rotateClientSecret" {
965 return Box::pin(self.rotate_client_secret(call.body_as()?, call, ctx)).await;
966 }
967 if call.operation_id() == "deleteOAuthClient" {
968 return Box::pin(self.delete_client(call.body_as()?, call, ctx)).await;
969 }
970 if call.operation_id() == "getOAuthClients" {
971 return Box::pin(self.list_clients(call, ctx)).await;
972 }
973 if call.operation_id() == "getOAuthClientPublic" {
974 return Box::pin(self.read_public_client(call.query_as()?, call, ctx)).await;
975 }
976 if call.operation_id() == "getOAuthClient" {
977 return Box::pin(self.read_client(call.query_as()?, call, ctx)).await;
978 }
979 if call.operation_id() == "createOAuthClient" {
980 return Box::pin(self.create_client(call.body_as()?, call, ctx)).await;
981 }
982 if call.operation_id() == "getOpenIdConfig"
983 && !self.config.scopes.iter().any(|scope| scope == "openid")
984 {
985 return Ok(EndpointResponse::error(AuthError::Api {
986 status: 404,
987 code: None,
988 message: String::new(),
989 })
990 .without_error_body()
991 .with_header("Content-Type", "application/json"));
992 }
993 EndpointResponse::json(&self.metadata(ctx)?)
994 }
995 async fn on_request(
996 &self,
997 req: &AuthRequest,
998 ctx: &AuthContext<S>,
999 ) -> AuthResult<Option<AuthResponse>> {
1000 if req.path() == "/oauth2/end-session/confirm" && *req.method() == HttpMethod::Post {
1001 let action = url::form_urlencoded::parse(req.body.as_deref().unwrap_or_default())
1002 .find(|(key, _)| key == "action")
1003 .map(|(_, value)| value.into_owned());
1004 if action.as_deref() != Some("confirm") {
1005 return Ok(Some(
1006 AuthError::bad_request("Invalid logout action").to_auth_response(),
1007 ));
1008 }
1009 return Ok(Some(
1010 Box::pin(self.confirm_logout(req, ctx))
1011 .await?
1012 .to_response()?,
1013 ));
1014 }
1015 if req.path() == "/oauth2/end-session"
1016 && matches!(req.method(), HttpMethod::Get | HttpMethod::Post)
1017 {
1018 let query = alibi_core::utils::json::parse_value(&serde_json::to_string(&req.query)?)?;
1019 let body = if *req.method() == HttpMethod::Post {
1020 Some(alibi_core::utils::json::parse_value(
1021 &serde_json::to_string(
1022 &url::form_urlencoded::parse(req.body.as_deref().unwrap_or_default())
1023 .into_owned()
1024 .collect::<std::collections::HashMap<_, _>>(),
1025 )?,
1026 )?)
1027 } else {
1028 None
1029 };
1030 return Ok(Some(
1031 Box::pin(self.end_session(Some(&query), body.as_ref(), req, ctx))
1032 .await?
1033 .to_response()?,
1034 ));
1035 }
1036 if req.path() == "/oauth2/userinfo"
1037 && matches!(req.method(), HttpMethod::Get | HttpMethod::Post)
1038 {
1039 let body = if *req.method() == HttpMethod::Post {
1040 Some(req.body_as_json::<alibi_core::utils::json::JsValue>()?)
1041 } else {
1042 None
1043 };
1044 let call = alibi_core::endpoint::current_endpoint_call_context().unwrap_or_else(|| {
1047 EndpointCall::from_http_request(
1048 req,
1049 &AuthRoute::get("/oauth2/userinfo", "oauth2UserInfo"),
1050 )
1051 });
1052 return Ok(Some(
1053 Box::pin(self.user_info(body.as_ref(), &call, ctx))
1054 .await?
1055 .to_response()?,
1056 ));
1057 }
1058 if req.path() == "/oauth2/token" && *req.method() == HttpMethod::Post {
1059 let body = req.body_as_json::<alibi_core::utils::json::JsValue>()?;
1063 let call = alibi_core::endpoint::current_endpoint_call_context().unwrap_or_else(|| {
1066 EndpointCall::from_http_request(
1067 req,
1068 &AuthRoute::post("/oauth2/token", "oauth2Token"),
1069 )
1070 });
1071 let call = call.handler_context(Some(body.clone()), call.query().cloned());
1072 return Ok(Some(
1073 Box::pin(self.token(Some(&body), &call, ctx))
1074 .await?
1075 .to_response()?,
1076 ));
1077 }
1078 if matches!(req.path(), "/oauth2/introspect" | "/oauth2/revoke")
1079 && *req.method() == HttpMethod::Post
1080 {
1081 let body = req.body_as_json::<alibi_core::utils::json::JsValue>()?;
1084 let revoking = req.path() == "/oauth2/revoke";
1085 let call = alibi_core::endpoint::current_endpoint_call_context().unwrap_or_else(|| {
1086 EndpointCall::from_http_request(
1087 req,
1088 &AuthRoute::post(
1089 req.path(),
1090 if revoking {
1091 "oauth2Revoke"
1092 } else {
1093 "oauth2Introspect"
1094 },
1095 ),
1096 )
1097 });
1098 let call = call.handler_context(Some(body.clone()), call.query().cloned());
1099 let response = if revoking {
1100 Box::pin(self.revoke(Some(&body), &call, ctx)).await?
1101 } else {
1102 Box::pin(self.introspect(Some(&body), &call, ctx)).await?
1103 };
1104 return Ok(Some(response.to_response()?));
1105 }
1106 if req.path() == "/oauth2/authorize"
1107 && matches!(req.method(), HttpMethod::Get | HttpMethod::Post)
1108 {
1109 let value = if *req.method() == HttpMethod::Post {
1110 req.body_as_json::<alibi_core::utils::json::JsValue>()?
1111 } else {
1112 req.query_as_js_value()
1113 };
1114 let call = alibi_core::endpoint::current_endpoint_call_context().unwrap_or_else(|| {
1118 EndpointCall::from_http_request(
1119 req,
1120 &AuthRoute::get("/oauth2/authorize", "oauth2Authorize"),
1121 )
1122 });
1123 let call = call.handler_context(call.body().cloned(), Some(value.clone()));
1124 return Ok(Some(
1125 alibi_core::endpoint::with_endpoint_call_context(
1126 call,
1127 Box::pin(self.authorize(Some(value), req, ctx)),
1128 )
1129 .await?
1130 .to_response()?,
1131 ));
1132 }
1133 if req.path() == "/oauth2/register" && *req.method() == HttpMethod::Post {
1134 let body: alibi_core::utils::json::JsValue = req.body_as_json()?;
1135 return Ok(Some(
1136 Box::pin(self.register_client(Some(&body), req, ctx))
1137 .await?
1138 .to_response()?,
1139 ));
1140 }
1141 if req.path() == "/oauth2/update-client" && *req.method() == HttpMethod::Post {
1142 let body: alibi_core::utils::json::JsValue = req.body_as_json()?;
1143 let input =
1144 alibi_core::utils::json::from_value(clients::validate_update(Some(&body))?)?;
1145 return Ok(Some(
1146 Box::pin(self.update_client(input, req, ctx, false))
1147 .await?
1148 .to_response()?,
1149 ));
1150 }
1151 if matches!(
1152 req.path(),
1153 "/oauth2/delete-client" | "/oauth2/client/rotate-secret"
1154 ) && *req.method() == HttpMethod::Post
1155 {
1156 let body: alibi_core::utils::json::JsValue = req.body_as_json()?;
1157 let input =
1158 alibi_core::utils::json::from_value(clients::validate_delete(Some(&body))?)?;
1159 if req.path() == "/oauth2/client/rotate-secret" {
1160 return Ok(Some(
1161 Box::pin(self.rotate_client_secret(input, req, ctx))
1162 .await?
1163 .to_response()?,
1164 ));
1165 }
1166 return Ok(Some(
1167 Box::pin(self.delete_client(input, req, ctx))
1168 .await?
1169 .to_response()?,
1170 ));
1171 }
1172 if req.path() == "/oauth2/public-client-prelogin" && *req.method() == HttpMethod::Post {
1173 let body = req.body_as_json()?;
1174 let input =
1175 alibi_core::utils::json::from_value(clients::validate_prelogin(Some(&body))?)?;
1176 return Ok(Some(
1177 Box::pin(self.read_prelogin_client(input, ctx))
1178 .await?
1179 .to_response()?,
1180 ));
1181 }
1182 if req.path() == "/oauth2/continue" && *req.method() == HttpMethod::Post {
1183 let body = req.body_as_json()?;
1184 let input = alibi_core::utils::json::from_value(continuation::validate(Some(&body))?)?;
1185 return Ok(Some(
1186 Box::pin(self.continue_authorization(input, req, ctx))
1187 .await?
1188 .to_response()?,
1189 ));
1190 }
1191 if req.path() == "/oauth2/consent" && *req.method() == HttpMethod::Post {
1192 let body = req.body_as_json()?;
1193 let input = alibi_core::utils::json::from_value(consent::validate(Some(&body))?)?;
1194 return Ok(Some(
1195 Box::pin(self.consent(input, req, ctx))
1196 .await?
1197 .to_response()?,
1198 ));
1199 }
1200 if req.path() == "/oauth2/delete-consent" && *req.method() == HttpMethod::Post {
1201 let body = req.body_as_json()?;
1202 let input =
1203 alibi_core::utils::json::from_value(consent::validate_id(Some(&body), "body")?)?;
1204 return Ok(Some(
1205 Box::pin(self.delete_consent(input, req, ctx))
1206 .await?
1207 .to_response()?,
1208 ));
1209 }
1210 if req.path() == "/oauth2/update-consent" && *req.method() == HttpMethod::Post {
1211 let body = req.body_as_json()?;
1212 let input =
1213 alibi_core::utils::json::from_value(consent::validate_update(Some(&body))?)?;
1214 return Ok(Some(
1215 Box::pin(self.update_consent(input, req, ctx))
1216 .await?
1217 .to_response()?,
1218 ));
1219 }
1220 if req.path() == "/oauth2/get-consent" && *req.method() == HttpMethod::Get {
1221 let query = alibi_core::utils::json::parse_value(&serde_json::to_string(&req.query)?)?;
1222 let input =
1223 alibi_core::utils::json::from_value(consent::validate_id(Some(&query), "query")?)?;
1224 return Ok(Some(
1225 Box::pin(self.read_consent(input, req, ctx))
1226 .await?
1227 .to_response()?,
1228 ));
1229 }
1230 if req.path() == "/oauth2/get-consents" && *req.method() == HttpMethod::Get {
1231 return Ok(Some(
1232 Box::pin(self.list_consents(req, ctx))
1233 .await?
1234 .to_response()?,
1235 ));
1236 }
1237 if req.path() == "/oauth2/get-clients" && *req.method() == HttpMethod::Get {
1238 return Ok(Some(
1239 Box::pin(self.list_clients(req, ctx)).await?.to_response()?,
1240 ));
1241 }
1242 if matches!(req.path(), "/oauth2/get-client" | "/oauth2/public-client")
1243 && *req.method() == HttpMethod::Get
1244 {
1245 let query = alibi_core::utils::json::parse_value(&serde_json::to_string(&req.query)?)?;
1246 let input = alibi_core::utils::json::from_value(clients::validate_read(Some(&query))?)?;
1247 if req.path() == "/oauth2/public-client" {
1248 return Ok(Some(
1249 Box::pin(self.read_public_client(input, req, ctx))
1250 .await?
1251 .to_response()?,
1252 ));
1253 }
1254 return Ok(Some(
1255 Box::pin(self.read_client(input, req, ctx))
1256 .await?
1257 .to_response()?,
1258 ));
1259 }
1260 if req.path() == "/oauth2/create-client" && *req.method() == HttpMethod::Post {
1261 let body: alibi_core::utils::json::JsValue = req.body_as_json()?;
1262 let input = alibi_core::utils::json::from_value(clients::validate(Some(&body))?)?;
1263 return Ok(Some(
1264 Box::pin(self.create_client(input, req, ctx))
1265 .await?
1266 .to_response()?,
1267 ));
1268 }
1269 Ok(None)
1270 }
1271 async fn on_init(&self, ctx: &mut alibi_core::AuthInitContext<S>) -> AuthResult<()> {
1272 self.validate_extensions()?;
1273 for scope in self
1274 .config
1275 .client_registration_allowed_scopes
1276 .iter()
1277 .flatten()
1278 .chain(
1279 self.config
1280 .client_registration_default_scopes
1281 .iter()
1282 .flatten(),
1283 )
1284 {
1285 if !self.config.scopes.contains(scope) {
1286 return Err(AuthError::config(format!(
1287 "clientRegistrationAllowedScope {scope} not found in scopes"
1288 )));
1289 }
1290 }
1291 if self
1292 .config
1293 .advertised_scopes
1294 .as_ref()
1295 .is_some_and(|scopes| {
1296 scopes
1297 .iter()
1298 .any(|scope| !self.config.scopes.contains(scope))
1299 })
1300 {
1301 return Err(AuthError::config(
1302 "advertisedMetadata.scopes_supported contains an unsupported scope",
1303 ));
1304 }
1305 let configured_resources = self
1306 .config
1307 .resources
1308 .iter()
1309 .filter_map(|resource| {
1310 resource
1311 .as_str()
1312 .or_else(|| resource.get("identifier").and_then(Value::as_str))
1313 })
1314 .collect::<std::collections::HashSet<_>>();
1315 for (option, identifiers) in [
1316 (
1317 "clientRegistrationDefaultResources",
1318 &self.config.client_registration_default_resources,
1319 ),
1320 (
1321 "clientRegistrationAllowedResources",
1322 &self.config.client_registration_allowed_resources,
1323 ),
1324 ] {
1325 for identifier in identifiers.iter().flatten() {
1326 if !configured_resources.contains(identifier.as_str()) {
1327 return Err(AuthError::config(format!(
1328 "{option} resource {identifier} not found in resources"
1329 )));
1330 }
1331 }
1332 }
1333 if self
1334 .config
1335 .pairwise_secret
1336 .as_ref()
1337 .is_some_and(|secret| !secret.is_empty() && secret.len() < 32)
1338 {
1339 return Err(AuthError::config(
1340 "pairwiseSecret must be at least 32 characters long for adequate HMAC-SHA256 security",
1341 ));
1342 }
1343 if self
1344 .config
1345 .grant_types
1346 .iter()
1347 .any(|grant| grant == "refresh_token")
1348 && !self
1349 .config
1350 .grant_types
1351 .iter()
1352 .any(|grant| grant == "authorization_code")
1353 {
1354 return Err(AuthError::config(
1355 "refresh_token grant requires authorization_code grant",
1356 ));
1357 }
1358 if self.config.disable_jwt_plugin && self.config.client_secret_hasher.is_some() {
1359 return Err(AuthError::config(
1360 "unable to store hashed secrets because id tokens will be signed with secret",
1361 ));
1362 }
1363 if !self.config.disable_jwt_plugin && self.config.client_secret_cipher.is_some() {
1364 return Err(AuthError::config(
1365 "encryption method not recommended, please use 'hashed' or the 'hash' function",
1366 ));
1367 }
1368 let mut discovery_ids = std::collections::HashSet::new();
1369 for discovery in &self.config.client_discoveries {
1370 if alibi_core::utils::javascript::trim(discovery.id()).is_empty() {
1371 return Err(AuthError::config(
1372 "OAuth Provider extension client discovery id cannot be empty",
1373 ));
1374 }
1375 if !discovery_ids.insert(discovery.id()) {
1376 return Err(AuthError::config(format!(
1377 "OAuth Provider extensions register client discovery id \"{}\" more than once. Extension contributions must be disjoint.",
1378 discovery.id()
1379 )));
1380 }
1381 }
1382 self.seed_resources(ctx.database.as_ref()).await?;
1383 ctx.extensions.insert(resource_seed::SeedState::default());
1384 ctx.extensions.insert(discovery::TrustedClients {
1385 ids: self.config.cached_trusted_clients.clone(),
1386 cache: self.trusted_clients.clone(),
1387 });
1388 ctx.extensions.insert(discovery::ClientDiscoveries(
1389 self.config.client_discoveries.clone(),
1390 ));
1391 ctx.register_session_deletion_hook(std::sync::Arc::new(
1392 session_retirement::TokenRetirementHook(self.clone()),
1393 ));
1394 Ok(())
1395 }
1396}