tower_oauth2_resource_server/
builder.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use serde::de::DeserializeOwned;
4
5use crate::{
6    auth_resolver::{AuthorizerResolver, IssuerAuthorizerResolver, SingleAuthorizerResolver},
7    error::StartupError,
8    jwt_resolver::BearerTokenResolver,
9    server::OAuth2ResourceServer,
10    tenant::TenantConfiguration,
11};
12
13pub struct OAuth2ResourceServerBuilder<Claims> {
14    tenant_configurations: Vec<TenantConfiguration>,
15    auth_resolver: Option<Arc<dyn AuthorizerResolver<Claims>>>,
16    bearer_token_resolver: Option<Arc<dyn BearerTokenResolver + Send + Sync>>,
17    phantom: PhantomData<Claims>,
18}
19
20impl<Claims> OAuth2ResourceServer<Claims>
21where
22    Claims: Clone + DeserializeOwned + Send + Sync + 'static,
23{
24    pub fn builder() -> OAuth2ResourceServerBuilder<Claims> {
25        OAuth2ResourceServerBuilder::new()
26    }
27}
28
29impl<Claims> OAuth2ResourceServerBuilder<Claims> {
30    fn new() -> Self {
31        OAuth2ResourceServerBuilder::<Claims> {
32            tenant_configurations: Vec::new(),
33            auth_resolver: None,
34            bearer_token_resolver: None,
35            phantom: PhantomData,
36        }
37    }
38}
39
40impl<Claims> OAuth2ResourceServerBuilder<Claims>
41where
42    Claims: Clone + DeserializeOwned + Send + Sync + 'static,
43{
44    /// Add a tenant (authorization server).
45    pub fn add_tenant(mut self, tenant_configuration: TenantConfiguration) -> Self {
46        self.tenant_configurations.push(tenant_configuration);
47        self
48    }
49
50    /// Add multiple tenants (authorization servers).
51    pub fn add_tenants(mut self, tenant_configurations: Vec<TenantConfiguration>) -> Self {
52        self.tenant_configurations.extend(tenant_configurations);
53        self
54    }
55
56    /// Provide a custom authorization resolver.
57    ///
58    /// Only needs to be provided if the default resolver is not sufficient.
59    ///
60    /// See [AuthorizerResolver] for more information.
61    pub fn auth_resolver(mut self, auth_resolver: Arc<dyn AuthorizerResolver<Claims>>) -> Self {
62        self.auth_resolver = Some(auth_resolver);
63        self
64    }
65
66    /// Provide a custom bearer token resolver.
67    ///
68    /// Only needs to be provided if the default bearer token resolver is not sufficient.
69    ///
70    /// See [BearerTokenResolver] for more information.
71    pub fn bearer_token_resolver(
72        mut self,
73        bearer_token_resolver: Arc<dyn BearerTokenResolver + Send + Sync>,
74    ) -> Self {
75        self.bearer_token_resolver = Some(bearer_token_resolver);
76        self
77    }
78
79    /// Construct an OAuth2ResourceServer.
80    ///
81    /// During construction the OIDC Provider Configuration endpoint of the
82    /// authorization server might be queried.
83    /// Thus, the operation can fail and therefore returns a Result.
84    pub async fn build(self) -> Result<OAuth2ResourceServer<Claims>, StartupError> {
85        if self.tenant_configurations.is_empty() {
86            return Err(StartupError::InvalidParameter(
87                "At least one TenantConfiguration is required".to_owned(),
88            ));
89        }
90        let num_tenants = self.tenant_configurations.len();
91        let auth_resolver = self.auth_resolver.unwrap_or_else(|| {
92            if num_tenants == 1 {
93                Arc::new(SingleAuthorizerResolver {})
94            } else {
95                Arc::new(IssuerAuthorizerResolver {})
96            }
97        });
98        OAuth2ResourceServer::new(
99            self.tenant_configurations,
100            auth_resolver,
101            self.bearer_token_resolver,
102        )
103        .await
104    }
105}
106
107impl<Claims> Default for OAuth2ResourceServerBuilder<Claims> {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use serde::Deserialize;
116
117    use super::*;
118
119    #[derive(Clone, Debug, Deserialize)]
120    struct Claims {}
121
122    #[tokio::test]
123    async fn should_require_tenant_configurations() {
124        let result = OAuth2ResourceServerBuilder::<Claims>::new().build().await;
125        assert!(result.is_err());
126        assert_eq!(
127            result.unwrap_err(),
128            StartupError::InvalidParameter(
129                "At least one TenantConfiguration is required".to_owned()
130            )
131        )
132    }
133}