aetheris_server/auth/
google.rs1use openidconnect::core::{
2 CoreClient, CoreIdTokenClaims, CoreIdTokenVerifier, CoreProviderMetadata,
3};
4use openidconnect::{
5 ClientId, ClientSecret, EndpointMaybeSet, EndpointNotSet, EndpointSet, IssuerUrl, Nonce,
6};
7use std::str::FromStr;
8use tonic::Status;
9
10pub struct GoogleOidcClient {
11 client: CoreClient<
12 EndpointSet,
13 EndpointNotSet,
14 EndpointNotSet,
15 EndpointNotSet,
16 EndpointMaybeSet,
17 EndpointMaybeSet,
18 >,
19}
20
21impl GoogleOidcClient {
22 pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
31 let client_id =
32 std::env::var("GOOGLE_CLIENT_ID").map_err(|_| "GOOGLE_CLIENT_ID missing")?;
33 let client_secret =
34 std::env::var("GOOGLE_CLIENT_SECRET").map_err(|_| "GOOGLE_CLIENT_SECRET missing")?;
35
36 let http_client = reqwest::Client::builder()
37 .redirect(reqwest::redirect::Policy::none())
38 .build()?;
39
40 let provider_metadata = CoreProviderMetadata::discover_async(
41 IssuerUrl::new("https://accounts.google.com".to_string())?,
42 &|req: openidconnect::HttpRequest| {
43 let client = http_client.clone();
44 async move {
45 let resp = client
46 .execute(req.try_into().map_err(|e| {
47 openidconnect::HttpClientError::Other(format!("Reqwest error: {e}"))
48 })?)
49 .await
50 .map_err(|e| openidconnect::HttpClientError::Reqwest(Box::new(e)))?;
51
52 let status = resp.status();
53 let headers = resp.headers().clone();
54 let body = resp
55 .bytes()
56 .await
57 .map_err(|e| openidconnect::HttpClientError::Reqwest(Box::new(e)))?;
58
59 let mut http_resp = openidconnect::HttpResponse::new(body.to_vec());
60 *http_resp.status_mut() = status;
61 *http_resp.headers_mut() = headers;
62 Ok::<_, openidconnect::HttpClientError<reqwest::Error>>(http_resp)
63 }
64 },
65 )
66 .await?;
67
68 let client = CoreClient::from_provider_metadata(
69 provider_metadata,
70 ClientId::new(client_id),
71 Some(ClientSecret::new(client_secret)),
72 );
73
74 Ok(Self { client })
75 }
76
77 pub fn verify_token(&self, id_token: &str, nonce: &str) -> Result<CoreIdTokenClaims, Status> {
86 let id_token = openidconnect::core::CoreIdToken::from_str(id_token)
87 .map_err(|e| Status::unauthenticated(format!("Malformed ID token: {e}")))?;
88
89 let verifier: CoreIdTokenVerifier = self.client.id_token_verifier();
90
91 let claims = id_token
92 .claims(&verifier, &Nonce::new(nonce.to_string()))
93 .map_err(|e| Status::unauthenticated(format!("Invalid ID token claims: {e}")))?;
94
95 Ok(claims.clone())
96 }
97}