mas_handlers/upstream_oauth2/
mod.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use 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    // Decrypt the client secret
74    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(&params)?;
129
130            let key = elliptic_curve::SecretKey::from_pkcs8_pem(&params.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}