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}