haste_server/auth_n/middleware/
basic_auth.rs

1use crate::{
2    auth_n::oidc::{
3        error::{OIDCError, OIDCErrorCode},
4        routes::token::{ClientCredentialsMethod, client_credentials_to_token_response},
5        schemas::token_body::{OAuth2TokenBody, OAuth2TokenBodyGrantType},
6    },
7    extract::{
8        basic_credentials::BasicCredentialsHeader,
9        path_tenant::{ProjectIdentifier, TenantIdentifier},
10    },
11    services::AppState,
12};
13use axum::{
14    extract::{Request, State},
15    middleware::Next,
16    response::Response,
17};
18use axum_extra::extract::Cached;
19use haste_fhir_search::SearchEngine;
20use haste_fhir_terminology::FHIRTerminology;
21use haste_repository::Repository;
22use std::sync::Arc;
23
24pub async fn basic_auth_middleware<
25    Repo: Repository + Send + Sync + 'static,
26    Search: SearchEngine + Send + Sync + 'static,
27    Terminology: FHIRTerminology + Send + Sync + 'static,
28>(
29    Cached(TenantIdentifier { tenant }): Cached<TenantIdentifier>,
30    Cached(ProjectIdentifier { project }): Cached<ProjectIdentifier>,
31    State(state): State<Arc<AppState<Repo, Search, Terminology>>>,
32    // run the `HeaderMap` extractor
33    BasicCredentialsHeader(credentials): BasicCredentialsHeader,
34    // you can also add more extractors here but the last
35    // extractor must implement `FromRequest` which
36    // `Request` does
37    mut request: Request,
38    next: Next,
39) -> Result<Response, OIDCError> {
40    if let Some(credentials) = credentials {
41        let res = client_credentials_to_token_response(
42            state.as_ref(),
43            &tenant,
44            &project,
45            &None,
46            &OAuth2TokenBody {
47                client_id: credentials.0,
48                client_secret: Some(credentials.1),
49                code: None,
50                code_verifier: None,
51                grant_type: OAuth2TokenBodyGrantType::ClientCredentials,
52                redirect_uri: None,
53                refresh_token: None,
54                scope: None,
55            },
56            ClientCredentialsMethod::BasicAuth,
57        )
58        .await?;
59
60        if let Some(token_response) = res.id_token {
61            request.headers_mut().insert(
62                axum::http::header::AUTHORIZATION,
63                format!("Bearer {}", token_response).parse().unwrap(),
64            );
65        } else {
66            return Err(OIDCError::new(
67                OIDCErrorCode::AccessDenied,
68                Some("Failed to authorize client.".to_string()),
69                None,
70            ));
71        }
72    }
73
74    Ok(next.run(request).await)
75}