1use std::collections::{BTreeMap, BTreeSet};
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::{Arc, RwLock};
5
6use rustauth_core::db::{Session, User};
7use rustauth_core::error::RustAuthError;
8use rustauth_core::options::RateLimitRule;
9use serde_json::{Map, Value};
10use thiserror::Error;
11
12use crate::models::SchemaClient;
13
14type ClientReferenceFuture =
15 Pin<Box<dyn Future<Output = Result<Option<String>, RustAuthError>> + Send>>;
16type ClientPrivilegesFuture = Pin<Box<dyn Future<Output = Result<bool, RustAuthError>> + Send>>;
17type JsonObjectFuture =
18 Pin<Box<dyn Future<Output = Result<Map<String, Value>, RustAuthError>> + Send>>;
19type OptionalStringFuture =
20 Pin<Box<dyn Future<Output = Result<Option<String>, RustAuthError>> + Send>>;
21type RequestUriFuture =
22 Pin<Box<dyn Future<Output = Result<Option<Vec<(String, String)>>, RustAuthError>> + Send>>;
23type StringGeneratorFuture = Pin<Box<dyn Future<Output = Result<String, RustAuthError>> + Send>>;
24type BoolResolverFuture = Pin<Box<dyn Future<Output = Result<bool, RustAuthError>> + Send>>;
25type RefreshTokenEncodeFuture = Pin<Box<dyn Future<Output = Result<String, RustAuthError>> + Send>>;
26type RefreshTokenDecodeFuture =
27 Pin<Box<dyn Future<Output = Result<RefreshTokenFormatDecodeOutput, RustAuthError>> + Send>>;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ClientReferenceInput {
32 pub user: Option<User>,
33 pub session: Option<Session>,
34}
35
36#[derive(Clone)]
38pub struct ClientReferenceResolver {
39 resolver: Arc<dyn Fn(ClientReferenceInput) -> ClientReferenceFuture + Send + Sync>,
40}
41
42impl ClientReferenceResolver {
43 pub fn new<F, Fut>(resolver: F) -> Self
45 where
46 F: Fn(ClientReferenceInput) -> Fut + Send + Sync + 'static,
47 Fut: Future<Output = Result<Option<String>, RustAuthError>> + Send + 'static,
48 {
49 Self {
50 resolver: Arc::new(move |input| Box::pin(resolver(input))),
51 }
52 }
53
54 pub async fn resolve(
55 &self,
56 input: ClientReferenceInput,
57 ) -> Result<Option<String>, RustAuthError> {
58 (self.resolver)(input).await
59 }
60}
61
62impl std::fmt::Debug for ClientReferenceResolver {
63 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 formatter.write_str("ClientReferenceResolver(..)")
65 }
66}
67
68impl PartialEq for ClientReferenceResolver {
69 fn eq(&self, _other: &Self) -> bool {
70 true
71 }
72}
73
74impl Eq for ClientReferenceResolver {}
75
76#[derive(Clone, Default)]
77pub struct TrustedClientCache {
78 clients: Arc<RwLock<BTreeMap<String, SchemaClient>>>,
79}
80
81impl TrustedClientCache {
82 pub fn get(&self, client_id: &str) -> Result<Option<SchemaClient>, RustAuthError> {
83 let clients = self
84 .clients
85 .read()
86 .map_err(|_| RustAuthError::Api("trusted client cache lock poisoned".to_owned()))?;
87 Ok(clients.get(client_id).cloned())
88 }
89
90 pub fn insert(&self, client: SchemaClient) -> Result<(), RustAuthError> {
91 let mut clients = self
92 .clients
93 .write()
94 .map_err(|_| RustAuthError::Api("trusted client cache lock poisoned".to_owned()))?;
95 clients.insert(client.client_id.clone(), client);
96 Ok(())
97 }
98}
99
100impl std::fmt::Debug for TrustedClientCache {
101 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 formatter.write_str("TrustedClientCache(..)")
103 }
104}
105
106impl PartialEq for TrustedClientCache {
107 fn eq(&self, _other: &Self) -> bool {
108 true
109 }
110}
111
112impl Eq for TrustedClientCache {}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub enum ClientPrivilegeAction {
117 Create,
118 Read,
119 Update,
120 Delete,
121 List,
122 Rotate,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct ClientPrivilegesInput {
128 pub action: ClientPrivilegeAction,
129 pub user: Option<User>,
130 pub session: Option<Session>,
131}
132
133#[derive(Clone)]
135pub struct ClientPrivilegesResolver {
136 resolver: Arc<dyn Fn(ClientPrivilegesInput) -> ClientPrivilegesFuture + Send + Sync>,
137}
138
139impl ClientPrivilegesResolver {
140 pub fn new<F, Fut>(resolver: F) -> Self
141 where
142 F: Fn(ClientPrivilegesInput) -> Fut + Send + Sync + 'static,
143 Fut: Future<Output = Result<bool, RustAuthError>> + Send + 'static,
144 {
145 Self {
146 resolver: Arc::new(move |input| Box::pin(resolver(input))),
147 }
148 }
149
150 pub async fn resolve(&self, input: ClientPrivilegesInput) -> Result<bool, RustAuthError> {
151 (self.resolver)(input).await
152 }
153}
154
155impl std::fmt::Debug for ClientPrivilegesResolver {
156 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 formatter.write_str("ClientPrivilegesResolver(..)")
158 }
159}
160
161impl PartialEq for ClientPrivilegesResolver {
162 fn eq(&self, _other: &Self) -> bool {
163 true
164 }
165}
166
167impl Eq for ClientPrivilegesResolver {}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct ClientSecretHashInput {
172 pub secret: String,
173}
174
175#[derive(Clone)]
177pub struct ClientSecretHashResolver {
178 resolver: Arc<dyn Fn(ClientSecretHashInput) -> StringGeneratorFuture + Send + Sync>,
179}
180
181impl ClientSecretHashResolver {
182 pub fn new<F, Fut>(resolver: F) -> Self
183 where
184 F: Fn(ClientSecretHashInput) -> Fut + Send + Sync + 'static,
185 Fut: Future<Output = Result<String, RustAuthError>> + Send + 'static,
186 {
187 Self {
188 resolver: Arc::new(move |input| Box::pin(resolver(input))),
189 }
190 }
191
192 pub async fn resolve(&self, input: ClientSecretHashInput) -> Result<String, RustAuthError> {
193 (self.resolver)(input).await
194 }
195}
196
197impl std::fmt::Debug for ClientSecretHashResolver {
198 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 formatter.write_str("ClientSecretHashResolver(..)")
200 }
201}
202
203impl PartialEq for ClientSecretHashResolver {
204 fn eq(&self, _other: &Self) -> bool {
205 true
206 }
207}
208
209impl Eq for ClientSecretHashResolver {}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct ClientSecretVerifyInput {
214 pub secret: String,
215 pub stored_hash: String,
216}
217
218#[derive(Clone)]
220pub struct ClientSecretVerifyResolver {
221 resolver: Arc<dyn Fn(ClientSecretVerifyInput) -> BoolResolverFuture + Send + Sync>,
222}
223
224impl ClientSecretVerifyResolver {
225 pub fn new<F, Fut>(resolver: F) -> Self
226 where
227 F: Fn(ClientSecretVerifyInput) -> Fut + Send + Sync + 'static,
228 Fut: Future<Output = Result<bool, RustAuthError>> + Send + 'static,
229 {
230 Self {
231 resolver: Arc::new(move |input| Box::pin(resolver(input))),
232 }
233 }
234
235 pub async fn resolve(&self, input: ClientSecretVerifyInput) -> Result<bool, RustAuthError> {
236 (self.resolver)(input).await
237 }
238}
239
240impl std::fmt::Debug for ClientSecretVerifyResolver {
241 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 formatter.write_str("ClientSecretVerifyResolver(..)")
243 }
244}
245
246impl PartialEq for ClientSecretVerifyResolver {
247 fn eq(&self, _other: &Self) -> bool {
248 true
249 }
250}
251
252impl Eq for ClientSecretVerifyResolver {}
253
254#[derive(Debug, Clone, PartialEq, Eq)]
256pub struct TokenHashInput {
257 pub token: String,
258 pub token_type: String,
259}
260
261#[derive(Clone)]
263pub struct TokenHashResolver {
264 resolver: Arc<dyn Fn(TokenHashInput) -> StringGeneratorFuture + Send + Sync>,
265}
266
267impl TokenHashResolver {
268 pub fn new<F, Fut>(resolver: F) -> Self
269 where
270 F: Fn(TokenHashInput) -> Fut + Send + Sync + 'static,
271 Fut: Future<Output = Result<String, RustAuthError>> + Send + 'static,
272 {
273 Self {
274 resolver: Arc::new(move |input| Box::pin(resolver(input))),
275 }
276 }
277
278 pub async fn resolve(&self, input: TokenHashInput) -> Result<String, RustAuthError> {
279 (self.resolver)(input).await
280 }
281}
282
283impl std::fmt::Debug for TokenHashResolver {
284 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285 formatter.write_str("TokenHashResolver(..)")
286 }
287}
288
289impl PartialEq for TokenHashResolver {
290 fn eq(&self, _other: &Self) -> bool {
291 true
292 }
293}
294
295impl Eq for TokenHashResolver {}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct PromptRedirectInput {
300 pub user: User,
301 pub session: Session,
302 pub scopes: Vec<String>,
303}
304
305#[derive(Clone)]
307pub struct PromptRedirectResolver {
308 resolver: Arc<dyn Fn(PromptRedirectInput) -> OptionalStringFuture + Send + Sync>,
309}
310
311impl PromptRedirectResolver {
312 pub fn new<F, Fut>(resolver: F) -> Self
313 where
314 F: Fn(PromptRedirectInput) -> Fut + Send + Sync + 'static,
315 Fut: Future<Output = Result<Option<String>, RustAuthError>> + Send + 'static,
316 {
317 Self {
318 resolver: Arc::new(move |input| Box::pin(resolver(input))),
319 }
320 }
321
322 pub async fn resolve(
323 &self,
324 input: PromptRedirectInput,
325 ) -> Result<Option<String>, RustAuthError> {
326 (self.resolver)(input).await
327 }
328}
329
330impl std::fmt::Debug for PromptRedirectResolver {
331 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332 formatter.write_str("PromptRedirectResolver(..)")
333 }
334}
335
336impl PartialEq for PromptRedirectResolver {
337 fn eq(&self, _other: &Self) -> bool {
338 true
339 }
340}
341
342impl Eq for PromptRedirectResolver {}
343
344#[derive(Clone)]
346pub struct PromptShouldRedirectResolver {
347 resolver: Arc<dyn Fn(PromptRedirectInput) -> BoolResolverFuture + Send + Sync>,
348}
349
350impl PromptShouldRedirectResolver {
351 pub fn new<F, Fut>(resolver: F) -> Self
352 where
353 F: Fn(PromptRedirectInput) -> Fut + Send + Sync + 'static,
354 Fut: Future<Output = Result<bool, RustAuthError>> + Send + 'static,
355 {
356 Self {
357 resolver: Arc::new(move |input| Box::pin(resolver(input))),
358 }
359 }
360
361 pub async fn resolve(&self, input: PromptRedirectInput) -> Result<bool, RustAuthError> {
362 (self.resolver)(input).await
363 }
364}
365
366impl std::fmt::Debug for PromptShouldRedirectResolver {
367 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368 formatter.write_str("PromptShouldRedirectResolver(..)")
369 }
370}
371
372impl PartialEq for PromptShouldRedirectResolver {
373 fn eq(&self, _other: &Self) -> bool {
374 true
375 }
376}
377
378impl Eq for PromptShouldRedirectResolver {}
379
380#[derive(Debug, Clone, PartialEq)]
382pub struct CustomIdTokenClaimsInput {
383 pub user: User,
384 pub scopes: Vec<String>,
385 pub metadata: Option<Value>,
386}
387
388#[derive(Debug, Clone, PartialEq)]
390pub struct CustomAccessTokenClaimsInput {
391 pub user: Option<User>,
392 pub reference_id: Option<String>,
393 pub scopes: Vec<String>,
394 pub resource: Vec<String>,
395 pub metadata: Option<Value>,
396}
397
398#[derive(Clone)]
400pub struct CustomAccessTokenClaimsResolver {
401 resolver: Arc<dyn Fn(CustomAccessTokenClaimsInput) -> JsonObjectFuture + Send + Sync>,
402}
403
404impl CustomAccessTokenClaimsResolver {
405 pub fn new<F, Fut>(resolver: F) -> Self
406 where
407 F: Fn(CustomAccessTokenClaimsInput) -> Fut + Send + Sync + 'static,
408 Fut: Future<Output = Result<Map<String, Value>, RustAuthError>> + Send + 'static,
409 {
410 Self {
411 resolver: Arc::new(move |input| Box::pin(resolver(input))),
412 }
413 }
414
415 pub async fn resolve(
416 &self,
417 input: CustomAccessTokenClaimsInput,
418 ) -> Result<Map<String, Value>, RustAuthError> {
419 (self.resolver)(input).await
420 }
421}
422
423impl std::fmt::Debug for CustomAccessTokenClaimsResolver {
424 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425 formatter.write_str("CustomAccessTokenClaimsResolver(..)")
426 }
427}
428
429impl PartialEq for CustomAccessTokenClaimsResolver {
430 fn eq(&self, _other: &Self) -> bool {
431 true
432 }
433}
434
435impl Eq for CustomAccessTokenClaimsResolver {}
436
437#[derive(Clone)]
439pub struct CustomIdTokenClaimsResolver {
440 resolver: Arc<dyn Fn(CustomIdTokenClaimsInput) -> JsonObjectFuture + Send + Sync>,
441}
442
443impl CustomIdTokenClaimsResolver {
444 pub fn new<F, Fut>(resolver: F) -> Self
445 where
446 F: Fn(CustomIdTokenClaimsInput) -> Fut + Send + Sync + 'static,
447 Fut: Future<Output = Result<Map<String, Value>, RustAuthError>> + Send + 'static,
448 {
449 Self {
450 resolver: Arc::new(move |input| Box::pin(resolver(input))),
451 }
452 }
453
454 pub async fn resolve(
455 &self,
456 input: CustomIdTokenClaimsInput,
457 ) -> Result<Map<String, Value>, RustAuthError> {
458 (self.resolver)(input).await
459 }
460}
461
462impl std::fmt::Debug for CustomIdTokenClaimsResolver {
463 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464 formatter.write_str("CustomIdTokenClaimsResolver(..)")
465 }
466}
467
468impl PartialEq for CustomIdTokenClaimsResolver {
469 fn eq(&self, _other: &Self) -> bool {
470 true
471 }
472}
473
474impl Eq for CustomIdTokenClaimsResolver {}
475
476#[derive(Debug, Clone, PartialEq)]
478pub struct CustomTokenResponseFieldsInput {
479 pub grant_type: GrantType,
480 pub user: Option<User>,
481 pub scopes: Vec<String>,
482 pub metadata: Option<Value>,
483}
484
485#[derive(Clone)]
487pub struct CustomTokenResponseFieldsResolver {
488 resolver: Arc<dyn Fn(CustomTokenResponseFieldsInput) -> JsonObjectFuture + Send + Sync>,
489}
490
491impl CustomTokenResponseFieldsResolver {
492 pub fn new<F, Fut>(resolver: F) -> Self
493 where
494 F: Fn(CustomTokenResponseFieldsInput) -> Fut + Send + Sync + 'static,
495 Fut: Future<Output = Result<Map<String, Value>, RustAuthError>> + Send + 'static,
496 {
497 Self {
498 resolver: Arc::new(move |input| Box::pin(resolver(input))),
499 }
500 }
501
502 pub async fn resolve(
503 &self,
504 input: CustomTokenResponseFieldsInput,
505 ) -> Result<Map<String, Value>, RustAuthError> {
506 (self.resolver)(input).await
507 }
508}
509
510impl std::fmt::Debug for CustomTokenResponseFieldsResolver {
511 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512 formatter.write_str("CustomTokenResponseFieldsResolver(..)")
513 }
514}
515
516impl PartialEq for CustomTokenResponseFieldsResolver {
517 fn eq(&self, _other: &Self) -> bool {
518 true
519 }
520}
521
522impl Eq for CustomTokenResponseFieldsResolver {}
523
524#[derive(Debug, Clone, PartialEq)]
526pub struct CustomUserInfoClaimsInput {
527 pub user: User,
528 pub scopes: Vec<String>,
529 pub jwt: Value,
530}
531
532#[derive(Clone)]
534pub struct CustomUserInfoClaimsResolver {
535 resolver: Arc<dyn Fn(CustomUserInfoClaimsInput) -> JsonObjectFuture + Send + Sync>,
536}
537
538impl CustomUserInfoClaimsResolver {
539 pub fn new<F, Fut>(resolver: F) -> Self
540 where
541 F: Fn(CustomUserInfoClaimsInput) -> Fut + Send + Sync + 'static,
542 Fut: Future<Output = Result<Map<String, Value>, RustAuthError>> + Send + 'static,
543 {
544 Self {
545 resolver: Arc::new(move |input| Box::pin(resolver(input))),
546 }
547 }
548
549 pub async fn resolve(
550 &self,
551 input: CustomUserInfoClaimsInput,
552 ) -> Result<Map<String, Value>, RustAuthError> {
553 (self.resolver)(input).await
554 }
555}
556
557impl std::fmt::Debug for CustomUserInfoClaimsResolver {
558 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559 formatter.write_str("CustomUserInfoClaimsResolver(..)")
560 }
561}
562
563impl PartialEq for CustomUserInfoClaimsResolver {
564 fn eq(&self, _other: &Self) -> bool {
565 true
566 }
567}
568
569impl Eq for CustomUserInfoClaimsResolver {}
570
571#[derive(Debug, Clone, PartialEq, Eq)]
573pub struct RequestUriResolverInput {
574 pub request_uri: String,
575 pub client_id: Option<String>,
576}
577
578#[derive(Clone)]
580pub struct RequestUriResolver {
581 resolver: Arc<dyn Fn(RequestUriResolverInput) -> RequestUriFuture + Send + Sync>,
582}
583
584impl RequestUriResolver {
585 pub fn new<F, Fut>(resolver: F) -> Self
586 where
587 F: Fn(RequestUriResolverInput) -> Fut + Send + Sync + 'static,
588 Fut: Future<Output = Result<Option<Vec<(String, String)>>, RustAuthError>> + Send + 'static,
589 {
590 Self {
591 resolver: Arc::new(move |input| Box::pin(resolver(input))),
592 }
593 }
594
595 pub async fn resolve(
596 &self,
597 input: RequestUriResolverInput,
598 ) -> Result<Option<Vec<(String, String)>>, RustAuthError> {
599 (self.resolver)(input).await
600 }
601}
602
603impl std::fmt::Debug for RequestUriResolver {
604 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
605 formatter.write_str("RequestUriResolver(..)")
606 }
607}
608
609impl PartialEq for RequestUriResolver {
610 fn eq(&self, _other: &Self) -> bool {
611 true
612 }
613}
614
615impl Eq for RequestUriResolver {}
616
617#[derive(Clone)]
619pub struct StringGeneratorResolver {
620 resolver: Arc<dyn Fn() -> StringGeneratorFuture + Send + Sync>,
621}
622
623impl StringGeneratorResolver {
624 pub fn new<F, Fut>(resolver: F) -> Self
625 where
626 F: Fn() -> Fut + Send + Sync + 'static,
627 Fut: Future<Output = Result<String, RustAuthError>> + Send + 'static,
628 {
629 Self {
630 resolver: Arc::new(move || Box::pin(resolver())),
631 }
632 }
633
634 pub async fn generate(&self) -> Result<String, RustAuthError> {
635 (self.resolver)().await
636 }
637}
638
639impl std::fmt::Debug for StringGeneratorResolver {
640 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
641 formatter.write_str("StringGeneratorResolver(..)")
642 }
643}
644
645impl PartialEq for StringGeneratorResolver {
646 fn eq(&self, _other: &Self) -> bool {
647 true
648 }
649}
650
651impl Eq for StringGeneratorResolver {}
652
653#[derive(Debug, Clone, PartialEq, Eq)]
655pub struct RefreshTokenFormatEncodeInput {
656 pub token: String,
657 pub session_id: Option<String>,
658}
659
660#[derive(Debug, Clone, PartialEq, Eq)]
662pub struct RefreshTokenFormatDecodeOutput {
663 pub session_id: Option<String>,
664 pub token: String,
665}
666
667#[derive(Clone)]
669pub struct RefreshTokenFormatter {
670 encoder: Arc<dyn Fn(RefreshTokenFormatEncodeInput) -> RefreshTokenEncodeFuture + Send + Sync>,
671 decoder: Arc<dyn Fn(String) -> RefreshTokenDecodeFuture + Send + Sync>,
672}
673
674impl RefreshTokenFormatter {
675 pub fn new<Encode, EncodeFuture, Decode, DecodeFuture>(encoder: Encode, decoder: Decode) -> Self
676 where
677 Encode: Fn(RefreshTokenFormatEncodeInput) -> EncodeFuture + Send + Sync + 'static,
678 EncodeFuture: Future<Output = Result<String, RustAuthError>> + Send + 'static,
679 Decode: Fn(String) -> DecodeFuture + Send + Sync + 'static,
680 DecodeFuture:
681 Future<Output = Result<RefreshTokenFormatDecodeOutput, RustAuthError>> + Send + 'static,
682 {
683 Self {
684 encoder: Arc::new(move |input| Box::pin(encoder(input))),
685 decoder: Arc::new(move |token| Box::pin(decoder(token))),
686 }
687 }
688
689 pub async fn encode(
690 &self,
691 input: RefreshTokenFormatEncodeInput,
692 ) -> Result<String, RustAuthError> {
693 (self.encoder)(input).await
694 }
695
696 pub async fn decode(
697 &self,
698 token: String,
699 ) -> Result<RefreshTokenFormatDecodeOutput, RustAuthError> {
700 (self.decoder)(token).await
701 }
702}
703
704impl std::fmt::Debug for RefreshTokenFormatter {
705 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
706 formatter.write_str("RefreshTokenFormatter(..)")
707 }
708}
709
710impl PartialEq for RefreshTokenFormatter {
711 fn eq(&self, _other: &Self) -> bool {
712 true
713 }
714}
715
716impl Eq for RefreshTokenFormatter {}
717
718#[derive(Debug, Clone, Default, PartialEq, Eq)]
720pub struct OAuthTokenPrefixes {
721 pub opaque_access_token: Option<String>,
722 pub refresh_token: Option<String>,
723 pub client_secret: Option<String>,
724}
725
726#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
728#[serde(rename_all = "snake_case")]
729pub enum GrantType {
730 AuthorizationCode,
731 ClientCredentials,
732 RefreshToken,
733}
734
735impl GrantType {
736 pub fn as_str(self) -> &'static str {
737 match self {
738 Self::AuthorizationCode => "authorization_code",
739 Self::ClientCredentials => "client_credentials",
740 Self::RefreshToken => "refresh_token",
741 }
742 }
743}
744
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
747#[serde(rename_all = "snake_case")]
748pub enum TokenEndpointAuthMethod {
749 None,
750 ClientSecretBasic,
751 ClientSecretPost,
752}
753
754impl TokenEndpointAuthMethod {
755 pub fn as_str(self) -> &'static str {
756 match self {
757 Self::None => "none",
758 Self::ClientSecretBasic => "client_secret_basic",
759 Self::ClientSecretPost => "client_secret_post",
760 }
761 }
762}
763
764#[derive(Debug, Clone, Copy, PartialEq, Eq)]
766pub enum SecretStorage {
767 Auto,
769 Hashed,
771 Encrypted,
773}
774
775#[derive(Debug, Clone, PartialEq, Eq)]
777pub enum OAuthProviderRateLimit {
778 Default,
780 Disabled,
782 Custom(RateLimitRule),
784}
785
786#[derive(Debug, Clone, PartialEq, Eq)]
788pub struct OAuthProviderRateLimits {
789 pub token: OAuthProviderRateLimit,
790 pub authorize: OAuthProviderRateLimit,
791 pub introspect: OAuthProviderRateLimit,
792 pub revoke: OAuthProviderRateLimit,
793 pub register: OAuthProviderRateLimit,
794 pub userinfo: OAuthProviderRateLimit,
795}
796
797impl Default for OAuthProviderRateLimits {
798 fn default() -> Self {
799 Self {
800 token: OAuthProviderRateLimit::Default,
801 authorize: OAuthProviderRateLimit::Default,
802 introspect: OAuthProviderRateLimit::Default,
803 revoke: OAuthProviderRateLimit::Default,
804 register: OAuthProviderRateLimit::Default,
805 userinfo: OAuthProviderRateLimit::Default,
806 }
807 }
808}
809
810#[derive(Debug, Clone, Default, PartialEq, serde::Serialize)]
812pub struct McpMetadataOverrides {
813 #[serde(default, skip_serializing_if = "Map::is_empty")]
814 pub authorization_server: Map<String, Value>,
815 #[serde(default, skip_serializing_if = "Map::is_empty")]
816 pub protected_resource: Map<String, Value>,
817}
818
819#[derive(Debug, Clone, Default, PartialEq)]
821pub struct McpOptions {
822 pub resource: Option<String>,
824 pub metadata: McpMetadataOverrides,
825}
826
827#[derive(Debug, Clone, PartialEq, serde::Serialize)]
829pub struct ResolvedMcpOptions {
830 pub resource: Option<String>,
831 pub metadata: McpMetadataOverrides,
832}
833
834#[derive(Clone)]
836pub struct OAuthProviderOptions {
837 pub scopes: Vec<String>,
838 pub client_registration_default_scopes: Vec<String>,
839 pub client_registration_allowed_scopes: Vec<String>,
840 pub grant_types: Vec<GrantType>,
841 pub login_page: String,
842 pub consent_page: String,
843 pub signup_page: Option<String>,
844 pub select_account_page: Option<String>,
845 pub post_login_page: Option<String>,
846 pub signup_redirect: Option<PromptRedirectResolver>,
847 pub select_account_redirect: Option<PromptRedirectResolver>,
848 pub post_login_redirect: Option<PromptRedirectResolver>,
849 pub signup_should_redirect: Option<PromptShouldRedirectResolver>,
850 pub select_account_should_redirect: Option<PromptShouldRedirectResolver>,
851 pub post_login_should_redirect: Option<PromptShouldRedirectResolver>,
852 pub consent_reference_id: Option<ClientReferenceResolver>,
853 pub code_expires_in: u64,
854 pub access_token_expires_in: u64,
855 pub m2m_access_token_expires_in: u64,
856 pub id_token_expires_in: u64,
857 pub refresh_token_expires_in: u64,
858 pub client_credential_grant_default_scopes: Vec<String>,
859 pub scope_expirations: BTreeMap<String, u64>,
860 pub client_registration_client_secret_expiration: Option<u64>,
861 pub allow_unauthenticated_client_registration: bool,
862 pub allow_dynamic_client_registration: bool,
863 pub allow_public_client_prelogin: bool,
864 pub cached_trusted_clients: BTreeSet<String>,
865 pub client_reference: Option<ClientReferenceResolver>,
866 pub client_privileges: Option<ClientPrivilegesResolver>,
867 pub custom_access_token_claims: Option<CustomAccessTokenClaimsResolver>,
868 pub custom_id_token_claims: Option<CustomIdTokenClaimsResolver>,
869 pub custom_token_response_fields: Option<CustomTokenResponseFieldsResolver>,
870 pub custom_userinfo_claims: Option<CustomUserInfoClaimsResolver>,
871 pub request_uri_resolver: Option<RequestUriResolver>,
872 pub prefixes: OAuthTokenPrefixes,
873 pub generate_client_id: Option<StringGeneratorResolver>,
874 pub generate_client_secret: Option<StringGeneratorResolver>,
875 pub generate_opaque_access_token: Option<StringGeneratorResolver>,
876 pub generate_refresh_token: Option<StringGeneratorResolver>,
877 pub format_refresh_token: Option<RefreshTokenFormatter>,
878 pub disable_jwt_plugin: bool,
879 pub store_client_secret: SecretStorage,
880 pub store_tokens: SecretStorage,
881 pub hash_client_secret: Option<ClientSecretHashResolver>,
882 pub verify_client_secret_hash: Option<ClientSecretVerifyResolver>,
883 pub hash_token: Option<TokenHashResolver>,
884 pub pairwise_secret: Option<String>,
885 pub advertised_scopes_supported: Vec<String>,
886 pub advertised_claims_supported: Vec<String>,
887 pub advertised_jwks_uri: Option<String>,
888 pub advertised_id_token_signing_algorithms: Vec<String>,
889 pub jwks_path: String,
890 pub valid_audiences: Vec<String>,
891 pub rate_limits: OAuthProviderRateLimits,
892 pub mcp: Option<McpOptions>,
894}
895
896impl Default for OAuthProviderOptions {
897 fn default() -> Self {
898 Self {
899 scopes: Vec::new(),
900 client_registration_default_scopes: Vec::new(),
901 client_registration_allowed_scopes: Vec::new(),
902 grant_types: Vec::new(),
903 login_page: String::new(),
904 consent_page: String::new(),
905 signup_page: None,
906 select_account_page: None,
907 post_login_page: None,
908 signup_redirect: None,
909 select_account_redirect: None,
910 post_login_redirect: None,
911 signup_should_redirect: None,
912 select_account_should_redirect: None,
913 post_login_should_redirect: None,
914 consent_reference_id: None,
915 code_expires_in: 600,
916 access_token_expires_in: 3600,
917 m2m_access_token_expires_in: 3600,
918 id_token_expires_in: 36000,
919 refresh_token_expires_in: 2_592_000,
920 client_credential_grant_default_scopes: Vec::new(),
921 scope_expirations: BTreeMap::new(),
922 client_registration_client_secret_expiration: None,
923 allow_unauthenticated_client_registration: false,
924 allow_dynamic_client_registration: false,
925 allow_public_client_prelogin: false,
926 cached_trusted_clients: BTreeSet::new(),
927 client_reference: None,
928 client_privileges: None,
929 custom_access_token_claims: None,
930 custom_id_token_claims: None,
931 custom_token_response_fields: None,
932 custom_userinfo_claims: None,
933 request_uri_resolver: None,
934 prefixes: OAuthTokenPrefixes::default(),
935 generate_client_id: None,
936 generate_client_secret: None,
937 generate_opaque_access_token: None,
938 generate_refresh_token: None,
939 format_refresh_token: None,
940 disable_jwt_plugin: false,
941 store_client_secret: SecretStorage::Auto,
942 store_tokens: SecretStorage::Hashed,
943 hash_client_secret: None,
944 verify_client_secret_hash: None,
945 hash_token: None,
946 pairwise_secret: None,
947 advertised_scopes_supported: Vec::new(),
948 advertised_claims_supported: Vec::new(),
949 advertised_jwks_uri: None,
950 advertised_id_token_signing_algorithms: Vec::new(),
951 jwks_path: "/jwks".to_owned(),
952 valid_audiences: Vec::new(),
953 rate_limits: OAuthProviderRateLimits::default(),
954 mcp: None,
955 }
956 }
957}
958
959impl OAuthProviderOptions {
960 #[must_use]
962 pub fn with_external_jwt(mut self) -> Self {
963 self.disable_jwt_plugin = true;
964 self
965 }
966}
967
968#[derive(Debug, Clone, PartialEq)]
970pub struct ResolvedOAuthProviderOptions {
971 pub scopes: Vec<String>,
972 pub claims: Vec<String>,
973 pub client_registration_allowed_scopes: Vec<String>,
974 pub grant_types: Vec<GrantType>,
975 pub login_page: String,
976 pub consent_page: String,
977 pub signup_page: Option<String>,
978 pub select_account_page: Option<String>,
979 pub post_login_page: Option<String>,
980 pub signup_redirect: Option<PromptRedirectResolver>,
981 pub select_account_redirect: Option<PromptRedirectResolver>,
982 pub post_login_redirect: Option<PromptRedirectResolver>,
983 pub signup_should_redirect: Option<PromptShouldRedirectResolver>,
984 pub select_account_should_redirect: Option<PromptShouldRedirectResolver>,
985 pub post_login_should_redirect: Option<PromptShouldRedirectResolver>,
986 pub consent_reference_id: Option<ClientReferenceResolver>,
987 pub code_expires_in: u64,
988 pub access_token_expires_in: u64,
989 pub m2m_access_token_expires_in: u64,
990 pub id_token_expires_in: u64,
991 pub refresh_token_expires_in: u64,
992 pub client_credential_grant_default_scopes: Vec<String>,
993 pub scope_expirations: BTreeMap<String, u64>,
994 pub client_registration_default_scopes: Vec<String>,
995 pub client_registration_client_secret_expiration: Option<u64>,
996 pub allow_unauthenticated_client_registration: bool,
997 pub allow_dynamic_client_registration: bool,
998 pub allow_public_client_prelogin: bool,
999 pub cached_trusted_clients: BTreeSet<String>,
1000 pub trusted_client_cache: TrustedClientCache,
1001 pub client_reference: Option<ClientReferenceResolver>,
1002 pub client_privileges: Option<ClientPrivilegesResolver>,
1003 pub custom_access_token_claims: Option<CustomAccessTokenClaimsResolver>,
1004 pub custom_id_token_claims: Option<CustomIdTokenClaimsResolver>,
1005 pub custom_token_response_fields: Option<CustomTokenResponseFieldsResolver>,
1006 pub custom_userinfo_claims: Option<CustomUserInfoClaimsResolver>,
1007 pub request_uri_resolver: Option<RequestUriResolver>,
1008 pub prefixes: OAuthTokenPrefixes,
1009 pub generate_client_id: Option<StringGeneratorResolver>,
1010 pub generate_client_secret: Option<StringGeneratorResolver>,
1011 pub generate_opaque_access_token: Option<StringGeneratorResolver>,
1012 pub generate_refresh_token: Option<StringGeneratorResolver>,
1013 pub format_refresh_token: Option<RefreshTokenFormatter>,
1014 pub disable_jwt_plugin: bool,
1015 pub store_client_secret: SecretStorage,
1016 pub store_tokens: SecretStorage,
1017 pub hash_client_secret: Option<ClientSecretHashResolver>,
1018 pub verify_client_secret_hash: Option<ClientSecretVerifyResolver>,
1019 pub hash_token: Option<TokenHashResolver>,
1020 pub pairwise_secret: Option<String>,
1021 pub advertised_scopes_supported: Vec<String>,
1022 pub advertised_claims_supported: Vec<String>,
1023 pub advertised_jwks_uri: Option<String>,
1024 pub advertised_id_token_signing_algorithms: Vec<String>,
1025 pub jwks_path: String,
1026 pub valid_audiences: Vec<String>,
1027 pub rate_limits: OAuthProviderRateLimits,
1028 pub mcp: Option<ResolvedMcpOptions>,
1029}
1030
1031#[derive(Debug, Clone, PartialEq, Eq, Error)]
1033pub enum OAuthProviderConfigError {
1034 #[error("login_page is required")]
1035 MissingLoginPage,
1036 #[error("consent_page is required")]
1037 MissingConsentPage,
1038 #[error("clientRegistrationAllowedScope {0} not found in scopes")]
1039 UnknownClientRegistrationScope(String),
1040 #[error("clientCredentialGrantDefaultScopes {0} not found in scopes")]
1041 UnknownClientCredentialGrantScope(String),
1042 #[error("advertisedMetadata.scopes_supported {0} not found in scopes")]
1043 UnknownAdvertisedScope(String),
1044 #[error(
1045 "pairwiseSecret must be at least 32 characters long for adequate HMAC-SHA256 security"
1046 )]
1047 PairwiseSecretTooShort,
1048 #[error("refresh_token grant requires authorization_code grant")]
1049 RefreshTokenRequiresAuthorizationCode,
1050 #[error("unable to store hashed secrets because id tokens will be signed with secret")]
1051 HashedClientSecretsRequireJwtPlugin,
1052 #[error("encryption method not recommended, please use 'hashed' or the 'hash' function")]
1053 EncryptedClientSecretsWithJwtPlugin,
1054 #[error("mcp.resource must be a valid absolute URL when set")]
1055 InvalidMcpResource,
1056}