Skip to content

Instantly share code, notes, and snippets.

@jsquire
Last active March 12, 2021 15:58
Show Gist options
  • Save jsquire/861fb365fa672dd62530500ad2bc0db9 to your computer and use it in GitHub Desktop.
Save jsquire/861fb365fa672dd62530500ad2bc0db9 to your computer and use it in GitHub Desktop.
Azure.Core: AzureNamedKeyCredential

Azure.Core: AzureNamedKeyCredential

When using a shared key for authorization, some services require that the name of the key be used as a component of the algorithm to form the authorization token. For these cases, AzureKeyCredential and AzureSasCredential aren't adequate as they track only a single value. A new credential type, AzureNamedKeyCredential has been approved to address this gap.

Things to know before reading

  • The names used in this document are intended for illustration only. Some names are not ideal and will need to be refined during discussions.

  • Some details not related to the high-level concept are not illustrated; the scope of this is limited to the high level shape and paradigms for the feature area.

  • Fake methods are used to illustrate "something needs to happen, but the details are unimportant." These methods will most often use ellipses for the parameter list, in order to help differentiate them.

Goals

  • Offer a token credential suitable for working with any service needing a named key that follows patterns established by the other credential types offered in Azure.Core and Azure.Identity.

  • Developers should be able to construct the credential by providing a string-based name and key.

  • Developers should be able to update the name and key after construction to support rolling credentials or using a new access policy. Updating credential information should be done in-place and not require creating a new credential instance.

  • Developers should be able to read the name of they key, in order to support caching within an application so that it can be easily identified when updates are needed.

  • The Azure SDKs should be able to read the name and key, ensuring they reflect the most current values.

  • Support updates and reads concurrently with consistent behavior. Developers should be able to update the credential at any time without concern for whether the SDK is reading from it.

Non-Goals

  • Providing a friendly and discoverable means for developers to read the key from the credential.

  • Allowing the name or key to be updated independently; both elements will be required when updating a credential.

Challenges

  • Unlike the other key and SAS-based credential types, the named key credential has multiple attributes; the name/key pairing must be treated as an atomic unit for reads and updates.

  • Unlike the TokenCredential and Azure.Identity types, the named key credential cannot implicitly renew the name and key when needed; the updates are the responsibility of the host application and must be performed manually.

  • Reading the name and key from the credential is likely to be performed as part of the hot path for service clients; this should be optimized for performance and efficiency.

Option: Use deconstruction to read the values

This approach uses deconstruction to provide an atomic means of reading multiple values at once, without introducing a tuple as part of a public API.

API Shape

public class AzureNamedKeyCredential
{
    public string Name { get; }
    public AzureNamedKeyCredential(string name, string key);
    public void Update (string name, string key);
    
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void Deconstruct(out string name, out string key);
}

Usage

// Use the credential to create an Azure SDK client type.

var credential = new AzureNamedKeyCredential("<< NAME >>", "<< KEY >>");
var client = new AzureFakeClient("<< URI >>", credential, new AzureFakeClientOptions());
// Updating the name and/or key can be done without interacting with the client; the
// client should respond to the change automatically.

credential.Update("<< NEW NAME >>", "<< NEW KEY >>");
// Read the name and key for generation of an auth token.

var (name, key) = credential;
var authToken = CreateAuthToken(name, key, ...);

// -- OR --

credential.Deconstruct(out var name, out var key);
var authToken = CreateAuthToken(name, key, ...);

Benefits:

  • No new type is needed to represent the name/key pair.
  • The intended API for developers is discoverable and differentiated from that intended for SDK use.
  • There is no emphasis on reading the key for developers using the credential.
  • Developers may read the name/key pair to aid in mocking/testing scenarios.

Concerns:

  • Deconstruction is still a newer pattern in C# and may not be obvious to Azure SDK authors.

Option: Introduce a new type to represent the values

This approach uses a new container type to represent the name/key pairing, allowing it to be represented clearly and treated as a single unit.

API Shape

public class AzureNamedKeyCredential
{
    public string Name { get; }
    public AzureNamedKeyCredential(string name, string key);
    public void Update (string name, string key);
    
    [EditorBrowsable(EditorBrowsableState.Never)]
    public AzureNamedKey GetNamedKey();
}

public class AzureNamedKey
{
    public string Name { get; }
    public string Key { get; }
    
    internal AzureNamedKey(string name, string key);
}

Usage

// Use the credential to create an Azure SDK client type.

var credential = new AzureNamedKeyCredential("<< NAME >>", "<< KEY >>");
var client = new AzureFakeClient("<< URI >>", credential, new AzureFakeClientOptions());
// Updating the name and/or key can be done without interacting with the client; the
// client should respond to the change automatically.

credential.Update("<< NEW NAME >>", "<< NEW KEY >>");
// Read the name and key for generation of an auth token.

var namedKey = credential.GetNamedKey();
var authToken = CreateAuthToken(namedKey.Name, namedKey.Key, ...);

Benefits:

  • The intended API for developers is discoverable and differentiated from that intended for SDK use.
  • There is no emphasis on reading the key for developers using the credential.
  • Developers may read the name/key pair to aid in mocking/testing scenarios.

Concerns:

  • This requires a new public type to be introduced but is not something developers are likely to use.
  • GetNamedKey may look more tempting to developers than Deconstruct which seems more like plumbing.

Option: Introduce an embedded type to represent the values

This approach uses an embedded container type to represent the name/key pairing, allowing it to be represented clearly and treated as a single unit.

API Shape

public class AzureNamedKeyCredential
{
    public string Name { get; }
    public AzureNamedKeyCredential(string name, string key);
    public void Update (string name, string key);
    
    [EditorBrowsable(EditorBrowsableState.Never)]
    public NamedKey GetNamedKey();
    
    public class NamedKey
    {
        public string Name { get; }
        public string Key { get; }
        
        internal NamedKey(string name, string key);
    }
}

Usage

// Use the credential to create an Azure SDK client type.

var credential = new AzureNamedKeyCredential("<< NAME >>", "<< KEY >>");
var client = new AzureFakeClient("<< URI >>", credential, new AzureFakeClientOptions());
// Updating the name and/or key can be done without interacting with the client; the
// client should respond to the change automatically.

credential.Update("<< NEW NAME >>", "<< NEW KEY >>");
// Read the name and key for generation of an auth token.

var namedKey = credential.GetNamedKey();
var authToken = CreateAuthToken(namedKey.Name, namedKey.Key, ...);

Benefits:

  • The intended API for developers is discoverable and differentiated from that intended for SDK use.
  • There is no top-level type pollution.
  • There is no emphasis on reading the key for developers using the credential.
  • Developers may read the name/key pair to aid in mocking/testing scenarios.

Concerns:

  • GetNamedKey may look more tempting to developers than Deconstruct which seems more like plumbing.
  • The embedded type is a bit more cumbersome to reference directly, outside the credential.

References and Related

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment