Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Add defaults + split SignIn/SignOut #873

Closed
wants to merge 7 commits into from
Closed

Add defaults + split SignIn/SignOut #873

wants to merge 7 commits into from

Conversation

HaoK
Copy link
Member

@HaoK HaoK commented Jun 16, 2017

  • Move SignIn/Signout into IAuthenticationHandlerSign[In\Out]Handler
  • Add tests for AuthenticationService
  • add overloads for SignOut/Forbid to use new default

@Tratcher

@HaoK
Copy link
Member Author

HaoK commented Jun 16, 2017

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

@HaoK
Copy link
Member Author

HaoK commented Jun 16, 2017

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"/>.
Copy link
Member

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)
Copy link
Member

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();
Copy link
Member

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

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 was just mirroring what we have in security today, where we have OIDC only implementing remote sign out...

Copy link
Member Author

@HaoK HaoK Jun 17, 2017

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

Copy link
Member Author

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

Copy link
Member

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.

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 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;
Copy link
Member

@Tratcher Tratcher Jun 17, 2017

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.

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 done in the scheme itself,

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)

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)

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
Copy link
Member

Choose a reason for hiding this comment

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

Why the derivation?

Copy link
Member Author

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
Copy link
Member

Choose a reason for hiding this comment

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

Why the derivation?

@kevinchalet
Copy link
Contributor

So much sorcery in this PR... 😢

It would be much better to throw an InvalidOperationException when there's more than 1 scheme to encourage the developers to read the related documentation, so they can set the different schemes themselves.

@kevinchalet
Copy link
Contributor

Related post: aspnet/Security#1267 (comment).

@HaoK
Copy link
Member Author

HaoK commented Jun 22, 2017

Updated the default sign/signout to enforce the handlers implement the interface, otherwise they return null (should this throw instead?)

@HaoK
Copy link
Member Author

HaoK commented Jun 22, 2017

We don't throw if the default scheme doesn't exist today, so it feels weird to throw

@HaoK
Copy link
Member Author

HaoK commented Jun 22, 2017

We'll also make SignIn derive from SignOut which dervies in Auth

@HaoK
Copy link
Member Author

HaoK commented Jun 22, 2017

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));
Copy link
Member

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

Copy link
Member Author

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()
Copy link
Member Author

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?

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

@HaoK
Copy link
Member Author

HaoK commented Jun 30, 2017

271faf1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants