mas_oidc_client/requests/
userinfo.rs1use std::collections::HashMap;
12
13use headers::{ContentType, HeaderMapExt, HeaderValue};
14use http::header::ACCEPT;
15use mas_http::RequestBuilderExt;
16use mime::Mime;
17use serde_json::Value;
18use url::Url;
19
20use super::jose::JwtVerificationData;
21use crate::{
22 error::{IdTokenError, ResponseExt, UserInfoError},
23 requests::jose::verify_signed_jwt,
24};
25
26#[tracing::instrument(skip_all, fields(userinfo_endpoint))]
55pub async fn fetch_userinfo(
56 http_client: &reqwest::Client,
57 userinfo_endpoint: &Url,
58 access_token: &str,
59 jwt_verification_data: Option<JwtVerificationData<'_>>,
60) -> Result<HashMap<String, Value>, UserInfoError> {
61 tracing::debug!("Obtaining user info…");
62
63 let expected_content_type = if jwt_verification_data.is_some() {
64 "application/jwt"
65 } else {
66 mime::APPLICATION_JSON.as_ref()
67 };
68
69 let userinfo_request = http_client
70 .get(userinfo_endpoint.as_str())
71 .bearer_auth(access_token)
72 .header(ACCEPT, HeaderValue::from_static(expected_content_type));
73
74 let userinfo_response = userinfo_request
75 .send_traced()
76 .await?
77 .error_from_oauth2_error_response()
78 .await?;
79
80 let content_type: Mime = userinfo_response
81 .headers()
82 .typed_try_get::<ContentType>()
83 .map_err(|_| UserInfoError::InvalidResponseContentTypeValue)?
84 .ok_or(UserInfoError::MissingResponseContentType)?
85 .into();
86
87 if content_type.essence_str() != expected_content_type {
88 return Err(UserInfoError::UnexpectedResponseContentType {
89 expected: expected_content_type.to_owned(),
90 got: content_type.to_string(),
91 });
92 }
93
94 let claims = if let Some(verification_data) = jwt_verification_data {
95 let response_body = userinfo_response.text().await?;
96 verify_signed_jwt(&response_body, verification_data)
97 .map_err(IdTokenError::from)?
98 .into_parts()
99 .1
100 } else {
101 userinfo_response.json().await?
102 };
103
104 Ok(claims)
105}