Mixing UI and API Endpoints in ASP.NET Core 2.1 (aka Dynamic Scheme Selection)

Some people like to co-locate UI and API endpoints in the same application. I generally prefer to keep them separate, but I acknowledge that certain architecture styles make this conscious decision.

Server-side UIs typically use cookies for authentication (or a combination of cookies and OpenID Connect) and APIs should use access tokens – and you want to make sure that you are not accepting cookies in the API by accident.

Since authentication of incoming calls in ASP.NET.Core are abstracted by so called authentication handlers, and you can register as many of them as you want – you can support both authentication scenarios. That’s by design.

One way you could implement that is explicitly decorating every controller with an [Authorize] attribute and specify the name of the authentication scheme you want use. That’s a bit tedious, and also error prone. I prefer to have a global authorization policy that denies anonymous access and then rather opt-out of that with [AllowAnonymous] where needed.

This did not work prior to ASP.NET Core 2.1 because global policies rely on the default scheme configuration – but since we have two different schemes, there is no default. The effect would be e.g. that you would get a redirect to a login page for an anonymous API call where you would have expected a 401.

In ASP.NET Core 2.1 there is a new feature where you can dynamically select the authentication scheme based on the incoming HTTP request. Let’s say e.g. all your API endpoint are below /api – you could define a rule that for requests to that path you would use JWT tokens – and for all other, OpenID Connect with cookies. You do that by adding a forward selector to the authentication handler like this:

options.ForwardDefaultSelector = ctx =>
{
    if (ctx.Request.Path.StartsWithSegments("/api"))
    {
        return "jwt";
    }
    else
    {
        return "cookies";
    }
};

For a full sample – see here.

This entry was posted in ASP.NET Core, OpenID Connect, Uncategorized, WebAPI. Bookmark the permalink.

14 Responses to Mixing UI and API Endpoints in ASP.NET Core 2.1 (aka Dynamic Scheme Selection)

  1. Robba says:

    I’m a bit confused, you’re saying “This did not work prior to ASP.NET Core 2.1 because global policies rely on the default scheme configuration – but since we have two different schemes, there is no default.”, but wasn’t it always a possibility to use this:

    services.AddMvc(options =>
    {
    var policy = new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes(“ApplicationCookie”, “JWT”)
    .RequireAuthenticatedUser()
    .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
    });

    To force an authenticated user using one of two schemes?

  2. Which can lead to CSRF problems. Hence my configuration.

  3. “Which can lead to CSRF problems. Hence my configuration.”

    Tokens can lead to XSS problems

    No silver bullets in security, are there? It is all a trade off, by picking between cookies and tokens you pretty much pick which attack vectors you will be mitigating against.

    • > Tokens can lead to XSS problems

      Not quite sure I would agree, but XSS is always a problem you have to protect against regardless of tokens or not.

      • This is only in the context of your earlier comment Dominick. I am not favoring one or the other, just pointing out that if you are going to say that allowing cookies exposes you to CRSF, dealing with tokens puts you in more of risk of XSS strictly from the stand-point of auth cookies being http-only (at least should be).

        There is way more to this discussion, is there? If we’re talking about SPAs, then you’re faced with the dilemma of doing implicit flow and the whole stigma associated with it (exposing access_tokens through the front-channel, less secure than server flow, refresh with an iframe, etc…). It is a nice model to code (as you show in IdSrv samples with oidc.js), but then how much is it used in real world apps is another question? Do you see a lot of implementations with an implicit flow given the risks? Or do they resort to server web app being protected by the cookie auth (and if that is the model, do they proxy the calls to the web api or do they allow cookie auth)?
        This I think is the model Robba was referring to in his comment and I do find that pretty common too.

  4. > but then how much is it used in real world apps is another question?

    I see both approaches. Older apps sometime use the proxy approach (because it is the easiest to retrofit). New “real” SPAs mostly use the “implicit flow + iframe” approach.

    • “New “real” SPAs mostly use the “implicit flow + iframe” approach.”

      Interesting…

      Reading a discussion like this one between Brock and members of the Auth-WG
      “[OAUTH-WG] is updated guidance needed for JS/SPA apps?”

      it doesn’t sound like anyone is willing to jump in and give implicit a stamp of approval. Definitely not appauth-js guys who flat out rejected it.
      I appreciate Brock’s effort to try to get some guidance and it is a very good discussion overall, but you also get a sense of how much of a struggle it is to get a clear answer as to whether implicit should be used in real world apps nowadays. I can easily draw a conclusion that not yet, at least until token binding becomes more of a standard.

  5. The implicit flow is not the problem. The problem is token storage in the browser.

    You could effectively mitigate XSS with CSP – but not enough developers use it (or even know about it).

    • Brock’s oauth-wg thread is probably a better place for this type of discussion.

      This by the way, IMO, is the main reason why we don’t have enough progress on the guidance – this discussion is so fragmented. It gets started and dies on its own over and over again (the oauth-wg threads from you and Brock are good examples). This for reasons I don’t understand is not high enough on oauth-wg radar despite the growing popularity of SPAs?

      By the way, last I checked, you still have this on the oauth2 page:
      “It is generally not recommended to use the implicit flow (and some servers prohibit this flow entirely). In the time since the spec was originally written, the industry best practice has changed to recommend that public clients should use either the authorization code flow without the client secret, or use the PKCE extension instead.”

      https://oauth.net/2/grant-types/implicit/

  6. > This is not a page maintained by us.

    Of course, did not mean to make it sound like that. More to the ongoing debate.

    Thanks for the discussion Dominick, I am hoping something gets done in the not too distant future in the area of guidance for spas, but I won’t hold my breath for it :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s