-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[KeyVault] Challenge based authentication parallel fix, including tests #9059
Conversation
sdk/keyvault/keyvault-keys/src/core/challengeBasedAuthenticationPolicy.ts
Outdated
Show resolved
Hide resolved
/src/core/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, why are we ignoring these files anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the common folder PR that I'm proposing: #8866
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohh... can we rename the folder to 'generated' at some point to make this clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's an issue: #9069
import { Constants } from "@azure/core-http"; | ||
import { HttpOperationResponse } from "@azure/core-http"; | ||
import { HttpHeaders } from "@azure/core-http"; | ||
import { WebResource } from "@azure/core-http"; | ||
import { AccessTokenCache, ExpiringAccessTokenCache } from "@azure/core-http"; | ||
|
||
type ValidParsedWWWAuthenticateProperties = | ||
| "authorization" | ||
| "authorization_url" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure that's my mistake. I'll move to authorization_uri
. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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",
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate the nod, but might want to link to the main repo's source? 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, so you do compare the authority? hmm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok! Updated!
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small suggestion
/src/core/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(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_authenticate
header, 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