ASP.NET Core Identity - ConfigureServices Essentials
Tags: c, dotnet, identity, aspnet
Published: 2020-01-16 16:33:16

---
title: ASP.NET Core Identity - ConfigureServices Essentials
published: true
description: Configure ASP.NET Core Identity to keep users logged in, with proper cookie expiry dates. Understand why users are logged out before the session cookie expiry.
tags: c#, dotnet, identity, asp.net
canonical_url: https://andyp.dev/posts/asp-net-core-identity-configure-services-essentials
cover_image: https://andyp.dev/images/article/2020/asp-net-core-identity-cookie-expiry-essentials/asp-net-core-identity-dev-to.jpg
---

<p>This article was originally <a href="https://andyp.dev/posts/asp-net-core-identity-configure-services-essentials">posted from my blog.</a></p>

<p>I recently ported an old webapp from ASP.NET Core 2.0 to 3.1, using Identity to handle authorisation and authentication. Coming from Core 2.0, there&#39;s some changes in 3.1 which really felt like gotchas, and initially didn&#39;t made sense to me based on the information in the official documentation.</p>

<p>This guide describes how I configured ASP.NET Core 3.1 Identity, along with some complimentary information you may find useful. Assumptions are made that this information will be applied to an ASP.NET Core 3 MVC/Razor pages project.</p>

<p>Hopefully the contents of this article will teach you the essentials needed to setup your ConfigureServices method.</p>

<h2>Startup.cs and ConfigureServices -&nbsp;Configure Identity services</h2>

<p>Indentity services are added inside <em>ConfigureServices</em>, which is located inside your project&#39;s&nbsp;your <em>startup.cs</em> file.</p>

<h3>An Example <em>ConfigureServices </em>method.</h3>

<p>The below code snippet is an example of the <strong>default&nbsp;ConfigureServices </strong>method inside startup.cs. This is assuming you ticked the box to <em>Enable Authorisation</em> and selected the option for <em>Individual User Accounts</em> when you created the project.</p>

<pre>
<code class="language-cs">      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
              options.UseSqlServer(
                  Configuration.GetConnectionString("DefaultConnection")));
          services.AddDefaultIdentity&lt;IdentityUser&gt;(options =&gt; options.SignIn.RequireConfirmedAccount = true)
              .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();
          services.AddControllersWithViews();
          services.AddMvc();
      }</code></pre>

<div class="d-flex justify-content-center"><img alt="Enable user accounts" class="img-fluid shadow" src="https://andyp.dev/images/article/2020/asp-net-core-identity-cookie-expiry-essentials/individual-user-accounts.PNG" /></div>

<h3>Services.addDbContext</h3>

<p>Here is where would usually&nbsp;configure the connection string for Identity.&nbsp;</p>

<p>I&#39;ve found that the best approach is&nbsp;to setup one DbContext for Identity, and a seperate DbContext for storing other application data. This modularisation can help to scale your app, if you need to grow in the future. The trick is to place all Identity data in a seperate database to your application data.</p>

<pre>
<code class="language-cs">services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
              options.UseSqlServer(
                  Configuration.GetConnectionString("DefaultConnection")));</code></pre>

<h3>AddDefaultIdentity</h3>

<p>Adds a set of common identity services to the application, including a default UI, token providers, and configures authentication to use identity cookies.</p>

