google_cloud_auth/credentials/
subject_token.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Subject Token Credential Type.
16//!
17//! A **subject token** is a credential that asserts the identity of a workload,
18//! application, or a user. In the case of the [Workload Identity Federation] flow,
19//! this allows applications to authenticate to Google Cloud, instead of using
20//! long-lived service account keys. The process involves exchanging this subject
21//! token for a short-lived Google Cloud access token via the
22//! [Security Token Service (STS)].
23//!
24//! This module provides the [`SubjectTokenProvider`] trait, which is used to
25//! fetch subject tokens. The Google Cloud client libraries for Rust will typically
26//! use the [`SubjectTokenProvider`] automatically for external account credentials.
27//! You might need to implement this trait for advanced authentication scenarios where
28//! you want to integrate a custom subject token fetching mechanism.
29//!
30//! # Example
31//!
32//! ```
33//! # use std::error::Error;
34//! # use std::fmt;
35//! # use std::future::Future;
36//! # use google_cloud_auth::credentials::subject_token::{
37//! #     Builder as SubjectTokenBuilder, SubjectToken, SubjectTokenProvider,
38//! # };
39//! # use google_cloud_auth::errors::SubjectTokenProviderError;
40//! #[derive(Debug)]
41//! struct CustomProviderError {
42//!     message: String,
43//!     is_transient: bool,
44//! }
45//!
46//! impl fmt::Display for CustomProviderError {
47//!     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48//!         write!(f, "CustomProviderError: {}", self.message)
49//!     }
50//! }
51//!
52//! impl Error for CustomProviderError {}
53//!
54//! impl SubjectTokenProviderError for CustomProviderError {
55//!     fn is_transient(&self) -> bool {
56//!         self.is_transient
57//!     }
58//! }
59//!
60//! #[derive(Debug)]
61//! struct MyCustomProvider {
62//!     api_key: String,
63//! }
64//!
65//! impl SubjectTokenProvider for MyCustomProvider {
66//!     type Error = CustomProviderError;
67//!
68//!     async fn subject_token(&self) -> Result<SubjectToken, Self::Error> {
69//!             let token_from_idp = "a-very-secret-token-from-your-idp";
70//!             Ok(SubjectTokenBuilder::new(token_from_idp.to_string()).build())
71//!     }
72//! }
73//! ```
74//!
75//! [Workload Identity Federation]: https://cloud.google.com/iam/docs/workload-identity-federation
76//! [Security Token Service (STS)]: https://cloud.google.com/iam/docs/reference/sts/rest
77use crate::credentials::errors::SubjectTokenProviderError;
78
79/// A builder for [`SubjectToken`] instances.
80///
81/// # Example
82/// ```
83/// # use google_cloud_auth::credentials::subject_token::Builder;
84/// let subject_token = Builder::new("test-token")
85///     .build();
86///
87pub struct Builder {
88    token: String,
89}
90
91impl Builder {
92    /// Creates a new builder using the string token.
93    pub fn new<S: Into<String>>(token: S) -> Self {
94        Self {
95            token: token.into(),
96        }
97    }
98
99    /// Returns a [`SubjectToken`] instance.
100    pub fn build(self) -> SubjectToken {
101        SubjectToken { token: self.token }
102    }
103}
104
105/// Represents a third-party subject token used for authentication.
106///
107/// A `SubjectToken` should be constructed using its corresponding [`Builder`].
108///
109/// # Example
110///
111/// ```
112/// # use google_cloud_auth::credentials::subject_token::Builder;
113/// let token_value = "my-secret-token".to_string();
114/// let subject_token = Builder::new(token_value).build();
115///
116/// ```
117#[derive(Debug)]
118pub struct SubjectToken {
119    pub(crate) token: String,
120}
121
122/// Trait for providing a third-party subject token.
123///
124/// The Google Cloud client libraries for Rust will automatically implement this
125/// trait for external account credentials. You might need to implement this trait
126/// for advanced authentication scenarios where you want to integrate a custom
127/// subject token fetching mechanism.
128///
129/// # Example
130///
131/// ```
132/// # use std::error::Error;
133/// # use std::fmt;
134/// # use std::future::Future;
135/// # use google_cloud_auth::credentials::subject_token::{
136/// #     Builder as SubjectTokenBuilder, SubjectToken, SubjectTokenProvider,
137/// # };
138/// # use google_cloud_auth::errors::SubjectTokenProviderError;
139/// #[derive(Debug)]
140/// struct CustomProviderError {
141///     message: String,
142///     is_transient: bool,
143/// }
144///
145/// impl fmt::Display for CustomProviderError {
146///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147///         write!(f, "CustomProviderError: {}", self.message)
148///     }
149/// }
150///
151/// impl Error for CustomProviderError {}
152///
153/// impl SubjectTokenProviderError for CustomProviderError {
154///     fn is_transient(&self) -> bool {
155///         self.is_transient
156///     }
157/// }
158///
159/// #[derive(Debug)]
160/// struct MyCustomProvider {
161///     api_key: String,
162/// }
163///
164/// impl SubjectTokenProvider for MyCustomProvider {
165///     type Error = CustomProviderError;
166///
167///     async fn subject_token(&self) -> Result<SubjectToken, Self::Error> {
168///             let token_from_idp = "a-very-secret-token-from-your-idp";
169///             Ok(SubjectTokenBuilder::new(token_from_idp.to_string()).build())
170///     }
171/// }
172/// ```
173pub trait SubjectTokenProvider: std::fmt::Debug + Send + Sync {
174    /// The error type that can be returned by this provider.
175    ///
176    /// The error must implement the [`SubjectTokenProviderError`] trait to allow the
177    /// authentication client to know whether the error is transient and can be retried.
178    type Error: SubjectTokenProviderError;
179    /// Asynchronously fetches the third-party subject token.
180    fn subject_token(&self) -> impl Future<Output = Result<SubjectToken, Self::Error>> + Send;
181}
182
183pub(crate) mod dynamic {
184    use super::{SubjectToken, SubjectTokenProviderError};
185    use crate::errors::CredentialsError;
186
187    #[async_trait::async_trait]
188    pub trait SubjectTokenProvider: std::fmt::Debug + Send + Sync {
189        /// Asynchronously fetches the third-party subject token.
190        async fn subject_token(&self) -> Result<SubjectToken, CredentialsError>;
191    }
192
193    #[async_trait::async_trait]
194    impl<T> SubjectTokenProvider for T
195    where
196        T: super::SubjectTokenProvider,
197        T::Error: Send + Sync + 'static,
198    {
199        async fn subject_token(&self) -> Result<SubjectToken, CredentialsError> {
200            let result = self.subject_token().await;
201            result.map_err(|e| CredentialsError::from_source(e.is_transient(), e))
202        }
203    }
204}