1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-09 04:22:45 +03:00

Add a global HTTP client factory

This commit is contained in:
Quentin Gliech
2022-11-23 13:18:48 +01:00
parent d514a8922c
commit 4227fa7a83
14 changed files with 163 additions and 83 deletions

View File

@@ -41,6 +41,8 @@ use sqlx::PgExecutor;
use thiserror::Error;
use tower::{Service, ServiceExt};
use crate::http_client_factory::HttpClientFactory;
static JWT_BEARER_CLIENT_ASSERTION: &str = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
#[derive(Deserialize)]
@@ -91,6 +93,7 @@ impl Credentials {
#[tracing::instrument(skip_all, err)]
pub async fn verify<S: StorageBackend>(
&self,
http_client_factory: &HttpClientFactory,
encrypter: &Encrypter,
method: &OAuthClientAuthenticationMethod,
client: &Client<S>,
@@ -132,7 +135,7 @@ impl Credentials {
.as_ref()
.ok_or(CredentialsVerificationError::InvalidClientConfig)?;
let jwks = fetch_jwks(jwks)
let jwks = fetch_jwks(http_client_factory, jwks)
.await
.map_err(|_| CredentialsVerificationError::JwksFetchFailed)?;
@@ -166,7 +169,10 @@ impl Credentials {
}
}
async fn fetch_jwks(jwks: &JwksOrJwksUri) -> Result<PublicJsonWebKeySet, BoxError> {
async fn fetch_jwks(
http_client_factory: &HttpClientFactory,
jwks: &JwksOrJwksUri,
) -> Result<PublicJsonWebKeySet, BoxError> {
let uri = match jwks {
JwksOrJwksUri::Jwks(j) => return Ok(j.clone()),
JwksOrJwksUri::JwksUri(u) => u,
@@ -177,7 +183,8 @@ async fn fetch_jwks(jwks: &JwksOrJwksUri) -> Result<PublicJsonWebKeySet, BoxErro
.body(mas_http::EmptyBody::new())
.unwrap();
let mut client = mas_http::client("fetch-jwks")
let mut client = http_client_factory
.client("fetch-jwks")
.await?
.response_body_to_bytes()
.json_response::<PublicJsonWebKeySet>();

View File

@@ -0,0 +1,78 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::Arc;
use axum::body::Full;
use mas_http::{
BodyToBytesResponseLayer, ClientInitError, ClientLayer, ClientService, HttpService,
TracedClient,
};
use tokio::sync::Semaphore;
use tower::{
util::{MapErrLayer, MapRequestLayer},
BoxError, Layer,
};
#[derive(Debug, Clone)]
pub struct HttpClientFactory {
semaphore: Arc<Semaphore>,
}
impl HttpClientFactory {
#[must_use]
pub fn new(concurrency_limit: usize) -> Self {
Self {
semaphore: Arc::new(Semaphore::new(concurrency_limit)),
}
}
/// Constructs a new HTTP client
///
/// # Errors
///
/// Returns an error if the client failed to initialise
pub async fn client<B>(
&self,
operation: &'static str,
) -> Result<ClientService<TracedClient<B>>, ClientInitError>
where
B: axum::body::HttpBody + Send + Sync + 'static,
B::Data: Send,
{
let client = mas_http::make_traced_client::<B>().await?;
let layer = ClientLayer::with_semaphore(operation, self.semaphore.clone());
Ok(layer.layer(client))
}
/// Constructs a new [`HttpService`], suitable for [`mas_oidc_client`]
///
/// # Errors
///
/// Returns an error if the client failed to initialise
pub async fn http_service(
&self,
operation: &'static str,
) -> Result<HttpService, ClientInitError> {
let client = self.client(operation).await?;
let client = (
MapErrLayer::new(BoxError::from),
MapRequestLayer::new(|req: http::Request<_>| req.map(Full::new)),
BodyToBytesResponseLayer::default(),
)
.layer(client);
Ok(HttpService::new(client))
}
}

View File

@@ -26,6 +26,7 @@ pub mod client_authorization;
pub mod cookies;
pub mod csrf;
pub mod fancy_error;
pub mod http_client_factory;
pub mod jwt;
pub mod session;
pub mod user_authorization;