diff --git a/src/authenticator.rs b/src/authenticator.rs index cc9cc1c..ddd7fae 100644 --- a/src/authenticator.rs +++ b/src/authenticator.rs @@ -60,7 +60,9 @@ where where T: AsRef, { - self.find_token(scopes, /* force_refresh = */ false).await + self.find_token_info(scopes, /* force_refresh = */ false) + .await + .map(|info| info.into()) } /// Return a token for the provided scopes, but don't reuse cached tokens. Instead, @@ -72,15 +74,27 @@ where where T: AsRef, { - self.find_token(scopes, /* force_refresh = */ true).await + self.find_token_info(scopes, /* force_refresh = */ true) + .await + .map(|info| info.into()) + } + + /// Return the current ID token for the provided scopes, if any + pub async fn id_token<'a, T>(&'a self, scopes: &'a [T]) -> Result, Error> + where + T: AsRef, + { + self.find_token_info(scopes, /* force_refresh = */ false) + .await + .map(|info| info.id_token) } /// Return a cached token or fetch a new one from the server. - async fn find_token<'a, T>( + async fn find_token_info<'a, T>( &'a self, scopes: &'a [T], force_refresh: bool, - ) -> Result + ) -> Result where T: AsRef, { @@ -116,7 +130,7 @@ where .storage .set(hashed_scopes, token_info.clone()) .await?; - Ok(token_info.into()) + Ok(token_info) } _ => { // no token in the cache or the token returned can't be refreshed. @@ -129,7 +143,7 @@ where .storage .set(hashed_scopes, token_info.clone()) .await?; - Ok(token_info.into()) + Ok(token_info) } } } diff --git a/src/storage.rs b/src/storage.rs index 0dceb3b..4202fde 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -452,6 +452,7 @@ mod tests { access_token: access_token.to_owned(), refresh_token: None, expires_at: None, + id_token: None, }; let scope_set = ScopeSet::from(&["myscope"]); let tempdir = tempfile::tempdir().unwrap(); diff --git a/src/types.rs b/src/types.rs index 0da033f..c4fe272 100644 --- a/src/types.rs +++ b/src/types.rs @@ -63,6 +63,11 @@ pub struct TokenInfo { pub refresh_token: Option, /// The time when the token expires. pub expires_at: Option>, + /// Optionally included by the OAuth2 server and may contain information to verify the identity + /// used to obtain the access token. + /// Specifically Google API:s include this if the additional scopes "email" and/or "profile" + /// are used. In that case the content is an JWT token. + pub id_token: Option, } impl TokenInfo { @@ -73,6 +78,7 @@ impl TokenInfo { refresh_token: Option, token_type: String, expires_in: Option, + id_token: Option, } // Serialize first to a `serde_json::Value` then to `AuthErrorOr` to work around this bug in @@ -83,6 +89,7 @@ impl TokenInfo { refresh_token, token_type, expires_in, + id_token, } = >::deserialize(raw_token)?.into_result()?; if token_type.to_lowercase().as_str() != "bearer" { @@ -104,6 +111,7 @@ impl TokenInfo { access_token, refresh_token, expires_at, + id_token, }) }