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}