matrix_sdk/authentication/oauth/
oidc_discovery.rs

1// Copyright 2025 Kévin Commaille
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Requests for [OpenID Connect Provider Discovery].
16//!
17//! [OpenID Connect Provider Discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html
18
19use oauth2::{AsyncHttpClient, HttpClientError, RequestTokenError};
20use ruma::{
21    api::client::discovery::get_authorization_server_metadata::msc2965::AuthorizationServerMetadata,
22    serde::Raw,
23};
24use url::Url;
25
26use super::{
27    error::OAuthDiscoveryError,
28    http_client::{check_http_response_json_content_type, check_http_response_status_code},
29    OAuthHttpClient,
30};
31
32/// Fetch the OpenID Connect provider metadata.
33pub(super) async fn discover(
34    http_client: &OAuthHttpClient,
35    issuer: &str,
36) -> Result<Raw<AuthorizationServerMetadata>, OAuthDiscoveryError> {
37    tracing::debug!("Fetching OpenID Connect provider metadata...");
38
39    let mut url = Url::parse(issuer)?;
40
41    // If the path doesn't end with a slash, the last segment is removed when
42    // using `join`.
43    if !url.path().ends_with('/') {
44        let mut path = url.path().to_owned();
45        path.push('/');
46        url.set_path(&path);
47    }
48
49    let config_url = url.join(".well-known/openid-configuration")?;
50
51    let request = http::Request::get(config_url.as_str())
52        .body(Vec::new())
53        .map_err(|err| RequestTokenError::Request(HttpClientError::Http(err)))?;
54    let response = http_client.call(request).await.map_err(RequestTokenError::Request)?;
55
56    check_http_response_status_code(&response)?;
57    check_http_response_json_content_type(&response)?;
58
59    let metadata = serde_json::from_slice(&response.into_body())?;
60
61    tracing::debug!(?metadata);
62
63    Ok(metadata)
64}