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

[Prototype] Revisit the events model #1243

Closed
wants to merge 25 commits into from
Closed

[Prototype] Revisit the events model #1243

wants to merge 25 commits into from

Conversation

kevinchalet
Copy link
Contributor

Things I've changed:

  • AuthenticateResult was split into 2 classes: AuthenticationResult (living in the abstractions repo) and RemoteAuthenticationResult (living here).
  • AuthenticateResult.Handle()/Skip() was moved to RemoteAuthenticationResult.
  • The base context classes are now generic and expose the options + the scheme.
  • The specialized OIDC/JWT/cookies base classes are gone thanks to the new generic classes.
  • HandleResponse()/SkipToNextMiddleware() are no longer available from events that don't offer real flow control (basically all the events that are not called from HandleRemoteAuthenticationAsync().
  • Events that used to expose HandleResponse()/SkipToNextMiddleware() for something else that real flow control now have CompleteAuthentication/SkipAuthentication/RejectAuthentication (or the equivalent for challenge/signout.

Please note that it's a very early prototype 😄

/cc @HaoK @Tratcher

@dnfclas
Copy link

dnfclas commented May 31, 2017

@PinpointTownes,
Thanks for having already signed the Contribution License Agreement. Your agreement was validated by .NET Foundation. We will now review your pull request.
Thanks,
.NET Foundation Pull Request Bot

@kevinchalet
Copy link
Contributor Author

@HaoK if you see things that should be changed/reverted, feel free to directly push commits to my forked branch (you have write access).

@HaoK
Copy link
Member

HaoK commented May 31, 2017

Cool I'll try to take a look at this sometime this week

@HaoK
Copy link
Member

HaoK commented Jun 1, 2017

@PinpointTownes looks good in general, one minor tweak suggestion, can you try dropping Base from everything other than BaseContext, since we no longer have the other context's in Http, these can just be the ChallengeContext/AuthenticationContext/RemoteAuthenticationContext, etc.

@kevinchalet
Copy link
Contributor Author

@HaoK thanks!

That's a good idea. TBH, I'm not super happy with the context classes names mainly because things like CookieRedirectContext or RedirectContext are used for both challenge and signout/signin so it's annoying to make them inherit from (Base)ChallengeContext from a semantic POV.

If you (or @Tratcher) have an idea, don't hesitate 😄

@HaoK
Copy link
Member

HaoK commented Jun 2, 2017

So BaseChallengeContext and BaseAuthenticationContext are siblings? What's the split of responsibility between these two?

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Jun 2, 2017

There are currently 6 base context classes in this PR:

  • BaseContext: it's the base class used by all the events. It only exposes the HTTP context, the scheme and the scheme options.

  • BaseControlContext: it inherits from BaseContext and exposes the HandleResponse()/SkipToNextMiddleware() methods needed to control the request flow. It's only used for events invoked via HandleRequestAsync() (including the events invoked from HandleRemoteAuthenticateAsync().

  • BaseAuthenticationContext: it inherits from BaseContext and is used for all the events invoked from HandleAuthenticateAsync (in the non-remote authentication middleware) that need to deal with an authentication ticket or just an AuthenticationProperties instance (for OIDC). It exposes the following methods: CompleteAuthentication(AuthenticationTicket ticket), SkipAuthentication() and RejectAuthentication(Exception failure).

  • BaseRemoteAuthenticationContext: it's BaseAuthenticationContext's sibling. The only difference is that it inherits from BaseControlContext instead of BaseContext, which allows controlling both the authentication flow and the request flow.

  • BaseChallengeContext: it inherits from BaseContext and only exposes SkipChallenge(). It's used by the challenge events to expose the AuthenticationProperties.

  • BaseSignOutContext is exactly like BaseChallengeContext but has a SkipSignOut() method.

We could probably merge BaseChallengeContext and BaseSignOutContext into a single class, but I'm not sure about the name. Any idea?

@HaoK
Copy link
Member

HaoK commented Jun 2, 2017

Maybe something like this?

BaseControlContext => HandleRequestContext?
BaseAuthenticationContext => ResultContext (its job is to eventually produce an AuthenticationResult right?)
BaseRemoteAuthenticationContext => HandleRequestResultContext
BaseChallenge/SignOutContext => AuthenticationContext(or HandlerContext)?

@kevinchalet
Copy link
Contributor Author

@Tratcher thoughts?

@Tratcher
Copy link
Member

Tratcher commented Jun 2, 2017

Needs a UML diagram...

I prefer @PinpointTownes's names over @HaoK's.

@kevinchalet
Copy link
Contributor Author

Needs a UML diagram...

https://blogs.msdn.microsoft.com/visualstudioalm/2016/10/14/uml-designers-have-been-removed-layer-designer-now-supports-live-architectural-analysis/

The VS 15.3 Community Preview I've installed doesn't seem to have the new tooling (likely an Enterprise feature) so if one of you could generate the diagram, that would be great 😅

@HaoK
Copy link
Member

HaoK commented Jun 2, 2017

Yeah I think the names are fine in general, just that the Base prefixs can be omitted without any loss of clarity

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Jun 2, 2017

There's still one problem: we need to find a name for the base class used by context classes like CookieRedirectContext or RedirectContext, that are used for multiple events of different nature (challenge, sign-in, sign-out).

What about merging BaseSignOutContext/BaseChallengeContext and naming the resulting class BasePropertiesContext? (not a huge fan of this name, but I can't think of a better one).

@HaoK
Copy link
Member

HaoK commented Jun 2, 2017

I believe we haven't used EventContext or HandlerContext if you need a generic-y name that covers multiple things

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Jun 3, 2017

I believe we haven't used EventContext or HandlerContext if you need a generic-y name that covers multiple things

If you like very generic things, I guess something like (Base)ProcessingContext or (Base)OperationContext with a StopProcessing()/StopOperation() method could work too?

@Tratcher when you have a sec', please let us know your preferences. Should the Base prefix be removed as @HaoK suggested? Would you be okay with EventContext/HandlerContext/(Base)ProcessingContext or (Base)OperationContext for the generic event exposing the authentication properties to signout/challenge/redirect events?

If you like the general idea, I'll update the PR, add the missing XML docs and remove the unnecessary logger extensions.

@HaoK
Copy link
Member

HaoK commented Jun 3, 2017

Out of those options that you listed, I'd vote for Event or Handler + Stop() without the extra word. We don't have base in any of our auth handler base classes, so its weird to have base in these. To be fully consistent we could rename BaseContext to something like SchemeContext

@kevinchalet
Copy link
Contributor Author

I'd vote for Event or Handler + Stop() without the extra word

The only concern I have with such a generic name is that it gives the impression it will be used as the super base class for every event, which is not true (it will only be used for events that need to expose the authentication properties and a way to stop the default logic processing).

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

Okay, so are these only used for IAuthenticationRequestHandlers events? If so HandleRequestEvent could signal that they should only be used in the context of a handle request

@kevinchalet
Copy link
Contributor Author

@HaoK nah, for flow control events, we have BaseRemoteAuthenticationContext, that inherits from BaseControlContext.

We only need a new name for BaseSignOutContext/BaseChallengeContext - that I'd like to merge into a single class. Note that these events don't offer flow control, because they are used in HandleForbiddenAsync()/HandleSignInAsync()/HandleChallengeAsync(), where it's not possible.

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

Ok so this is the NotRemoteAuthenticationContext basically :)

@kevinchalet
Copy link
Contributor Author

lol yeah, but we already have BaseAuthenticationContext for the events called from HandleAuthenticateAsync that allow overriding/providing the ticket or stopping the authentication process, so having another context class with Authentication may be confusing, no?

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

I think we've proven that this stuff is STILL confusing :)

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

This is kinda where I was going where I honestly prefer extension via derivation of virtuals... but I get this is much easier for people to plug in... but the naming on these context objects is a nightmare...

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

Maybe we just have duplication, and name the context to exactly match the virtual/event its used in...Its not the end of the world to have a SignIn + Challenge context, its clear what they are used for that way right?

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Jun 5, 2017

I think we've proven that this stuff is STILL confusing :)
This is kinda where I was going where I honestly prefer extension via derivation of virtuals... but I get this is much easier for people to plug in... but the naming on these context objects is a nightmare...

Yeah. But luckily, it's only a nightmare for us (at least if we want to avoid code duplicate by having base context classes). The final user experience is much better 😅

Its not the end of the world to have a SignIn + Challenge context, its clear what they are used for that way right?

That would be totally fine if we didn't have events classes like RedirectContext or CookieRedirectContext that are used as-is in many places, for both sign-in, sign-out and challenge. E.g:

public virtual Task RedirectToIdentityProvider(RedirectContext context) => OnRedirectToIdentityProvider(context);
public virtual Task RedirectToIdentityProviderForSignOut(RedirectContext context) => OnRedirectToIdentityProviderForSignOut(context);
public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirectToLogout(context);
public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirectToLogin(context);
public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirectToReturnUrl(context);
public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirectToAccessDenied(context);

Instead of trying to merge BaseSignOutContext/BaseChallengeContext, we could also split the problematic context classes into multiple, specialized classes. E.g:

RedirectContext -> RedirectToIdentityProviderContext (inherits from BaseChallengeContext) + RedirectToIdentityProviderForSignOutContext (inherits from BaseSignOutContext).

@HaoK
Copy link
Member

HaoK commented Jun 5, 2017

@PinpointTownes yeah this naming is basically for us, since end users are using the delegates they don't see the context names, so lets just be pragmatic, I think its okay if the cookie/redirect events share a common type, but we have special Challenge/SignOut contexts if we can't come up with a sensible name.

I'm guessing you really like the Base prefix since you keep including it in your examples :)

@HaoK
Copy link
Member

HaoK commented Jun 6, 2017

@PinpointTownes I had an idea you can try, since you made all of these generic anyways, instead of adding Scheme/Options, can you try what making it generic against THandler looks like instead? That is basically a superset of Scheme/Options. As part of that, you can make all the handler types public too, fixing that other issue... That also makes HandlerContext the correct name for whichever Context contains the THandler...

@kevinchalet
Copy link
Contributor Author

I'm guessing you really like the Base prefix since you keep including it in your examples :)

Haha no, it's simpler: I just copied/pasted the names from previous posts to avoid having to re-type them :trollface:

@PinpointTownes I had an idea you can try, since you made all of these generic anyways, instead of adding Scheme/Options, can you try what making it generic against THandler looks like instead?

I had to remove the .NET Core 2.0 bits temporarily 'cause my VS setup was broken but I'll give them another try this weekend.

That said, not sure I'm a huge fan of what you suggest, as it will basically require adding a new generic argument to (Base)Context for no apparent gain (but maybe I'm missing your point):

