oauth2_google/extensions/
internal_oidc_v1_userinfo_endpoint.rs

1use oauth2_client::re_exports::{
2    http::header::{ACCEPT, AUTHORIZATION},
3    serde_json, thiserror, Body, Deserialize, Endpoint, HttpError, Request, Response,
4    SerdeJsonError, Serialize, MIME_APPLICATION_JSON,
5};
6
7pub const URL: &str = "https://openidconnect.googleapis.com/v1/userinfo";
8
9//
10#[derive(Debug, Clone)]
11pub struct OidcV1UserInfoEndpoint {
12    access_token: String,
13}
14impl OidcV1UserInfoEndpoint {
15    pub fn new(access_token: impl AsRef<str>) -> Self {
16        Self {
17            access_token: access_token.as_ref().to_owned(),
18        }
19    }
20}
21
22impl Endpoint for OidcV1UserInfoEndpoint {
23    type RenderRequestError = OidcV1UserInfoEndpointError;
24
25    type ParseResponseOutput = OidcV1UserInfo;
26    type ParseResponseError = OidcV1UserInfoEndpointError;
27
28    fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
29        let request = Request::builder()
30            .uri(URL)
31            .header(AUTHORIZATION, format!("Bearer {}", &self.access_token))
32            .header(ACCEPT, MIME_APPLICATION_JSON)
33            .body(vec![])
34            .map_err(OidcV1UserInfoEndpointError::MakeRequestFailed)?;
35
36        Ok(request)
37    }
38
39    fn parse_response(
40        &self,
41        response: Response<Body>,
42    ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
43        let body = serde_json::from_slice::<OidcV1UserInfo>(response.body())
44            .map_err(OidcV1UserInfoEndpointError::DeResponseBodyFailed)?;
45
46        Ok(body)
47    }
48}
49
50#[derive(Deserialize, Serialize, Debug, Clone)]
51pub struct OidcV1UserInfo {
52    pub sub: String,
53    pub name: Option<String>,
54    pub given_name: Option<String>,
55    pub family_name: Option<String>,
56    pub picture: Option<String>,
57    pub email: Option<String>,
58    pub email_verified: Option<bool>,
59    pub locale: Option<String>,
60}
61
62#[derive(thiserror::Error, Debug)]
63pub enum OidcV1UserInfoEndpointError {
64    #[error("MakeRequestFailed {0}")]
65    MakeRequestFailed(HttpError),
66    //
67    #[error("DeResponseBodyFailed {0}")]
68    DeResponseBodyFailed(SerdeJsonError),
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn de_user_info() {
77        match serde_json::from_str::<OidcV1UserInfo>(include_str!(
78            "../../tests/response_body_json_files/openidconnect_v1_userinfo.json"
79        )) {
80            Ok(user_info) => {
81                assert_eq!(user_info.sub, "110578243643543721809");
82            }
83            Err(err) => panic!("{err}"),
84        }
85    }
86}