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