public abstract class BaseContext<THandler, TOptions>
    where THandler : AuthenticationHandler<TOptions>
    where TOptions : AuthenticationSchemeOptions
{
}

As part of that, you can make all the handler types public too, fixing that other issue...

Given that middleware used to be public (but are now gone), I suppose it's the right thing to do.

That said, it probably deserves a separate issue/PR so we have a chance to discuss the public API (I'm tempted to think most/all the helpers methods should be private and that folks shouldn't be encouraged to subclass the handlers, IMHO).

@HaoK
Copy link
Member

HaoK commented Jun 8, 2017

Yeah so I had something fairly drastic in mind

public class HandlerContext<THandler> : where THandler : IAuthenticationHandler

The benefit I saw was that we wouldn't need to add a TOptions because THandler would have the properly typed Options property already, and it would already have Scheme as well. The minor downside is that event handler would have to dig a bit deeper to get the Options, context.Handler.Options, but seems fine.

So with Auth 2.0, the handlers are top level concepts and should be made public, as derivation should really be a first class supported scenario. I agree with you that most of that should be left to a different PR (where we rationalize/clean up the properties/methods as appropriate). But if we go the HandlerContext route we should make the types public in the same PR.

{
Logger.RemoteSignOutHandledResponse();
return true;
}
if (remoteSignOutContext.Skipped)
if (remoteSignOutContext.State == EventResultState.SkipToNextMiddleware)
Copy link
Member

