Skip to content

Commit

Permalink
rename AccessToken to Secret and expand it's usage (#1472)
Browse files Browse the repository at this point in the history
  • Loading branch information
demoray authored Nov 29, 2023
1 parent a32e411 commit e7c2845
Show file tree
Hide file tree
Showing 28 changed files with 188 additions and 127 deletions.
50 changes: 43 additions & 7 deletions sdk/core/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt::Debug};
use time::OffsetDateTime;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AccessToken(Cow<'static, str>);
#[derive(Clone, Deserialize, Serialize, Eq)]
pub struct Secret(Cow<'static, str>);

impl AccessToken {
impl Secret {
pub fn new<T>(access_token: T) -> Self
where
T: Into<Cow<'static, str>>,
Expand All @@ -20,26 +20,62 @@ impl AccessToken {
}
}

// NOTE: this is a constant time compare, however LLVM may (and probably will)
// optimize this in unexpected ways.
impl PartialEq for Secret {
fn eq(&self, other: &Self) -> bool {
let a = self.secret();
let b = other.secret();

if a.len() != b.len() {
return false;
}

a.bytes()
.zip(b.bytes())
.fold(0, |acc, (a, b)| acc | (a ^ b))
== 0
}
}

impl From<String> for Secret {
fn from(access_token: String) -> Self {
Self::new(access_token)
}
}

impl Debug for Secret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Secret").field(&"<REDACTED>").finish()
}
}

/// Represents an Azure service bearer access token with expiry information.
#[derive(Debug, Clone)]
pub struct TokenResponse {
/// Get the access token value.
pub token: AccessToken,
pub token: Secret,
/// Gets the time when the provided token expires.
pub expires_on: OffsetDateTime,
}

impl TokenResponse {
/// Create a new `TokenResponse`.
pub fn new(token: AccessToken, expires_on: OffsetDateTime) -> Self {
Self { token, expires_on }
pub fn new<T>(token: T, expires_on: OffsetDateTime) -> Self
where
T: Into<Secret>,
{
Self {
token: token.into(),
expires_on,
}
}
}

/// Represents a credential capable of providing an OAuth token.
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
pub trait TokenCredential: Send + Sync {
pub trait TokenCredential: Send + Sync + Debug {
/// Gets a `TokenResponse` for the specified resource
async fn get_token(&self, resource: &str) -> crate::Result<TokenResponse>;
}
16 changes: 9 additions & 7 deletions sdk/core/src/hmac.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::auth::Secret;
#[cfg(any(feature = "hmac_rust", feature = "hmac_openssl"))]
use crate::{
base64,
Expand All @@ -6,10 +7,10 @@ use crate::{

// if both hmac_rust and hmac_openssl are enabled, use hmac_openssl
#[cfg(all(feature = "hmac_rust", not(feature = "hmac_openssl")))]
pub fn hmac_sha256(data: &str, key: &str) -> crate::Result<String> {
pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result<String> {
use hmac::{Hmac, Mac};
use sha2::Sha256;
let key = base64::decode(key)?;
let key = base64::decode(key.secret())?;
let mut hmac = Hmac::<Sha256>::new_from_slice(&key)
.with_context(ErrorKind::DataConversion, || {
"failed to create hmac from key"
Expand All @@ -20,10 +21,10 @@ pub fn hmac_sha256(data: &str, key: &str) -> crate::Result<String> {
}

#[cfg(feature = "hmac_openssl")]
pub fn hmac_sha256(data: &str, key: &str) -> crate::Result<String> {
pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result<String> {
use openssl::{error::ErrorStack, hash::MessageDigest, pkey::PKey, sign::Signer};

let dkey = base64::decode(key)?;
let dkey = base64::decode(key.secret())?;
let signature = || -> Result<Vec<u8>, ErrorStack> {
let pkey = PKey::hmac(&dkey)?;
let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?;
Expand All @@ -37,19 +38,20 @@ pub fn hmac_sha256(data: &str, key: &str) -> crate::Result<String> {
}

#[cfg(not(any(feature = "hmac_rust", feature = "hmac_openssl")))]
pub fn hmac_sha256(_data: &str, _key: &str) -> crate::Result<String> {
pub fn hmac_sha256(_data: &str, _key: &Secret) -> crate::Result<String> {
unimplemented!("An HMAC signing request was called without an hmac implementation. Make sure to enable either the `hmac_rust` or `hmac_openssl` feature");
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_hmac_sign() {
let data = "create hmac signature for data";
let key = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
let key = Secret::new("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

let sig = super::hmac_sha256(data, key).unwrap();
let sig = hmac_sha256(data, &key).unwrap();

let expected_sig = "D/y9XyIEdUzEbdV570h8dou/mfkbMA1lKCOPqPDPAd0=";
assert_eq!(sig, expected_sig);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::PermissionToken;
use azure_core::auth::TokenCredential;
use azure_core::auth::{Secret, TokenCredential};
use std::fmt;
use std::sync::Arc;

Expand All @@ -9,7 +9,7 @@ use std::sync::Arc;
#[derive(Clone)]
pub enum AuthorizationToken {
/// Used for administrative resources: database accounts, databases, users, and permissions
PrimaryKey(String),
PrimaryKey(Secret),
/// Used for application resources: containers, documents, attachments, stored procedures, triggers, and UDFs
Resource(String),
/// AAD token credential
Expand All @@ -24,7 +24,7 @@ impl AuthorizationToken {
where
S: Into<String>,
{
Ok(AuthorizationToken::PrimaryKey(key.into()))
Ok(AuthorizationToken::PrimaryKey(Secret::new(key.into())))
}

/// Create a resource `AuthorizationToken` for the given resource.
Expand Down
6 changes: 3 additions & 3 deletions sdk/data_cosmos/src/resources/permission/permission_token.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::AuthorizationToken;
use azure_core::base64;
use azure_core::{auth::Secret, base64};

const PERMISSION_TYPE_PREFIX: &str = "type=";
const VERSION_PREFIX: &str = "ver=";
Expand Down Expand Up @@ -42,7 +42,7 @@ impl std::fmt::Display for PermissionToken {
use std::borrow::Cow;
let (permission_type, signature) = match &self.token {
AuthorizationToken::Resource(s) => ("resource", Cow::Borrowed(s)),
AuthorizationToken::PrimaryKey(s) => ("master", Cow::Owned(base64::encode(s))),
AuthorizationToken::PrimaryKey(s) => ("master", Cow::Owned(base64::encode(s.secret()))),
AuthorizationToken::TokenCredential(_) => {
panic!("TokenCredential not supported for PermissionToken")
}
Expand Down Expand Up @@ -86,7 +86,7 @@ impl std::convert::TryFrom<&str> for PermissionToken {
let permission_type = try_get_item(s, &parts, PERMISSION_TYPE_PREFIX)?;
let signature = try_get_item(s, &parts, SIGNATURE_PREFIX)?.to_owned();
let token = match permission_type {
"master" => AuthorizationToken::PrimaryKey(signature),
"master" => AuthorizationToken::PrimaryKey(Secret::new(signature)),
"resource" => AuthorizationToken::Resource(signature),
_ => {
return Err(PermissionTokenParseError::UnrecognizedPermissionType {
Expand Down
8 changes: 4 additions & 4 deletions sdk/identity/src/client_credentials_flow/login_response.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use azure_core::auth::AccessToken;
use azure_core::auth::Secret;
use serde::{Deserialize, Deserializer};
use time::OffsetDateTime;

Expand All @@ -21,7 +21,7 @@ pub struct LoginResponse {
pub expires_on: Option<OffsetDateTime>,
pub not_before: Option<OffsetDateTime>,
pub resource: Option<String>,
pub access_token: AccessToken,
pub access_token: Secret,
}

impl<'de> Deserialize<'de> for LoginResponse {
Expand All @@ -35,7 +35,7 @@ impl<'de> Deserialize<'de> for LoginResponse {
}

impl LoginResponse {
pub fn access_token(&self) -> &AccessToken {
pub fn access_token(&self) -> &Secret {
&self.access_token
}

Expand All @@ -56,7 +56,7 @@ impl LoginResponse {
expires_on,
not_before,
resource: r.resource,
access_token: AccessToken::new(r.access_token),
access_token: r.access_token.into(),
}
}
}
14 changes: 7 additions & 7 deletions sdk/identity/src/device_code_flow/device_code_responses.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use azure_core::auth::AccessToken;
use azure_core::auth::Secret;
use serde::Deserialize;
use std::fmt;

Expand Down Expand Up @@ -34,26 +34,26 @@ pub struct DeviceCodeAuthorization {
pub expires_in: u64,
/// Issued for the scopes that were requested.
/// Format: Opaque string
access_token: AccessToken,
access_token: Secret,
/// Issued if the original scope parameter included offline_access.
/// Format: JWT
refresh_token: Option<AccessToken>,
refresh_token: Option<Secret>,
/// Issued if the original scope parameter included the openid scope.
/// Format: Opaque string
id_token: Option<AccessToken>,
id_token: Option<Secret>,
}

impl DeviceCodeAuthorization {
/// Get the access token
pub fn access_token(&self) -> &AccessToken {
pub fn access_token(&self) -> &Secret {
&self.access_token
}
/// Get the refresh token
pub fn refresh_token(&self) -> Option<&AccessToken> {
pub fn refresh_token(&self) -> Option<&Secret> {
self.refresh_token.as_ref()
}
/// Get the id token
pub fn id_token(&self) -> Option<&AccessToken> {
pub fn id_token(&self) -> Option<&Secret> {
self.id_token.as_ref()
}
}
8 changes: 4 additions & 4 deletions sdk/identity/src/federated_credentials_flow/login_response.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use azure_core::auth::AccessToken;
use azure_core::auth::Secret;
use serde::{Deserialize, Deserializer};
use time::OffsetDateTime;

Expand All @@ -21,7 +21,7 @@ pub struct LoginResponse {
pub expires_on: Option<OffsetDateTime>,
pub not_before: Option<OffsetDateTime>,
pub resource: Option<String>,
pub access_token: AccessToken,
pub access_token: Secret,
}

impl<'de> Deserialize<'de> for LoginResponse {
Expand All @@ -35,7 +35,7 @@ impl<'de> Deserialize<'de> for LoginResponse {
}

impl LoginResponse {
pub fn access_token(&self) -> &AccessToken {
pub fn access_token(&self) -> &Secret {
&self.access_token
}

Expand All @@ -56,7 +56,7 @@ impl LoginResponse {
expires_on,
not_before,
resource: r.resource,
access_token: AccessToken::new(r.access_token),
access_token: r.access_token.into(),
}
}
}
14 changes: 7 additions & 7 deletions sdk/identity/src/refresh_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use azure_core::Method;
use azure_core::{
auth::AccessToken,
auth::Secret,
content_type,
error::{Error, ErrorKind, ResultExt},
headers, HttpClient, Request,
Expand All @@ -18,7 +18,7 @@ pub async fn exchange(
tenant_id: &str,
client_id: &str,
client_secret: Option<&str>,
refresh_token: &AccessToken,
refresh_token: &Secret,
) -> azure_core::Result<RefreshTokenResponse> {
let encoded = {
let mut encoded = &mut form_urlencoded::Serializer::new(String::new());
Expand Down Expand Up @@ -66,8 +66,8 @@ pub struct RefreshTokenResponse {
scopes: Vec<String>,
expires_in: u64,
ext_expires_in: u64,
access_token: AccessToken,
refresh_token: AccessToken,
access_token: Secret,
refresh_token: Secret,
}

impl RefreshTokenResponse {
Expand All @@ -84,11 +84,11 @@ impl RefreshTokenResponse {
self.expires_in
}
/// Issued for the scopes that were requested.
pub fn access_token(&self) -> &AccessToken {
pub fn access_token(&self) -> &Secret {
&self.access_token
}
/// The new refresh token and should replace old refresh token.
pub fn refresh_token(&self) -> &AccessToken {
pub fn refresh_token(&self) -> &Secret {
&self.refresh_token
}
/// Indicates the extended lifetime of an `access_token`.
Expand Down Expand Up @@ -147,7 +147,7 @@ mod tests {
"UNUSED",
"UNUSED",
None,
&AccessToken::new("UNUSED"),
&Secret::new("UNUSED"),
));
}
}
Loading

0 comments on commit e7c2845

Please sign in to comment.