-
Notifications
You must be signed in to change notification settings - Fork 190
Conversation
Fixes aspnet/Security#1244 and aspnet/Security#1264 Need to update Security/Hosts (to remove SignIn/Signout impls only) as a result of this PR as well |
I'm going to combine the events changes we want for aspnet/Security#1266 into this PR as well. So the update to AuthenticationResult + rename of None => Ignore will go in this PR as well |
@@ -93,7 +90,7 @@ public Task<AuthenticationScheme> GetDefaultForbidSchemeAsync() | |||
/// <summary> | |||
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>. | |||
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>. | |||
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. | |||
/// Otherwise, this will fallback to <see cref="GetDefaultAuthenticateSchemeAsync"/>. |
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.
Update comment to include the single scenario?
@@ -121,6 +118,10 @@ public Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() | |||
{ | |||
return GetSchemeAsync(_options.DefaultSignOutScheme); | |||
} | |||
if (_signOutHandlers.Count() == 1) |
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.
Add to doc comments?
if (_signOutHandlers.Count() == 1) | ||
{ | ||
return Task.FromResult(_signOutHandlers[0]); | ||
} | ||
return GetDefaultSignInSchemeAsync(); |
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's now no guarantee that a signin handler supports signout. Maybe we should change the hierarchy to ISignIn -> ISignOut -> IAuth
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 was just mirroring what we have in security today, where we have OIDC only implementing remote sign out...
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.
Maybe we just tweak this to also ensure that the sign in scheme handler implements sign out before returning it, we can have it continue to fallback to sign in
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 problem also exists for the fallback for SignIn => Authenticate, so we need to add a similar check for that too
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.
No, the SignIn interface derives from the Auth interface, but SignOut does not derive from SignIn.
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.
That's ok, I just mean:
var signIn = GetDefaultSignInSchemeAsync();
if (signIn != null && typeof(IAuthenticationSignOutHandler).IsAssignableFrom(signIn.HandlerType)) return signIn;
else return null;
if (typeof(IAuthenticationSignOutHandler).IsAssignableFrom(scheme.HandlerType)) | ||
{ | ||
_signOutHandlers.Add(scheme); | ||
} | ||
_map[scheme.Name] = scheme; |
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's no check that the HandlerType actually implements IAuthenticationHandler, which the doc comments say is required.
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.
That's done in the scheme itself,
HttpAbstractions/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationScheme.cs
Line 31 in 88a797c
if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType)) |
@@ -102,11 +99,11 @@ public Task<AuthenticationScheme> GetDefaultSignInSchemeAsync() | |||
{ | |||
return GetSchemeAsync(_options.DefaultSignInScheme); | |||
} | |||
if (_map.Count == 1) | |||
if (_signInHandlers.Count() == 1) |
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 using Linq?
@@ -121,6 +118,10 @@ public Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() | |||
{ | |||
return GetSchemeAsync(_options.DefaultSignOutScheme); | |||
} | |||
if (_signOutHandlers.Count() == 1) |
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 using Linq?
/// <summary> | ||
/// Used to determine if a handler supports SignOut. | ||
/// </summary> | ||
public interface IAuthenticationSignOutHandler : IAuthenticationHandler |
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 derivation?
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.
We require every handler to implement authenticate/challenge/forbid at a minimum, derivation makes this clearer, if we didn't require this, wouldn't that lead ppl to think they could only implement SignOut and use their handler?
/// <summary> | ||
/// Used to determine if a handler supports SignIn. | ||
/// </summary> | ||
public interface IAuthenticationSignInHandler : IAuthenticationHandler |
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 derivation?
So much sorcery in this PR... 😢 It would be much better to throw an |
Related post: aspnet/Security#1267 (comment). |
Updated the default sign/signout to enforce the handlers implement the interface, otherwise they return null (should this throw instead?) |
We don't throw if the default scheme doesn't exist today, so it feels weird to throw |
We'll also make SignIn derive from SignOut which dervies in Auth |
Updated, SignInHandler is now the old uber AuthenticationHandler that has all methods implemented. Renamed None() => NoResult() with the bool property Nothing => None (done for Security) |
{ | ||
if (_options.DefaultSignOutScheme != null) | ||
{ | ||
return GetSchemeAsync(_options.DefaultSignOutScheme); | ||
return EnsureAssignable<IAuthenticationSignOutHandler>(await GetSchemeAsync(_options.DefaultSignOutScheme)); |
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 type check is duplicated in SignOutAsync
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.
Oh right, ok I can back out the extra complexity in the scheme provider then, and leave the type interface enforcement to the service
/// Indicates that there was no information returned for this authentication scheme. | ||
/// </summary> | ||
/// <returns>The result.</returns> | ||
public static AuthenticateResult None() | ||
public static AuthenticateResult NoResult() |
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.
@Tratcher how about we keep this None, but call it NoResult() on the ResultContext's that will get exposed to events?
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.
That would leave you with a method and property that have the same name. NoResult is fine here.
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.
These are currently called Nothing and None(), I just don't see much benefit renaming None => NoResult as we do have usages in the code internally already today (i.e. Identity), we can keep NoResult on ResultContext so the public API for events is still the same.
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.
NoResult() and None are better than None() and Nothing.
@Tratcher