Choose a reason for hiding this comment

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

Nice, now can we rename SkipToNextMiddleware => Skipped, and HandleResponse => Handled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And have Skipped vs Bypassed? I'm generally all for using short names, but what you suggest is way too generic for such semantic.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think you've convinced us for the need for Bypass yet.

Copy link
Member

Choose a reason for hiding this comment

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

Introducing Bypass certainly isn't a blocker for all of your other changes as it doesn't exist today...

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't it seem better to fix the handlers themselves to make it easy to opt-out of any default behaviors, as opposed to having some framework support for that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh I think I see the problem, I missed the fact that we were now pushing these concepts onto all of our middleware, today only OIDC + JwtBearer expose Control contexts... yeah so we don't want to spread this out.

That's absolutely not the objective of this PR and I believe the current state reflects it (I will submit a separate issue/PR so we can discuss making the events consistent across the stack, but that's a totally different story).

This PR has only two goals: adding the options/scheme to the base context class and removing HandleResponse/SkipToNextMiddleware from every context where it doesn't make any sense to replace it by a BypassDefaultLogic concept that does exactly the same thing as in 1.0/1.1 but with a better name and a way better intellisense story.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

which is why it does require a lot of code

Except this scenario is quite frequent and yet is still extremely painful: ExternalLoginSignInAsync doesn't take a ClaimsPrincipal/claims collection so you pretty much have to rewrite everything it internally calls: SignInOrTwoFactorAsync, StoreTwoFactorInfo and SignInAsync. It's a total nightmare for such a simple thing.