<pre>
<code class="language-cs">services.AddDefaultIdentity&lt;IdentityUser&gt;(options =&gt; options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();</code></pre>

<p>That covers the default services added to the&nbsp;ConfigureServices when you start a new project with Identity. Now lets look at some additional services you can add.</p>

<h3>Add AuthorizeFilter() to MVC service</h3>

<p>We use MVC for request handling in ASP.NET Core. Adding&nbsp;AuthorizeFilter() to the MVC Dependancy Injection service will redirect all requests to the login page if the user is not logged in.</p>

<pre>
<code class="language-cs">services.AddMvc(options =&gt;
{
    options.Filters.Add(new AuthorizeFilter());
});</code></pre>

<p>You can specify additional policies such as the below which restricts access to Admin and SuperUser users. If you don&#39;t specify a policy then default values will be used by AuthorizeFilter().</p>

<pre>
<code class="language-cs">var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .RequireRole("Admin", "SuperUser")
        .Build();

services.AddMvc(options =&gt;
{
    options.Filters.Add(new AuthorizeFilter(policy));
});</code></pre>

<h3>Specify your password requirements -&nbsp;services.Configure&lt;IdentityOptions&gt;</h3>

<p>At some point you&#39;ll likely want to define the level of complexity of user passwords in your ASP.NET Core web app.&nbsp;</p>

<p>You can specify the minimum length of user&#39;s passwords, toggle the requirement for lower and upper case characters, specify which characters are allowed in the password and also require users have a unique email address.&nbsp;</p>

<p>The beauty is that you don&#39;t need to write complex code to implement this yourself since it&#39;s all handled with some easy dependancy injection.</p>

<pre>
<code class="language-cs">services.Configure&lt;IdentityOptions&gt;(options =&gt;
            {
                options.Password.RequiredLength = 8;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = true;

                options.User.AllowedUserNameCharacters =
                        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = true;

            });</code></pre>

<h3>ConfigureApplicationCookie</h3>

<p>This is a big one. ConfigureApplicationCookie is where we define the handling of authorization cookies, it&#39;s also an area where I found little helpful documentation (when Core 3.1 released).</p>

<h3>options.Cookie.HttpOnly</h3>

<p>The HttpOnly flag instructs the browser that the cookie should only be accessed by the server and not any clientside&nbsp;scripts. This is very important for preventing XSS (Cross-site scripting) attacks. The flag should only be set to false if you know what you&#39;re doing!</p>

<h3>options.LoginPath</h3>

<p>Just as it sounds. This is the path that your users will be redirected to, if they need to login.</p>

<h3>options.AccessDeniedPath</h3>

<p>The&nbsp;path that users will be redirected to, if they try to access a resource that they are not authorized to view.</p>

<h3>options.SlidingExpiration</h3>

<p>SlidingExpiration resets the expiry DateTime of the cookie, so a new cookie is issued with a new expiry time, if the current time is more than halfway through the lifespan of the cookie. This means that users will stay logged in and the cookie stays valid, providing that the user visits the site at least once between halfway through the cookie life, and the expiry time.</p>

<p>So if the cookie expires in 12 hours, the cookie will be re-issued if the user visits the site after 6 hours, but before 12 hours after the cookie was issued.</p>

<h3>options.ExpireTimeSpan</h3>

<p>Which brings us onto the next essential option you should be aware of. ExpireTimeSpan&nbsp;is where you can confgure a custom expiry time for your cookies.</p>

<p>TimeSpan.FromHours(24) sets the expiry date to 24 hours from now. However you can also use .FromDays() or .FromMinutes() to specify a custom expiry length.</p>

<h3>options.Cookie.Name</h3>

<p>Configure the name of the Identity cookie with options.Cookie.Name.</p>

<pre>
<code class="language-cs">services.ConfigureApplicationCookie(options =&gt;
            {
                // Cookie settings
                options.Cookie.HttpOnly = true; // define the cookie for http/https requests only
                options.LoginPath = "/Identity/Account/Login"; // Set here login path.
                options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // set here access denied path.
                options.SlidingExpiration = true; // resets cookie expiration if more than half way through lifespan
                options.ExpireTimeSpan = TimeSpan.FromHours(24); // cookie validation time
                options.Cookie.Name = "myExampleCookie"; // name of the cookie saved to user's browsers
            });</code></pre>

<h3>AddDataProtection</h3>

<p>By default, when you restart an ASP.Net Core webapp, the application will lose track of which cookies are valid. All Identity users will need to login again, even if they visit the site before the cookie has expired.</p>

<p>With services.AddDataProtection, you can&nbsp;instruct&nbsp;your application to save the keys to the hard disk for recovery when the app is restarted. Note that this is suitable&nbsp;if you are deploying your app to a single server, but for multi-server deployments you should use a networked key-store or a database.</p>

<h3>PersistKeysToFileSystem</h3>

<p>By specifying a directory path in&nbsp;PersistKeysToFileSystem(), you can specify the folder where the keys should be stored.</p>

<h3>SetApplicationName</h3>

<p>It&#39;s possible for the key vault to store keys from different applications in the same folder. Separate keys by setting a unique application name.</p>

<h3>SetDefaultKeyLifetime</h3>

<p>The default key lifetime specifies the default lifespan of the keys in the key vault.</p>

<pre>
<code class="language-cs">services.AddDataProtection()
                .PersistKeysToFileSystem(new DirectoryInfo("\\DataProtection\\Keys"))
                .SetApplicationName("myExampleApp")
                .SetDefaultKeyLifetime(TimeSpan.FromDays(90)
    );</code></pre>

<h2>A&nbsp;Full Example ConfigureServices Method</h2>

<p>I hope some of this information has been useful to you. Here is a full example of the&nbsp;ConfigureServices method that you might use in production.</p>

<pre>
<code class="language-cs">public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity&lt;IdentityUser&gt;(options =&gt; options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();

    services.AddControllersWithViews().AddRazorRuntimeCompilation();
    services.AddMvc(options =&gt;
    {
        options.Filters.Add(new AuthorizeFilter());
    });

    // Set requirements for security
    services.Configure&lt;IdentityOptions&gt;(options =&gt;
    {
        options.Password.RequiredLength = 8;
        options.Password.RequireUppercase = true;
        options.Password.RequireLowercase = true;

        // Default User settings.
        options.User.AllowedUserNameCharacters =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = true;

    });

    services.ConfigureApplicationCookie(options =&gt;
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.LoginPath = "/Identity/Account/Login"; // Set here login path.
        options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // set here access denied path.
        options.SlidingExpiration = true; // resets cookie expiration if more than half way through lifespan
        options.ExpireTimeSpan = TimeSpan.from(60); // cookie validation time
        options.Cookie.Name = "myExampleCookieName";
    });

    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo("\\DataProtection\\Keys"))
        .SetApplicationName("myExampleApp")
        .SetDefaultKeyLifetime(TimeSpan.FromDays(90));
}</code></pre>

<p>&nbsp;</p>