First of all note that you are not using ASP.NET Core Identity there. Identity is the user management stack that builds on top of the authentication system. You appear to be using OpenID Connect with an IdentityServer as the provider, so your web application will only consume the OIDC information but not have to manage its own identities (it may be possible that the IdentityServer is using ASP.NET Core Identity though).
The way the authentication stack works in ASP.NET Core is that you can configure a set of authentication schemes. Some of these schemes are meant to be used in combination, for example the cookie authentication scheme is rarely used on its own, but there are also schemes that can be used completely separate (for example JWT Bearer authentication).
Authentication actions
In the authentication world, there are certain actions that you can perform:
Authenticate: To authenticate basically means to use the given information and attempt to authenticate the user with that information. So this will attempt to create a user identity and make it available for the framework.
For example, the cookie authentication scheme uses cookie data to restore the user identity. Or the JWT Bearer authentication scheme will use the token that is provided as part of the Authorization
header in the request to create the user identity.
Challenge: When an authentication scheme is challenged, the scheme should prompt the user to authenticate themselves. This could for example mean that the user gets redirected to a login form, or that there will be a redirect to an external authentication provider.
Forbid: When an authentication scheme is forbidden, the scheme basically just responds with something that tells the user that they may not do whatever they attempted to do. This is commonly a HTTP 403 error, and may be a redirect to some error page.
Sign-in: When an authentication scheme is being signed in, then the scheme is being told to take an existing user (a ClaimsPrincipal
) and to persist that in some way. For example, signing a user in on the cookie authentication scheme will basically create a cookie containing that user’s identity.
Sign-out: This is the inverse of sign-in and will basically tell the authentication scheme to remove that persistance. Signing out on the cookie scheme will effectively expire the cookie.
Note that not all authentication schemes can perform all options. Sign-in and sign-out are typically special actions. The cookie authentication scheme is an example that supports signing in and out, but the OIDC scheme for example cannot do that but will rely on a different scheme to sign-in and persist the identity. That’s why you will usually see the cookie scheme with it as well.
Typical authentication flow
Authentication schemes can be used explicitly. When you use one of the authentication extension methods on the HttpContext
, for example httpContext.AuthenticateAsync()
, then you can always explicitly specify what authentication scheme you want to use for this operation.
So if you, for example, want to sign in with the cookie authentication scheme "Cookie"
, you could simply call it like this from your code:
var user = new ClaimsPrincipal(…);
await httpContext.SignInAsync(user, "Cookie");
But in practice, calling the authentication directly and explicitly like that is not the most common thing to do. Instead, you will typically rely on the framework to do authentication for you. And for that, the framework needs to know which authentication scheme to use for what operation.
That is what the AuthenticationOptions
are for. You can configure those options so that you can explicitly define what authentication scheme to use as the default for each of those authentication actions:
You typically don’t configure all those properties. Instead, the framework has some default fallbacks, so you can configure just a subset of those properties. The logic is like this:
- Authenticate:
DefaultAuthenticateScheme
, or DefaultScheme
- Challenge:
DefaultChallengeScheme
, or DefaultScheme
- Forbid:
DefaultForbidScheme
, or DefaultChallengeScheme
, or DefaultScheme
- Sign-in:
DefaultSignInScheme
, or DefaultScheme
- Sign-out:
DefaultSignOutScheme
, or DefaultScheme
As you can see, each of the authentication actions falls back to DefaultScheme
if the specific action’s default isn’t configured. So what you will typically see is the DefaultScheme
being configured, and then the specific actions are configured for those where a different scheme is required.
Your example shows this pretty well: With OIDC, you will need a sign-in scheme that can persist the identity that is provided by the external authentication provider. So you will usually see the OIDC and cookie authentication schemes:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
For the user, the normal interaction is through the cookie authentication scheme: When they access the web application, the cookie authentication scheme will attempt to authenticate them using their cookie. So using the cookie authentication scheme as the default scheme for all operations.
The exception is when challenging the authentication: In that case, we want the user to be redirected to the OIDC provider, so they can log in there and return with an identity. So we set the default challenge scheme to the OIDC scheme.
In addition, we also link the OIDC scheme with the cookie scheme. When the user gets challenged and logs in with their external authentication provider, they will get sent back to the web application with their external identity. The OIDC scheme cannot persist that identity though, so it signs in using a different scheme—the cookie scheme—which will then persist the identity on behalf of the OIDC scheme. So the cookie scheme will create a cookie for the OIDC identity, and on the next request, the cookie scheme (which is the default scheme) will be able to authenticate the user again using that cookie.
So most of the time, you will be fine with just specifying the default scheme and then depending on your authentication setup maybe change one or two explicit actions. But theoretically, you can totally set up a very complex setup of different defaults and multiple schemes: The framework gives you a lot of flexibility here.