mas_handlers/upstream_oauth2/
mod.rs1use std::string::FromUtf8Error;
8
9use mas_data_model::{UpstreamOAuthProvider, UpstreamOAuthProviderTokenAuthMethod};
10use mas_iana::jose::JsonWebSignatureAlg;
11use mas_keystore::{DecryptError, Encrypter, Keystore};
12use mas_oidc_client::types::client_credentials::ClientCredentials;
13use pkcs8::DecodePrivateKey;
14use serde::Deserialize;
15use thiserror::Error;
16use url::Url;
17
18pub(crate) mod authorize;
19pub(crate) mod cache;
20pub(crate) mod callback;
21mod cookie;
22pub(crate) mod link;
23mod template;
24
25use self::cookie::UpstreamSessions as UpstreamSessionsCookie;
26
27#[derive(Debug, Error)]
28#[allow(clippy::enum_variant_names)]
29enum ProviderCredentialsError {
30 #[error("Provider doesn't have a client secret")]
31 MissingClientSecret,
32
33 #[error("Could not decrypt client secret")]
34 DecryptClientSecret {
35 #[from]
36 inner: DecryptError,
37 },
38
39 #[error("Client secret is invalid")]
40 InvalidClientSecret {
41 #[from]
42 inner: FromUtf8Error,
43 },
44
45 #[error("Invalid JSON in client secret")]
46 InvalidClientSecretJson {
47 #[from]
48 inner: serde_json::Error,
49 },
50
51 #[error("Could not parse PEM encoded private key")]
52 InvalidPrivateKey {
53 #[from]
54 inner: pkcs8::Error,
55 },
56}
57
58#[derive(Debug, Deserialize)]
59pub struct SignInWithApple {
60 pub private_key: String,
61 pub team_id: String,
62 pub key_id: String,
63}
64
65fn client_credentials_for_provider(
66 provider: &UpstreamOAuthProvider,
67 token_endpoint: &Url,
68 keystore: &Keystore,
69 encrypter: &Encrypter,
70) -> Result<ClientCredentials, ProviderCredentialsError> {
71 let client_id = provider.client_id.clone();
72
73 let client_secret = provider
75 .encrypted_client_secret
76 .as_deref()
77 .map(|encrypted_client_secret| {
78 let decrypted = encrypter.decrypt_string(encrypted_client_secret)?;
79 let decrypted = String::from_utf8(decrypted)?;
80 Ok::<_, ProviderCredentialsError>(decrypted)
81 })
82 .transpose()?;
83
84 let client_credentials = match provider.token_endpoint_auth_method {
85 UpstreamOAuthProviderTokenAuthMethod::None => ClientCredentials::None { client_id },
86
87 UpstreamOAuthProviderTokenAuthMethod::ClientSecretPost => {
88 ClientCredentials::ClientSecretPost {
89 client_id,
90 client_secret: client_secret
91 .ok_or(ProviderCredentialsError::MissingClientSecret)?,
92 }
93 }
94
95 UpstreamOAuthProviderTokenAuthMethod::ClientSecretBasic => {
96 ClientCredentials::ClientSecretBasic {
97 client_id,
98 client_secret: client_secret
99 .ok_or(ProviderCredentialsError::MissingClientSecret)?,
100 }
101 }
102
103 UpstreamOAuthProviderTokenAuthMethod::ClientSecretJwt => {
104 ClientCredentials::ClientSecretJwt {
105 client_id,
106 client_secret: client_secret
107 .ok_or(ProviderCredentialsError::MissingClientSecret)?,
108 signing_algorithm: provider
109 .token_endpoint_signing_alg
110 .clone()
111 .unwrap_or(JsonWebSignatureAlg::Rs256),
112 token_endpoint: token_endpoint.clone(),
113 }
114 }
115
116 UpstreamOAuthProviderTokenAuthMethod::PrivateKeyJwt => ClientCredentials::PrivateKeyJwt {
117 client_id,
118 keystore: keystore.clone(),
119 signing_algorithm: provider
120 .token_endpoint_signing_alg
121 .clone()
122 .unwrap_or(JsonWebSignatureAlg::Rs256),
123 token_endpoint: token_endpoint.clone(),
124 },
125
126 UpstreamOAuthProviderTokenAuthMethod::SignInWithApple => {
127 let params = client_secret.ok_or(ProviderCredentialsError::MissingClientSecret)?;
128 let params: SignInWithApple = serde_json::from_str(¶ms)?;
129
130 let key = elliptic_curve::SecretKey::from_pkcs8_pem(¶ms.private_key)?;
131
132 ClientCredentials::SignInWithApple {
133 client_id,
134 key,
135 key_id: params.key_id,
136 team_id: params.team_id,
137 }
138 }
139 };
140
141 Ok(client_credentials)
142}