matrix_sdk/authentication/oidc/backend/
server.rs1use chrono::Utc;
19use http::StatusCode;
20use mas_oidc_client::{
21 http_service::HttpService,
22 jose::jwk::PublicJsonWebKeySet,
23 requests::{
24 authorization_code::{
25 access_token_with_authorization_code, build_par_authorization_url,
26 AuthorizationRequestData, AuthorizationValidationData,
27 },
28 discovery::{discover, insecure_discover},
29 jose::{fetch_jwks, JwtVerificationData},
30 refresh_token::refresh_access_token,
31 registration::register_client,
32 revocation::revoke_token,
33 },
34 types::{
35 client_credentials::ClientCredentials,
36 iana::oauth::OAuthTokenTypeHint,
37 oidc::{ProviderMetadata, ProviderMetadataVerificationError, VerifiedProviderMetadata},
38 registration::{ClientRegistrationResponse, VerifiedClientMetadata},
39 IdToken,
40 },
41};
42use ruma::api::client::discovery::{get_authentication_issuer, get_authorization_server_metadata};
43use url::Url;
44
45use super::{OidcBackend, OidcError, RefreshedSessionTokens};
46use crate::{
47 authentication::oidc::{rng, AuthorizationCode, OauthDiscoveryError, OidcSessionTokens},
48 Client,
49};
50
51#[derive(Debug)]
52pub(crate) struct OidcServer {
53 client: Client,
54}
55
56impl OidcServer {
57 pub(crate) fn new(client: Client) -> Self {
58 Self { client }
59 }
60
61 fn http_service(&self) -> HttpService {
62 HttpService::new(self.client.inner.http_client.clone())
63 }
64
65 async fn fetch_jwks(&self, uri: &Url) -> Result<PublicJsonWebKeySet, OidcError> {
70 fetch_jwks(&self.http_service(), uri).await.map_err(Into::into)
71 }
72}
73
74#[async_trait::async_trait]
75impl OidcBackend for OidcServer {
76 async fn discover(
77 &self,
78 insecure: bool,
79 ) -> Result<VerifiedProviderMetadata, OauthDiscoveryError> {
80 match self.client.send(get_authorization_server_metadata::msc2965::Request::new()).await {
81 Ok(response) => {
82 let metadata = response.metadata.deserialize_as::<ProviderMetadata>()?;
83
84 let result = if insecure {
85 metadata.insecure_verify_metadata()
86 } else {
87 let issuer = metadata.issuer.clone().ok_or(
90 mas_oidc_client::error::DiscoveryError::Validation(
91 ProviderMetadataVerificationError::MissingIssuer,
92 ),
93 )?;
94 metadata.validate(&issuer)
95 };
96
97 return Ok(result.map_err(mas_oidc_client::error::DiscoveryError::Validation)?);
98 }
99 Err(error)
100 if error
101 .as_client_api_error()
102 .is_some_and(|err| err.status_code == StatusCode::NOT_FOUND) =>
103 {
104 }
106 Err(error) => return Err(error.into()),
107 };
108
109 #[allow(deprecated)]
112 let issuer =
113 match self.client.send(get_authentication_issuer::msc2965::Request::new()).await {
114 Ok(response) => response.issuer,
115 Err(error)
116 if error
117 .as_client_api_error()
118 .is_some_and(|err| err.status_code == StatusCode::NOT_FOUND) =>
119 {
120 return Err(OauthDiscoveryError::NotSupported);
121 }
122 Err(error) => return Err(error.into()),
123 };
124
125 if insecure {
126 insecure_discover(&self.http_service(), &issuer).await.map_err(Into::into)
127 } else {
128 discover(&self.http_service(), &issuer).await.map_err(Into::into)
129 }
130 }
131
132 async fn trade_authorization_code_for_tokens(
133 &self,
134 provider_metadata: VerifiedProviderMetadata,
135 credentials: ClientCredentials,
136 metadata: VerifiedClientMetadata,
137 auth_code: AuthorizationCode,
138 validation_data: AuthorizationValidationData,
139 ) -> Result<OidcSessionTokens, OidcError> {
140 let jwks = self.fetch_jwks(provider_metadata.jwks_uri()).await?;
141
142 let id_token_verification_data = JwtVerificationData {
143 issuer: provider_metadata.issuer(),
144 jwks: &jwks,
145 client_id: &credentials.client_id().to_owned(),
146 signing_algorithm: metadata.id_token_signed_response_alg(),
147 };
148
149 let (response, id_token) = access_token_with_authorization_code(
150 &self.http_service(),
151 credentials.clone(),
152 provider_metadata.token_endpoint(),
153 auth_code.code,
154 validation_data,
155 Some(id_token_verification_data),
156 Utc::now(),
157 &mut rng()?,
158 )
159 .await?;
160
161 Ok(OidcSessionTokens {
162 access_token: response.access_token,
163 refresh_token: response.refresh_token,
164 latest_id_token: id_token,
165 })
166 }
167
168 async fn refresh_access_token(
169 &self,
170 provider_metadata: VerifiedProviderMetadata,
171 credentials: ClientCredentials,
172 metadata: &VerifiedClientMetadata,
173 refresh_token: String,
174 latest_id_token: Option<IdToken<'static>>,
175 ) -> Result<RefreshedSessionTokens, OidcError> {
176 let jwks = self.fetch_jwks(provider_metadata.jwks_uri()).await?;
177
178 let id_token_verification_data = JwtVerificationData {
179 issuer: provider_metadata.issuer(),
180 jwks: &jwks,
181 client_id: &credentials.client_id().to_owned(),
182 signing_algorithm: &metadata.id_token_signed_response_alg().clone(),
183 };
184
185 refresh_access_token(
186 &self.http_service(),
187 credentials,
188 provider_metadata.token_endpoint(),
189 refresh_token,
190 None,
191 Some(id_token_verification_data),
192 latest_id_token.as_ref(),
193 Utc::now(),
194 &mut rng()?,
195 )
196 .await
197 .map(|(response, _id_token)| RefreshedSessionTokens {
198 access_token: response.access_token,
199 refresh_token: response.refresh_token,
200 })
201 .map_err(Into::into)
202 }
203
204 async fn register_client(
205 &self,
206 registration_endpoint: &Url,
207 client_metadata: VerifiedClientMetadata,
208 software_statement: Option<String>,
209 ) -> Result<ClientRegistrationResponse, OidcError> {
210 register_client(
211 &self.http_service(),
212 registration_endpoint,
213 client_metadata,
214 software_statement,
215 )
216 .await
217 .map_err(Into::into)
218 }
219
220 async fn build_par_authorization_url(
221 &self,
222 client_credentials: ClientCredentials,
223 par_endpoint: &Url,
224 authorization_endpoint: Url,
225 authorization_data: AuthorizationRequestData,
226 ) -> Result<(Url, AuthorizationValidationData), OidcError> {
227 Ok(build_par_authorization_url(
228 &self.http_service(),
229 client_credentials,
230 par_endpoint,
231 authorization_endpoint,
232 authorization_data,
233 Utc::now(),
234 &mut rng()?,
235 )
236 .await?)
237 }
238
239 async fn revoke_token(
240 &self,
241 client_credentials: ClientCredentials,
242 revocation_endpoint: &Url,
243 token: String,
244 token_type_hint: Option<OAuthTokenTypeHint>,
245 ) -> Result<(), OidcError> {
246 Ok(revoke_token(
247 &self.http_service(),
248 client_credentials,
249 revocation_endpoint,
250 token,
251 token_type_hint,
252 Utc::now(),
253 &mut rng()?,
254 )
255 .await?)
256 }
257}