shield_oauth/
provider.rs

1use oauth2::{
2    AuthUrl, Client, ClientId, ClientSecret, EndpointMaybeSet, EndpointNotSet, IntrospectionUrl,
3    RedirectUrl, RevocationUrl, StandardRevocableToken, TokenUrl,
4    basic::{
5        BasicClient, BasicErrorResponse, BasicRevocationErrorResponse,
6        BasicTokenIntrospectionResponse, BasicTokenResponse,
7    },
8};
9use secrecy::{ExposeSecret, SecretString};
10use shield::{ConfigurationError, Provider};
11
12use crate::method::OAUTH_METHOD_ID;
13
14type OauthClient = Client<
15    BasicErrorResponse,
16    BasicTokenResponse,
17    BasicTokenIntrospectionResponse,
18    StandardRevocableToken,
19    BasicRevocationErrorResponse,
20    EndpointMaybeSet,
21    EndpointNotSet,
22    EndpointMaybeSet,
23    EndpointMaybeSet,
24    EndpointMaybeSet,
25>;
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum OauthProviderVisibility {
29    Public,
30    Unlisted,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum OauthProviderPkceCodeChallenge {
35    None,
36    Plain,
37    S256,
38}
39
40#[derive(Clone, Debug)]
41pub struct OauthProvider {
42    pub id: String,
43    pub name: String,
44    pub slug: Option<String>,
45    pub visibility: OauthProviderVisibility,
46    pub client_id: String,
47    pub client_secret: Option<SecretString>,
48    pub scopes: Option<Vec<String>>,
49    pub redirect_url: Option<String>,
50    pub authorization_url: Option<String>,
51    pub authorization_url_params: Option<String>,
52    pub token_url: Option<String>,
53    pub token_url_params: Option<String>,
54    pub introspection_url: Option<String>,
55    pub introspection_url_params: Option<String>,
56    pub revocation_url: Option<String>,
57    pub revocation_url_params: Option<String>,
58    pub pkce_code_challenge: OauthProviderPkceCodeChallenge,
59    pub icon_url: Option<String>,
60}
61
62impl OauthProvider {
63    pub async fn oauth_client(&self) -> Result<OauthClient, ConfigurationError> {
64        let mut client = BasicClient::new(ClientId::new(self.client_id.clone()));
65
66        if let Some(client_secret) = &self.client_secret {
67            client = client
68                .set_client_secret(ClientSecret::new(client_secret.expose_secret().to_owned()));
69        }
70
71        if let Some(redirect_url) = &self.redirect_url {
72            client = client.set_redirect_uri(
73                RedirectUrl::new(redirect_url.clone())
74                    .map_err(|err| ConfigurationError::Invalid(err.to_string()))?,
75            );
76        }
77
78        let client = client.set_auth_uri_option(
79            self.authorization_url
80                .as_ref()
81                .map(|authorization_url| {
82                    AuthUrl::new(authorization_url.clone())
83                        .map_err(|err| ConfigurationError::Invalid(err.to_string()))
84                })
85                .transpose()?,
86        );
87
88        let client = client.set_token_uri_option(
89            self.token_url
90                .as_ref()
91                .map(|token_url| {
92                    TokenUrl::new(token_url.clone())
93                        .map_err(|err| ConfigurationError::Invalid(err.to_string()))
94                })
95                .transpose()?,
96        );
97
98        let client = client.set_introspection_url_option(
99            self.introspection_url
100                .as_ref()
101                .map(|introspection_url| {
102                    IntrospectionUrl::new(introspection_url.clone())
103                        .map_err(|err| ConfigurationError::Invalid(err.to_string()))
104                })
105                .transpose()?,
106        );
107
108        let client = client.set_revocation_url_option(
109            self.revocation_url
110                .as_ref()
111                .map(|revocation_url| {
112                    RevocationUrl::new(revocation_url.clone())
113                        .map_err(|err| ConfigurationError::Invalid(err.to_string()))
114                })
115                .transpose()?,
116        );
117
118        Ok(client)
119    }
120}
121
122impl Provider for OauthProvider {
123    fn method_id(&self) -> String {
124        OAUTH_METHOD_ID.to_owned()
125    }
126
127    fn id(&self) -> Option<String> {
128        Some(self.id.clone())
129    }
130
131    fn name(&self) -> String {
132        self.name.clone()
133    }
134}