Copy link
Member

Choose a reason for hiding this comment

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

So looking at the current names,

HandleRequestContext => BaseControlContext since this isn't actually something that all RemoteAuthenticationHandlers use.

RemoteAuthenticateResultContext => AuthenticateResultControlContext which is only used by OIDC/JwtBearer events.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RemoteAuthenticateResultContext => AuthenticateResultControlContext which is only used by OIDC/JwtBearer events.

RemoteAuthenticateResultContext is not used by the JWT handler. It's purely specific to handlers that implement IAuthenticationRequestHandler AND produce tickets, like the RAH-derived handlers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HaoK now that the objective is clear for you, may I suggest you make another full pass to see if you still have concerns?

@@ -11,13 +11,18 @@ public enum EventResultState
Continue,

/// <summary>
/// Bypass the default logic.
/// </summary>
BypassDefaultLogic,
Copy link
Member

Choose a reason for hiding this comment

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

I think maybe Noop is better than bypass. That's what you want right? For the event to be an explicit Noop

@HaoK
Copy link
Member

HaoK commented Jun 14, 2017

Moving this out of a line comment

This PR has only two goals: adding the options/scheme to the base context class and removing HandleResponse/SkipToNextMiddleware from every context where it doesn't make any sense to replace it by a BypassDefaultLogic concept that does exactly the same thing as in 1.0/1.1 but with a better name and a way better intellisense story.

I'll let @Tratcher chime in with his thoughts, but from my perspective, my main concern is that all this Handle/Skip/Bypass stuff stays limited in scope to only the OIDC/JwtBearer events that already have these concepts and doesn't bleed out.

}

public EventResultState State { get; protected set; }
Copy link
Member

Choose a reason for hiding this comment

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

Yeah so this is a no go. We aren't putting this control flow everywhere.

Copy link
Member

Choose a reason for hiding this comment

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

This is exactly the bleed I was talking about :)

Copy link
Member

Choose a reason for hiding this comment

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

This complexity is fine in the context of JwtBearer + OIDC since they use it already, none of the other auth middleware should be exposed to this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Bleed is really excessive, sir.

State was just moved from BaseControlContext (which is absolutely not OIDC-specific) to BaseContext and I added the BypassDefaultLogic member.

The default value is Continue - which is exactly what ALL the events do by default when you implement them. The property setter is protected and ONLY context classes that expose HandleResponse/SkipToNextMiddleware methods can change this state.

Copy link
Member

Choose a reason for hiding this comment

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

Well my prior comment about keeping this limited to OIDC/JwtBearer events was basically exactly this :) Keeping Control state concepts/properties away from the other events that don't have them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a huge fan of that as it ties the base BaseControlContext/HandleRequestContext class to the RAR model, which is not the only one using it (ASOS uses BaseControlContext for all its events and yet it's not a RAH handler).

What's wrong with the enum?

Copy link
Member

Choose a reason for hiding this comment

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

Rename/make RemoteAuthenticationResult => HandleRequestAuthenticationResult and be the only concept. Enums are annoying since if we do want to add something like a Bypass, we can't do it until a major version, unlike results where we can add a new property whenever we want and use it immediately. We want to only have a single way to do flow, not two (Result + enum)

