Skip to content

Conversation

@chradek
Copy link
Contributor

@chradek chradek commented Mar 18, 2021

Resolves #14303

Summary

This PR adds the AzureNamedKeyCredential class and NamedKeyCredential interface to the @azure/core-auth pacakge.

Details are listed in the linked issue, but the TL;DR is:
When using a shared key for authorization, some services require the name of the key be used as part of the algorithm to form the authorization token.

Since AzureKeyCredential and AzureSasCredential only track a single value, we've added AzureNamedKeyCredential that lets us track 2 values! (Let's hope there's never 3 values needed!)

This credential will be used by the event hubs and service bus packages to start.

@ghost ghost added the Azure.Core label Mar 18, 2021
Copy link
Member

@xirzec xirzec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple thoughts

/**
* The value of the key to be used in authentication.
*/
public get key(): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any particular reason these are getters instead of normal props?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were wanting the user to have to call update() to update the key, and otherwise treat these as readonly properties. This is the pattern the other credentials in this package (e.g. AzureKeyCredential) follows as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense

* @param newName - The new name value to be used.
* @param newKey - The new key value to be used.
*/
public update(newName: string, newKey: string): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if I just want to update one? Leaving the other empty seems like a not unreasonable contract compared to having to set it to its existing value

Copy link
Contributor Author

@chradek chradek Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case I went just based on the requirements for the linked issue:

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

I think it may make the API a bit confusing if we allow setting them separately.

I'd expect the key to change more frequently than the name (e.g. you've rotated your credentials for a given name.) Only providing the key to update would look like cred.update(undefined, newKey) then, instead of cred.update(cred.name, newKey). I can't think of a scenario where you'd change the name, but not the key.

I could obviously flip the position of name/key...but this would be opposite of what the other languages are doing, and mentally I think of the key being associated to the policy name, not the other way around.

I could expose updateKey and updateName methods, in addition to update, but that's 3 times the surface area now!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just a little confused, since I would expect updating the key to be way more common than the name, but perhaps I'm not understanding the name field's purpose very well.

I agree that it's better to not ship too much surface area at once, but are other languages also fine with this kinda clunky contract?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsquire
Pinging you since you created the issue 😄 What was the reason you didn't want to support updating name/key separately? Was it just to be concurrency safe?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the reason you didn't want to support updating name/key separately? Was it just to be concurrency safe?

Yes; the update/read is intended to be atomic to avoid race conditions with concurrent use. For those languages with threads, allowing them to be set individually offers the chance for inconsistent data.

but are other languages also fine with this kinda clunky contract?

This was the design that the architects approved, with the trade-offs made to favor safe concurrent use. The goal was to ensure that we didn't provide overloads that callers may be tempted to use in a manner that could produce undesired results. You may want to have a conversation with Brian since threading isn't a consideration here.

perhaps I'm not understanding the name field's purpose very well.

The credential is intended for use when a service requires both the key name and key value to generate the value needed for authorization. The name isn't meant to be informational, but part of the auth flow. Not sure if that helps at all... 😄

what if I just want to update one? Leaving the other empty seems like a not unreasonable contract compared to having to set it to its existing value

The intention is that updates will always provide both values; essentially, you're fully reinitializing the state. If the name is not provided, I would expect that the current value be updated to undefined|null|whatever.

I'm just a little confused, since I would expect updating the key to be way more common than the name

I would agree. However, it is a valid use case to generate a new access policy and need to replace the existing one, which requires the name and key.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm once again relieved to not have to think about threading concerns in JS. :)

Copy link
Member

@xirzec xirzec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, seems like we're stuck in this place for consistency. At least the interface is clean.

* @param newName - The new name value to be used.
* @param newKey - The new key value to be used.
*/
public update(newName: string, newKey: string): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm once again relieved to not have to think about threading concerns in JS. :)

@chradek chradek merged commit 3cc2908 into Azure:master Mar 19, 2021
@chradek chradek deleted the azure-namded-key-credential branch March 19, 2021 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Azure.Core: AzureNamedKeyCredential

3 participants