This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git


The following commit(s) were added to refs/heads/main by this push:
     new a1e271d  Fix GCP signing token (#338)
a1e271d is described below

commit a1e271d2575d9767341b246b0e231ecec9243092
Author: jackm-mimica <[email protected]>
AuthorDate: Tue May 6 18:41:09 2025 +0100

    Fix GCP signing token (#338)
    
    * Fix GCP signing token
    
    * refresh token replaced by access token
    
    * Added parsing email from id_token
    
    * falls back to the tokeninfo endpoint on error
---
 src/gcp/credential.rs | 88 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 68 insertions(+), 20 deletions(-)

diff --git a/src/gcp/credential.rs b/src/gcp/credential.rs
index 261230b..1d224fd 100644
--- a/src/gcp/credential.rs
+++ b/src/gcp/credential.rs
@@ -223,6 +223,7 @@ struct TokenClaims<'a> {
 struct TokenResponse {
     access_token: String,
     expires_in: u64,
+    id_token: Option<String>,
 }
 
 /// Self-signed JWT (JSON Web Token).
@@ -604,6 +605,37 @@ struct EmailResponse {
     email: String,
 }
 
+#[derive(Debug, Deserialize)]
+struct IdTokenClaims {
+    email: String,
+}
+
+async fn get_token_response(
+    client_id: &str,
+    client_secret: &str,
+    refresh_token: &str,
+    client: &HttpClient,
+    retry: &RetryConfig,
+) -> Result<TokenResponse> {
+    client
+        .post(DEFAULT_TOKEN_GCP_URI)
+        .form([
+            ("grant_type", "refresh_token"),
+            ("client_id", client_id),
+            ("client_secret", client_secret),
+            ("refresh_token", refresh_token),
+        ])
+        .retryable(retry)
+        .idempotent(true)
+        .send()
+        .await
+        .map_err(|source| Error::TokenRequest { source })?
+        .into_body()
+        .json::<TokenResponse>()
+        .await
+        .map_err(|source| Error::TokenResponseBody { source })
+}
+
 impl AuthorizedUserSigningCredentials {
     pub(crate) fn from(credential: AuthorizedUserCredentials) -> 
crate::Result<Self> {
         Ok(Self { credential })
@@ -614,16 +646,42 @@ impl AuthorizedUserSigningCredentials {
         client: &HttpClient,
         retry: &RetryConfig,
     ) -> crate::Result<String> {
+        let response = get_token_response(
+            &self.credential.client_id,
+            &self.credential.client_secret,
+            &self.credential.refresh_token,
+            client,
+            retry,
+        )
+        .await?;
+
+        // Extract email from id_token if available
+        if let Some(id_token) = response.id_token {
+            // Split the JWT string by dots to get the payload section
+            let parts: Vec<&str> = id_token.split('.').collect();
+            if parts.len() == 3 {
+                // Decode the base64-encoded payload (middle part)
+                if let Ok(payload) = BASE64_URL_SAFE_NO_PAD.decode(parts[1]) {
+                    // Parse the payload as JSON and extract the email
+                    if let Ok(claims) = 
serde_json::from_slice::<IdTokenClaims>(&payload) {
+                        return Ok(claims.email);
+                    }
+                }
+                // If any of the parsing steps fail, fallback to other method
+            }
+        }
+
+        // Fallback to the original method if id_token is not available or 
invalid
         let response = client
             .get("https://oauth2.googleapis.com/tokeninfo";)
-            .query(&[("access_token", &self.credential.refresh_token)])
+            .query(&[("access_token", response.access_token)])
             .send_retry(retry)
             .await
             .map_err(|source| Error::TokenRequest { source })?
             .into_body()
             .json::<EmailResponse>()
             .await
-            .map_err(|source| Error::TokenResponseBody { source })?;
+            .map_err(|source: HttpError| Error::TokenResponseBody { source })?;
 
         Ok(response.email)
     }
@@ -639,7 +697,6 @@ impl TokenProvider for AuthorizedUserSigningCredentials {
         retry: &RetryConfig,
     ) -> crate::Result<TemporaryToken<Arc<GcpSigningCredential>>> {
         let email = self.client_email(client, retry).await?;
-
         Ok(TemporaryToken {
             token: Arc::new(GcpSigningCredential {
                 email,
@@ -659,23 +716,14 @@ impl TokenProvider for AuthorizedUserCredentials {
         client: &HttpClient,
         retry: &RetryConfig,
     ) -> crate::Result<TemporaryToken<Arc<GcpCredential>>> {
-        let response = client
-            .post(DEFAULT_TOKEN_GCP_URI)
-            .form([
-                ("grant_type", "refresh_token"),
-                ("client_id", &self.client_id),
-                ("client_secret", &self.client_secret),
-                ("refresh_token", &self.refresh_token),
-            ])
-            .retryable(retry)
-            .idempotent(true)
-            .send()
-            .await
-            .map_err(|source| Error::TokenRequest { source })?
-            .into_body()
-            .json::<TokenResponse>()
-            .await
-            .map_err(|source| Error::TokenResponseBody { source })?;
+        let response = get_token_response(
+            &self.client_id,
+            &self.client_secret,
+            &self.refresh_token,
+            client,
+            retry,
+        )
+        .await?;
 
         Ok(TemporaryToken {
             token: Arc::new(GcpCredential {

Reply via email to