[KeyVault] Challenge based authentication parallel fix, including tests#9059
[KeyVault] Challenge based authentication parallel fix, including tests#9059sadasant merged 16 commits intoAzure:masterfrom
Conversation
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
| @@ -1 +1,2 @@ | |||
| src/core No newline at end of file | |||
| /src/core/* | |||
There was a problem hiding this comment.
Why the root slash? Does that still always resolve to the root? The old line didn't have it.
There was a problem hiding this comment.
Without the root hash, I can't re-include the challengeBasedAuth file that is in the code folder :/ I will clean this up once we move this to a common folder.
There was a problem hiding this comment.
huh, why are we ignoring these files anyway?
There was a problem hiding this comment.
Here's the common folder PR that I'm proposing: #8866
There was a problem hiding this comment.
@xirzec the files in the src/core folder should only come from the swagger tool we use, which makes it so that any changes we make are futile :/ ... except for the challenge based authentication code! which we should move out of there.
There was a problem hiding this comment.
ohh... can we rename the folder to 'generated' at some point to make this clear?
|
|
||
| type ValidParsedWWWAuthenticateProperties = | ||
| | "authorization" | ||
| | "authorization_url" |
There was a problem hiding this comment.
In .NET, we have this as "authorization_uri". Are you sure it's "_url"? Adding @schaabs who wrote that originally.
There was a problem hiding this comment.
I'm sure that's my mistake. I'll move to authorization_uri. Thank you!
There was a problem hiding this comment.
If you're sure. I'm not so sure myself, which is why I included Scott. I'm not sure how to verify it as well. :)
There was a problem hiding this comment.
The authorization_uri was most likely copied from the track 1 code. If we're not sure probably best to default to what was in .NET track 1 since that SDK predates the rest. Either way this might just be a moot point left over from an older API version we're not supporting. Looking at the test recordings 7.0 seems to pass back "autorization" consistently in the challenge
"WWW-Authenticate": "Bearer authorization=\u0022https:\u002f\u002flogin.windows.net\*****, resource=\u0022https:\u002f\u002fvault.azure.net\u0022",
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
| // or if the cached challenge has a different scope, | ||
| // we store the just received challenge and reset the cached token, to force a re-authentication. | ||
| // This is exactly what C# is doing, as we can see here: | ||
| // https://github.com/heaths/azure-sdk-for-net/blob/a35434c1ce955d4cdeb393748f9f6407d4a984e2/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs#L233 |
There was a problem hiding this comment.
I appreciate the nod, but might want to link to the main repo's source? 😉
There was a problem hiding this comment.
I mean, I would get lost in the main repo's source! I want to link to what's similar in that code. Let me know if you know a better link.
There was a problem hiding this comment.
There was a problem hiding this comment.
Wait, so you do compare the authority? hmm
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
sdk/keyvault/keyvault-keys/test/challengeBasedAuthenticationPolicy.test.ts
Outdated
Show resolved
Hide resolved
| @@ -1 +1,2 @@ | |||
| src/core No newline at end of file | |||
| /src/core/* | |||
There was a problem hiding this comment.
huh, why are we ignoring these files anyway?
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Show resolved
Hide resolved
I would recommend changing secrets and certificates in the same PR for the master branch. For hotfix, just like last time, we can use 1 PR for keys & secrets, another for certificates. |
|
As a note on the wording, I'm using "parallel" since these promises are being executed at the same time, at least from the perspective of the developer. I'm using this as reference: https://takuti.me/note/parallel-vs-concurrent/ Let me know if I should change it. |
| @@ -0,0 +1,2 @@ | |||
| # Ignoring the core files since they're auto-generated. Eventually, the auto-generated code will be on par with our current eslint rules, but in the mean time we should ignore them. | |||
| src/core | |||
There was a problem hiding this comment.
This file was missing in certificates.
|
@jonathandturner, @xirzec, Can you take another look now that the PR has been update to work with secrets and certificates as well? @sadasant, Please update the PR title and have the hotfix PRs created as well |
xirzec
left a comment
There was a problem hiding this comment.
I think this will work as is, but left a couple suggestions for further polish.
sdk/keyvault/keyvault-certificates/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
| // - An authorization URI with a token, | ||
| // - The resource to which that token is valid against (also called the scope). | ||
| const parsedWWWAuth = this.parseWWWAuthenticate(wwwAuthenticate); | ||
| const authorization = parsedWWWAuth.authorization!; |
There was a problem hiding this comment.
feels weird to do all these ! operations to assert something exists. If we know it exists, why make it optional on the type? If we don't know that it exists, why not add some checks so we don't blow up at runtime?
There was a problem hiding this comment.
I'll add an exception case if we don't receive the values that we expect! Thank you!
There was a problem hiding this comment.
I added this:
if (!(authorization && resource)) {
return this._nextPolicy.sendRequest(webResource);
}
sdk/keyvault/keyvault-certificates/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
| } | ||
| return response; | ||
| } else { | ||
| // If we don't receive a response with a 401 status code, |
There was a problem hiding this comment.
can everything from here until the end be moved up after the line webResource.body = originalBody inside the body of the if? Since we didn't blank the body in the else, we know it will never respond with 401 and we can just return the response.
There was a problem hiding this comment.
Do we know it will never return 401? Seems like we are being extra cautious if we double check like this.
There was a problem hiding this comment.
Let's say we're already authenticated, but the server answers with 401 when our code tries to:
await this.loadToken(webResource);
response = await this._nextPolicy.sendRequest(webResource);Seems possible! I'm assuming that if the challenge or token have some kind of expiration, this might help us correctly re-authenticate.
There was a problem hiding this comment.
(I'm seeing an A-zure pun and I don't know what to do with it 😄)
- Adds changes missing from #9059
- Adds changes missing from #9059
Fixes #9005
The challenge based authentication hotfix introduces a bug in which: Either when the client is not authenticated, or when the token is invalid, parallel network requests end up in a race condition where while one request is authenticating, the other ones fail immediately. This is not a problem before the hotfix because the challenge based authentication was always re-authenticating before making a request.
The parallel problem in more detail is as follows:
A new token was generated only if
response.status == 401, and we received awww_authenticateheader, which is ok, but parallel requests would choke at the!challenge.equalTo(this.challengeCache.challenge)mark, since by the time the second request compared the challenges, they would be the same, but the "response" set at the end was the 401 one, and not a new request made with the valid challenge. The fix consists of re-loading the credentials, and re-sending the request if the challenge happens to be valid at that point.TODOs:
Do the same for Secrets and Certificates (should I do this in separate PRs?).Keep in mind that this PR is not about refactoring the entirety of the challenge based authentication, which I'll be doing separately, and I'm tracking it with this issue: #9058Update: Decided to refactor the challenge auth here, to make it easier to test it. Therefore:
Fixes #9058