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

Revalidating auth improvement. Fixes #12692 #12909

Merged
merged 11 commits into from
Aug 7, 2019

Conversation

SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Aug 6, 2019

This resolves the problem with identity template's authentication state provider. Previously it was getting the user info from HttpContext.User, and it was revalidating via SignInManager. Neither of these are actually safe to use because in Azure SignalR service, there's no HttpContext.

Since it turns out that we can't use SignInManager, we need to do as @rynowak suggested and use something "more granuar". The more granular thing is UserManager, since that provides access to the security stamp in the underlying store, without being coupled to HttpContext, at least as far as I can tell. UserManager does take an IServiceProvider, but doesn't refer to the concept of "HTTP" anywhere. A developer could configure their own UserManager that does use IHttpContextAccessor, but that would just fail to work on Azure SignalR (and since the error handling is so conservative, force the user to be logged out).

The drawback to using UserManager directly is that we have to duplicate some (~10 lines) of code from SignInManager that does the validation of security stamps. I felt that this pushed the complexity of the user code in the project template a bit too far, so to mitigate it, I've:

  • Factored out all the "revalidation loop" and "force logout" and "error handling" logic into a new public base class, RevalidatingServerAuthenticationStateProvider. Awfully long name, I know, but it's for clarity. This also means we can have a reasonable set of unit tests for that stuff. This base class remains decoupled from Identity, and would be a sensible starting point for any server-based mechanism for periodic revalidation of authentication state.
  • Rephrased SignInManager's very concise ValidateSecurityStampAsync logic into something functionally identical but more explicit and readable. At this point I'm now satisfied it's understandable enough that it's not dangerous to have it in user code.

To validate this actually behaves correctly on Azure SignalR, I'll need to wait to get an updated SDK build that includes the changes in this PR as well as some of the more recent API review PR changes, since the project template won't build otherwise. We have a lot of time before P9 lockdown so that should not be a blocker.

@SteveSandersonMS SteveSandersonMS added the area-blazor Includes: Blazor, Razor Components label Aug 6, 2019
@SteveSandersonMS SteveSandersonMS added this to the 3.0.0-preview9 milestone Aug 6, 2019
@SteveSandersonMS SteveSandersonMS added the tell-mode Indicates a PR which is being merged during tell-mode label Aug 6, 2019

while (!cancellationToken.IsCancellationRequested)
// Get the user manager from a new scope to ensure it fetches fresh data
using (var scope = _scopeFactory.CreateScope())
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be await using #12918

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure it will be that easy since IServiceScope doesn't derive from IAsyncDisposable (only the MSFT DI implementation does). You'll likely have to do some as casting 😕

Copy link
Member Author

Choose a reason for hiding this comment

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

That's quite a nasty mess given that this is going into user code in the template.

It feels like await using should be able to work with IDisposable too. If this can be implemented in user code without there being anything opinionated about it, the compiler should be able to codegen the same.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've handled it in what seems to be the most beginner-friendly manner, but it's still verbose and lame 😞


// Act
((IDisposable)provider).Dispose();
await Task.Delay(200);
Copy link
Member

Choose a reason for hiding this comment

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

Think about providing access to the actual validation task somehow so we can await it. This kind of thing ends up being flake eventually usually. Yeah I know that 200 > 50

Copy link
Member Author

Choose a reason for hiding this comment

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

That's what I have done in the cases where there is a validation task that can be awaited (it's NextValidateAuthenticationStateAsyncCall).

However for the tests where we're asserting that no further calls get made, such as this one, there's no validation task to await.

Copy link
Member Author

Choose a reason for hiding this comment

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

Without changing the public API in an unnatural test-centric manner (e.g., passing in some IDelayTaskProvider) I think this is the best we can do, so I propose leaving it. I haven't observed any hints of flakiness when running it locally so far.

If this does become a problem in the future, we do have the option to refactor without breaking any compatibility, e.g.:

  • Add a new internal property of type IDelayTaskProvider
  • If that is assigned, then use it instead of Task.Delay
  • Use it from tests

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

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

Looks great!

It's definitely a better compromise.

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/revalidating-auth-improvement branch from a05a141 to 29fc528 Compare August 7, 2019 11:11
@SteveSandersonMS SteveSandersonMS merged commit 92869c6 into release/3.0 Aug 7, 2019
@ghost ghost deleted the stevesa/revalidating-auth-improvement branch August 7, 2019 14:15
@SteveSandersonMS
Copy link
Member Author

Merged because I think the CR feedback is adequately addressed, but @rynowak please let me know if you disagree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components tell-mode Indicates a PR which is being merged during tell-mode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants