use crate::*; use openidconnect::{core::*, reqwest::async_http_client, *}; state!(WITH_GOOGLE_AUTH: bool = { env::var("GOOGLE_CLIENT_ID").is_ok() && env::var("GOOGLE_CLIENT_SECRET").is_ok() }); state!(GOOGLE_CLIENT: GoogleClient = async { if !*WITH_GOOGLE_AUTH { panic!("Attempted to use google client without credentials!") } let client_id = env::var("GOOGLE_CLIENT_ID")?; let client_secret = env::var("GOOGLE_CLIENT_SECRET")?; let callback_url = "http://localhost/auth/google/callback".to_owned(); // TODO: replace hardcoded cb url with ...? built-in routes? GoogleClient::init(callback_url, client_id, client_secret).await }); pub struct GoogleClient(GoogleOAuthClient); impl GoogleClient { pub async fn init(callback_url: String, client_id: String, client_secret: String) -> Self { let redirect_url = RedirectUrl::new(callback_url).unwrap(); let client_id = ClientId::new(client_id); let client_secret = ClientSecret::new(client_secret); let issuer_url = IssuerUrl::new("https://accounts.google.com".to_string()).unwrap(); let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, async_http_client) .await .unwrap(); let client = CoreClient::from_provider_metadata(provider_metadata, client_id, Some(client_secret)) .set_redirect_uri(redirect_url); Self(client) } pub fn authz_request(&self) -> (url::Url, OAuthCSRF, OAuthNonce) { let mut authz_req = self.0.authorize_url( AuthenticationFlow::::AuthorizationCode, OAuthCSRF::new_random, OAuthNonce::new_random, ); let scopes = ["email"]; for scope in scopes { authz_req = authz_req.add_scope(Scope::new(scope.to_string())); } authz_req.url() } pub async fn get_email(&self, code: OAuthCode, nonce: OAuthNonce) -> Result { let token = self.get_token(code).await?; Ok(token .extra_fields() .id_token() .ok_or(anyhow!("server did not return an ID token"))? .claims(&self.0.id_token_verifier(), &nonce)? .email() .ok_or(anyhow!("email not found in openID claims"))? .to_string()) } pub async fn get_token(&self, code: OAuthCode) -> Result { Ok(self .0 .exchange_code(AuthorizationCode::new(code)) .request_async(async_http_client) .await .map_err(|e| anyhow!(e))?) } } pub type GoogleTokenResponse = StandardTokenResponse< IdTokenFields< EmptyAdditionalClaims, EmptyExtraTokenFields, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, >, CoreTokenType, >; pub type GoogleOAuthClient = Client< EmptyAdditionalClaims, CoreAuthDisplay, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, CoreJsonWebKeyUse, CoreJsonWebKey, CoreAuthPrompt, StandardErrorResponse, GoogleTokenResponse, CoreTokenType, StandardTokenIntrospectionResponse, CoreRevocableToken, StandardErrorResponse, >;