Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename TokenResponse to AccessToken #1476

Merged
merged 9 commits into from
Nov 29, 2023
60 changes: 48 additions & 12 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 {
pub struct AccessToken {
/// 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 }
impl AccessToken {
/// Create a new `AccessToken`.
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 {
/// Gets a `TokenResponse` for the specified resource
async fn get_token(&self, resource: &str) -> crate::Result<TokenResponse>;
pub trait TokenCredential: Send + Sync + Debug {
/// Gets a `AccessToken` for the specified resource
async fn get_token(&self, resource: &str) -> crate::Result<AccessToken>;
}
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