Code for the "Experimenting with ASP.NET Core Authentication Schemes" article by Scott Allen

September 25 · 7 mins read

I was trying to trace the code execution of eShopOnContainers at runtime, and I stumbled upon this:

public class AccountController : Controller
{
    ...
    [Authorize(AuthenticationSchemes = "OpenIdConnect")]
    public async Task<IActionResult> SignIn(string returnUrl)
    {
        ...
    }
    ...
}

I’m not used to seeing this AuthenticationSchemes parameter on the Authorize attribute, so I googled for an explanation of what it is. I found this tutorial by Scott Allen: “Experimenting with ASP.NET Core Authentication Schemes”. But it does not point to a code repository which I can download and execute and play with. So I created one.

You can download the code from this GitHub repository. Go to the folder named 2020-09-11-aspnet-authentication-schemes.

Experimentation

You first need to read Scott Allen’s article before proceeding with the experimentation part below. In case his site is down, I saved a PDF of that article which you can download here.

… finished reading that article?…

Now open the 2020-09-11-aspnet-authentication-schemes/AspNetAuthenticationSchemes.sln in Visual Studio 2019. You will see an ASP.NET Razor Pages web app.

Look for Page1Model. It contains this code:

[Authorize]
public class Page1Model : PageModel
{
    ...
}

It has Authorize without an AuthenticationSchemes parameter. That means that the app will use the default authentication scheme configured in our Startup class — the one named cookie1, in our case:

public class Startup
{
    ...
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "cookie1";
        })
        .AddCookie("cookie1", "cookie1", options =>
        {
            options.Cookie.Name = "cookie1";
            options.LoginPath = "/loginc1";
        })
        .AddCookie("cookie2", "cookie2", options =>
        {
            options.Cookie.Name = "cookie2";
            options.LoginPath = "/loginc2";
        });
    }
    ...
}

Now run the app then go to https://localhost:44325/Page1. It will give you this output:

User

Authentication Type: cookie1
name Alice-c1

That page shows that you are now authenticated as Alice-c1

How were you authenticated as Alice-c1 when you did not do anything yet?… That is because this ctx.SignInAsync(...) line in the case "/loginc1": part of the code in your Startup was automatically executed when you visited Page 1.

switch (ctx.Request.Path)
{
    case "/loginc1":
        var identity1 = new ClaimsIdentity("cookie111");
        identity1.AddClaim(new Claim("name", "Alice-c1"));
        await ctx.SignInAsync("cookie1", new ClaimsPrincipal(identity1));
        break;
    ...
}

How did Page 1 know that it should execute that code?… It’s because the default authentication scheme has the /loginc1 path in its configuration:

...
options.LoginPath = "/loginc1"
...

More experiments

Now, to continue with the experiments, try the combination of logging in and logging out using these URLs in your browser:

https://localhost:44325/loginc1
https://localhost:44325/loginc2

https://localhost:44325/logoutc1
https://localhost:44325/logoutc2

Then try to visit

https://localhost:44325/Page1

and

https://localhost:44325/Page2

and

https://localhost:44325/Page3

after each login and logout and see what these pages output on the screen.

302 Found redirect

Now, logout using https://localhost:44325/logoutc1.

Then stop the app.

Then comment out the ctx.SignInAsync(...) for loginc1 in your Startup class, like this:

case "/loginc1":
    var identity1 = new ClaimsIdentity("cookie1");
    identity1.AddClaim(new Claim("name", "Alice-c1"));
    //await ctx.SignInAsync("cookie1", new ClaimsPrincipal(identity1));
    break;
...

Now, when you try visit https://localhost:44325/Page1, you will get a 302 Found redirect response with Location: https://localhost:44325/loginc1?ReturnUrl=%2FPage1.

Interesting…

Lesson learned

The string "OpenIdConnect" in

[Authorize(AuthenticationSchemes = "OpenIdConnect")]

of eShopOnContainers’ WebMVC project is just the name of the authentication scheme to use, the same name used in configuring authentication in the Startup class:

services.AddAuthentication(...)
.AddOpenIdConnect("OpenIdConnect", options =>
{
    ...
});

AuthDump

If you navigate to https://localhost:44325/AuthDump, you will be given this output:

Auth Schemes

DisplayName Name Type
cookie1 cookie1 Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
cookie2 cookie2 Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
DefaultAuthenticate : cookie1
DefaultForbid: cookie1
DefaultSignIn: cookie1
DefaultSignOut: cookie1