The public API on the contexts are the same and there's no more enum, and internally we are consistent with only results.

Copy link
Member

@HaoK HaoK Jun 15, 2017

Choose a reason for hiding this comment

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

You'll still be able to have ASOS derive only from HandleRequestContext to only expose Handle/Skip

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Enums are annoying since if we do want to add something like a Bypass, we can't do it until a major version

What? Adding an enum member is not a breaking change (unless you change the associated value of the existing members, obviously).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rename/make RemoteAuthenticationResult => HandleRequestAuthenticationResult and be the only concept.

There are events that don't produce authentication tickets and for which HandleRequestAuthenticationResult.Success()/HandleRequestAuthenticationResult.None() or HandleRequestAuthenticationResult.Fail() make absolutely no sense (ASOS has many events like that).

EventResultState is way more neutral/generic.

@kevinchalet
Copy link
Contributor Author

I'll let @Tratcher chime in with his thoughts, but from my perspective, my main concern is that all this Handle/Skip/Bypass stuff stays limited in scope to only the OIDC/JwtBearer events that already have these concepts and doesn't bleed out.

HandleResponse/SkipToNextMiddleware are used by all the RAH-based handlers (OIDC and OAuth2 for some events). Even my OIDC server middleware (hem, handler 😄) uses it massively.

I dedicated a blog post about this pattern: http://kevinchalet.com/2016/07/13/creating-your-own-openid-connect-server-with-asos-creating-your-own-authorization-provider/

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Jun 15, 2017

The new semantic is sooooooooo much nicer and wayyyyy clearer:

JWT

Before:

services.AddJwtBearerAuthentication(options =>
{
    options.Events.OnMessageReceived = context =>
    {
        if (context.Token == "skip-authentication")
        {
            // Calling this method skips the default logic but doesn't invoke
            // the next middleware because the JWT handler doesn't handle requests.
            context.SkipToNextMiddleware();
        }
        else if (context.Token == "reject-authentication-with-error")
        {
            // Impossible with context.SkipToNextMiddleware() or context.HandleResponse().
        }
        else if (context.Token == "complete-authentication")
        {
            // Calling this method skips the default logic but can't prevent other stuff from being invoked.
            context.Ticket = new AuthenticationTicket(new ClaimsPrincipal(), null, context.Scheme.Name);
            context.HandleResponse();
        }

        return Task.CompletedTask;
    };
});

After:

services.AddJwtBearerAuthentication(options =>
{
    options.Events.OnMessageReceived = context =>
    {
        if (context.Token == "skip-authentication")
        {
            context.SkipAuthentication();
        }
        else if (context.Token == "reject-authentication-with-error")
        {
            context.RejectAuthentication("Error message");
            // Or...
            context.RejectAuthentication(new Exception("Error message"));
        }
        else if (context.Token == "complete-authentication")
        {
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(), null, context.Scheme.Name);
            context.CompleteAuthentication(ticket);
        }

        return Task.CompletedTask;
    };
});

OIDC

Before:

services.AddOpenIdConnectAuthentication(options =>
{
    options.Events.OnMessageReceived = context =>
    {
        if (context.Token == "skip-authentication")
        {
            // Calling this method skips the default logic but doesn't invoke the next
            // middleware because IsProcessingComplete() returns a None result in this case.
            context.SkipToNextMiddleware();
        }
        else if (context.Token == "reject-authentication-with-error")
        {
            // Impossible with context.SkipToNextMiddleware() or context.HandleResponse().
        }
        else if (context.Token == "complete-authentication")
        {
            // Calling this method skips the default logic but can't prevent other stuff from being invoked.
            context.Ticket = new AuthenticationTicket(new ClaimsPrincipal(), null, context.Scheme.Name);
            context.HandleResponse();
        }
        else if (context.Token == "skip-to-next-middleware")
        {
            // Impossible with context.SkipToNextMiddleware() (despite its name...).
        }
        else if (context.Token == "handle-response")
        {
            // You have to make sure context.Ticket is null. Otherwise, the response
            // is NOT handled (an AuthenticateResult.Success is returned!).
            context.Ticket = null;
            context.HandleResponse();
        }

        return Task.CompletedTask;
    };
});

After:

