Backend Development 11 min read

Master Google OAuth 2.0 in Rust with Service Account Credentials

This article explains how to implement Google OAuth 2.0 authentication in Rust using service account credentials, covering JWT generation, token management, scope and subject configuration, and demonstrates a simple translation service built on the Google Cloud Translation API.

Architecture Development Notes
Architecture Development Notes
Architecture Development Notes
Master Google OAuth 2.0 in Rust with Service Account Credentials

Background

When developing backend applications in Rust, service accounts are often used for server‑to‑server authentication with Google APIs. This guide shows how to generate a JWT, request an access token, and manage token expiration.

Service Account Credentials Structure

A ServiceAccountCredentials struct stores the fields read from a JSON key file, as well as optional fields for the access token, scopes, and subject.

<code>#[derive(Debug, Clone)]
pub struct ServiceAccountCredentials {
    r#type: String,
    project_id: String,
    private_key_id: String,
    private_key: String,
    client_email: String,
    client_id: String,
    auth_uri: String,
    token_uri: String,
    auth_provider_x509_cert_url: String,
    client_x509_cert_url: String,
    universe_domain: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    token: Option<Token>,
    #[serde(skip_serializing_if = "Option::is_none")]
    scopes: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    sub: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Token {
    expiration_time: i64,
    access_token: String,
}</code>

Creating a ServiceAccountCredentials Instance

You can create an instance either from a JSON file or from a JSON string:

Read the JSON file and deserialize it.

<code>impl ServiceAccountCredentials {
    pub fn from_service_account_file(filepath: PathBuf) -> Result<Self> {
        let credentials_json = fs::read_to_string(filepath)?;
        Ok(serde_json::from_str::<ServiceAccountCredentials>(&credentials_json)?)
    }
}
</code>

Parse a JSON string directly.

<code>impl ServiceAccountCredentials {
    pub fn from_service_account_info(credentials_json: String) -> Result<Self> {
        Ok(serde_json::from_str::<ServiceAccountCredentials>(&credentials_json)?)
    }
}
</code>

Setting the Subject (sub)

Some APIs, such as Gmail, require the sub claim in the JWT. Use with_subject to set it, which also clears the cached token so a new JWT is generated.

<code>pub fn with_subject(&self, subject: &str) -> Self {
    let mut subjected_credential = self.clone();
    subjected_credential.sub = Some(subject.to_owned());
    subjected_credential.token = None;
    subjected_credential
}
</code>

Setting Scopes

Define the OAuth 2.0 scopes required for the target Google API with with_scopes . Changing scopes also clears the cached token.

<code>pub fn with_scopes(&self, scopes: Vec<&str>) -> Self {
    let mut scoped_credentials = self.clone();
    scoped_credentials.scopes = Some(scopes.into_iter().map(|s| s.to_owned()).collect());
    scoped_credentials.token = None;
    scoped_credentials
}
</code>

Getting an Access Token

The get_access_token method checks whether a valid token is cached; if not, it creates a new JWT, requests a token from Google, caches it with a one‑hour expiration, and returns the token.

<code>pub async fn get_access_token(&mut self) -> Result<String> {
    let now = Local::now();
    let iat = now.timestamp();
    match self.token.clone() {
        Some(token) => {
            if iat > token.expiration_time {
                let jwt = self.make_assertion()?;
                let access_token = self.request_token(&jwt).await?;
                self.token = Some(Token { expiration_time: (now + Duration::minutes(58)).timestamp(), access_token: access_token.clone() });
                Ok(access_token)
            } else {
                Ok(token.access_token.clone())
            }
        }
        None => {
            let jwt = self.make_assertion()?;
            let access_token = self.request_token(&jwt).await?;
            self.token = Some(Token { expiration_time: (now + Duration::minutes(58)).timestamp(), access_token: access_token.clone() });
            Ok(access_token)
        }
    }
}
</code>

The helper methods make_assertion builds the JWT and request_token performs the HTTP POST to the token endpoint.

Example: Translation Service

A simple TranslateService struct demonstrates how to combine the credential handling with the Google Cloud Translation API.

<code>static TRANSLATE_SERVICE_SCOPE: &str = "https://www.googleapis.com/auth/cloud-translation";
static TRANSLATE_SERVICE_BASE_URL: &str = "https://translation.googleapis.com/language/translate";

#[derive(Debug, Clone)]
pub struct TranslateService {
    api_key: Option<String>,
    service_account_credentials: Option<ServiceAccountCredentials>,
}

impl TranslateService {
    pub fn new_with_api_key(api_key: String) -> Self { Self { api_key: Some(api_key), service_account_credentials: None } }
    pub fn new_with_credentials(service_account_credentials: ServiceAccountCredentials) -> Self {
        let scoped_credentials = service_account_credentials.with_scopes(vec![TRANSLATE_SERVICE_SCOPE]);
        Self { api_key: None, service_account_credentials: Some(scoped_credentials) }
    }
    pub async fn translate(&mut self, text: Vec<&str>, target: &str) -> Result<TranslateTextResponse> {
        // Build request URL and headers, obtain token if needed, then call the API.
        // ... (omitted for brevity)
    }
}
</code>

Data structures for the request and response are defined with Serde for JSON (e.g., TranslateTextRequest , TranslateTextResponse ).

Summary

The article shows how to perform Google OAuth 2.0 authentication in Rust using service account credentials, manage JWT creation, token caching, scopes, and subjects, and provides a concrete example of a translation service that automatically obtains access tokens and calls the Cloud Translation API.

BackendRustOAuth2cloud-translationgoogle-oauthservice-account
Architecture Development Notes
Written by

Architecture Development Notes

Focused on architecture design, technology trend analysis, and practical development experience sharing.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.