I am learning how security works on ASP.NET Core 2.0 and IdentityServer4. I set up the projects with IdentityServer, API and ASP.NET Core MVC Client App.
ConfigureService
method on Client App as in below. Here I am confusing on DefaultScheme
and DefaultChallengeScheme
. What is the point of configuring those? A detailed description on how it works would be really helpful if possible.
I already seen instead of DefaultScheme
, DefaultSignInScheme
also works, but how does it work? What is the difference of those?
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; //options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie("Cookies") .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.SignInScheme = "Cookies"; options.RequireHttpsMetadata = false; options.Authority = "http://localhost:5000/"; options.ClientId = "mvcclient"; options.SaveTokens = true; }); }
Authentication is the process of determining a user's identity. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware.
Authorization refers to the process that determines what a user is able to do. For example, an administrative user is allowed to create a document library, add documents, edit documents, and delete them. A non-administrative user working with the library is only authorized to read the documents.
An authentication scheme is a module that implements a way for a user to authenticate itself to SimpleID. In particular, an authentication scheme checks credentials presented by the user against some data store containing user information, and determines whether the credentials match those stored in the data store.
Example: In the ASP.NET core application, the “ASPNETCORE_ENVIRONMENT” variable and file configuration provider (appsettings.json file) is used by default. When checking the ASP.NET core project template, you should see that the “ASPNETCORE_ENVIRONMENT” variable with the value “Development” is set by default.
When checking the ASP.NET core project template, you should see that the “ASPNETCORE_ENVIRONMENT” variable with the value “Development” is set by default. Those are three values that are used by convention: Development, Staging, and Production.
DefaultChallengeScheme: Sets the default scheme to use when challenging. DefaultForbidScheme: Sets the default scheme to use when access is forbidden. DefaultSignOutScheme: Sets the default scheme to sign out. DefaultScheme: Sets the default fallback scheme (see below). You typically don’t configure all those properties.
Configuration in ASP.NET Core is performed using one or more configuration providers. Configuration providers read configuration data from key-value pairs using a variety of configuration sources: Settings files, such as appsettings.json Custom providers, installed or created This topic provides information on configuration in ASP.NET Core.
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).
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.
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:
DefaultAuthenticateScheme
: Sets the default scheme to use when authenticating.DefaultChallengeScheme
: Sets the default scheme to use when challenging.DefaultForbidScheme
: Sets the default scheme to use when access is forbidden.DefaultSignInScheme
: Sets the default scheme to sign in.DefaultSignOutScheme
: Sets the default scheme to sign out.DefaultScheme
: Sets the default fallback scheme (see below).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:
DefaultAuthenticateScheme
, or DefaultScheme
DefaultChallengeScheme
, or DefaultScheme
DefaultForbidScheme
, or DefaultChallengeScheme
, or DefaultScheme
DefaultSignInScheme
, or DefaultScheme
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With