services.AddOpenIdConnectAuthentication(options =>
{
    options.Events.OnMessageReceived = context =>
    {
        if (context.Token == "skip-authentication")
        {
            context.SkipAuthentication();
        }
        else if (context.Token == "reject-authentication-with-error")
        {
            context.RejectAuthentication("Error message");
            context.RejectAuthentication(new Exception("Error message"));
        }
        else if (context.Token == "complete-authentication")
        {
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(), null, context.Scheme.Name);
            context.CompleteAuthentication(ticket);
        }
        else if (context.Token == "skip-to-next-middleware")
        {
            context.SkipToNextMiddleware();
        }
        else if (context.Token == "handle-response")
        {
            context.HandleResponse();
        }

        return Task.CompletedTask;
    };
});

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

Yeah its better, but the naming should be consistent with the RemoteAuthenticationResult, especially since you are introducing the class in this PR. If we want to change the names to be more clear, i.e. maybe Skip => SkipRequest, and Handle => HandleResponse, None => NoAuthentication, they should still match since they map 1:1

services.AddOpenIdConnectAuthentication(options =>
{
    options.Events.OnMessageReceived = context =>
    {
        if (context.Token == "no-authentication")
        {
            context.None();
        }
        else if (context.Token == "reject-authentication-with-error")
        {
            context.Fail("Error message");
            context.Fail(new Exception("Error message"));
        }
        else if (context.Token == "complete-authentication")
        {
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(), null, context.Scheme.Name);
            context.Success(ticket);
        }
        else if (context.Token == "skip-to-next-middleware")
        {
            context.Skip();
        }
        else if (context.Token == "handle-response")
        {
            context.Handle();
        }

        return Task.CompletedTask;
    };

@kevinchalet
Copy link
Contributor Author

Yeah its better, but the naming should be consistent with the RemoteAuthenticationResult, especially since you are introducing the class in this PR.

We don't have to, as they are independent (you can create a RAH without using any event and you can create a handler using the control events without using RAR). And honestly, RemoteAuthenticationResult is only used by RAH writers, not final users, so the naming is not that important (unlike the context APIs).

If you believe you can't live with that, I'd prefer keeping the current context APIs and updating AuthenticateResult/RemoteAuthenticationResult to use similar names.

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

so the naming is not that important (unlike the context APIs).

Naming is always important, we should have one good name for the context/result methods and they should be the same :) We should pick a name for each concept carefully now, since we will be living with it until 3.0...

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

they are independent (you can create a RAH without using any event and you can create a handler using the control events without using RAR

They won't be for long, since the enum is going away :)

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

For names, after seeing your code, I agree that we should distinguish the HandleRequest APIs vs the AuthenticateResult apis.

SkipRequest
HandleResponse

while the AuthResult ones should align with the AuthenticationResult names (which we can still rename as well)
Success
Fail
None

@kevinchalet
Copy link
Contributor Author

I've already dedicated much more time to this PR than I had originally planned and it's now clear that we're repeating the same things again and again and that finding a compromise that would make everyone happy is impossible at this stage.

If you think you can come up with a better design/better names, don't hesitate. For my part, I give up here :trollface:

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

@PinpointTownes Thanks for your work on this, I assume you are fine I start with your PR as a starting point? I think we'll end up with pretty much the same context API you wanted.

@kevinchalet
Copy link
Contributor Author

I assume you are fine I start with your PR as a starting point? I think we'll end up with pretty much the same context API you wanted.

I'm not sure I understand what's the point of doing that if you end up with the same/similar API shape, but why not, so... do what you want.

@kevinchalet
Copy link
Contributor Author

My 2-cent: since you're not familiar with the events model, consider taking a look at how people use it in real world apps. GitHub is full of such apps, so it shouldn't be too complicated.

After that, maybe you'll realize it's worth taking a few scenarios into account in your own design 😅

@HaoK
Copy link
Member

HaoK commented Jun 15, 2017

I'm almost done with the tweaks to your PR that I had in mind, its honestly not all that different for apps (ignoring the names part).

Again I was never pushing back against your usage/scenarios, just the naming and some implementation details, the spirit of what you wanted should really be the same after my tweaks, and I think prefer the implementation this way over the enum

@HaoK HaoK mentioned this pull request Jun 15, 2017
@kevinchalet kevinchalet deleted the handler branch July 5, 2017 13:57
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.

4 participants