<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andy Preston</title>
    <description>The latest articles on DEV Community by Andy Preston (@andy_preston).</description>
    <link>https://dev.to/andy_preston</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F83995%2F7d682277-f9df-48a5-841f-961eaa382954.jpg</url>
      <title>DEV Community: Andy Preston</title>
      <link>https://dev.to/andy_preston</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andy_preston"/>
    <language>en</language>
    <item>
      <title>Use a Jump Server for a Secure Tunnel to Lightsail Databases</title>
      <dc:creator>Andy Preston</dc:creator>
      <pubDate>Wed, 01 Apr 2020 16:17:55 +0000</pubDate>
      <link>https://dev.to/andy_preston/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases-9a4</link>
      <guid>https://dev.to/andy_preston/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases-9a4</guid>
      <description>&lt;p&gt;Jump servers, otherwise known as &lt;strong&gt;bastion hosts&lt;/strong&gt;, provide seperation between your servers and the internet.&lt;/p&gt;

&lt;p&gt;The idea is that you make a secure connection to the jump server, which then forwards your request to the target server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w1w-al4Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/jump-server-process.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w1w-al4Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/jump-server-process.PNG" alt="Jump Server Process" width="645" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: In this blog post I'm going to talk about connecting to server's in AWS Lightsail as an example, however the same idea is applicable and transferable to Azure, Google Cloud or wherever you host your servers.&lt;/p&gt;

&lt;p&gt;Note 2: This article was originally posted on &lt;a href="https://andyp.dev/posts/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a Jump Server?
&lt;/h2&gt;

&lt;p&gt;Imagine that i've got 2 servers. A web server running on a compute instance, and then a seperate database instance in Lightsail.&lt;/p&gt;

&lt;p&gt;I don't want to make my database publically accessable to the whole world over the internet because it leaves the system open to security breaches. However I need to sometimes make a connection to the database for development purposes.&lt;/p&gt;

&lt;p&gt;Maybe I could only enable Public Mode while developing, but then I still need to remember to switch off Public Mode when I'm finished. And I might be spending days developing, which is a long time to leave the database accessable over the internet.&lt;/p&gt;

&lt;p&gt;If I create a jump server in the &lt;strong&gt;same VPC&lt;/strong&gt; as my database, then I can maintain a good level of security for my database, while forwarding requests from my development machine to the database. The web server can also connect to the database because it's on the same VPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump Server Security
&lt;/h2&gt;

&lt;p&gt;By using a jump server, we're still exposing a server to the internet, which is our Linux jump server. However we'll be authenticating the connection to our Linux server with an SSH key, and you could go one step further with 2FA (two factor authentication). We need to have port 22 open for the SSH connection.&lt;/p&gt;

&lt;p&gt;This is more secure than opening port 3306 for MySql, port 1433 for SQL Server or port 5432 for PostgreSQL to the whole world.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Local Port Forwarding?
&lt;/h2&gt;

&lt;p&gt;As the guide below will show you. Local port forwarding allows traffic from a specific port on your computer, to be routed via the jump server, to the destination.&lt;/p&gt;

&lt;p&gt;Instead of going from your computer to the database. The traffic goes from your computer &amp;gt; jump server &amp;gt; database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guide To Setting Up A Jump Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  You need to have MobaXterm installed. There are alternative programs, and you can also achieve the same results via the command line.  But this tutorial will utilize MobaXterm.&lt;/li&gt;
&lt;li&gt;  I'm assuming that you have a database or some kind of endpoint that you need to route traffic to inside Lightsail.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setup Ubuntu Jump Server
&lt;/h3&gt;

&lt;p&gt;Login to Lightsail, select the option for instances and then create a new instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EwWSIku8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/create-new-instance.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EwWSIku8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/create-new-instance.png" alt="Create lightsail instance" width="880" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Instance Location:&lt;/strong&gt; Select the Region and availability zone where your target servers are located&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Blueprint:&lt;/strong&gt; OS Only &amp;gt; Ubuntu (I'm using 18.04)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Key Pair:&lt;/strong&gt; Select an existing or create and download a new SSH key&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Instance Plan:&lt;/strong&gt; I'm using a $3.50 instance with 512mb RAM / 1vCPU / 20gb SSH / 1TB transfer. This should be more than enough for a jump server unless there's several people connecting at once.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Instance name:&lt;/strong&gt; Something useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure you have the SSH key downloaded from the Lightsail portal. You'll need this later.&lt;/p&gt;

&lt;p&gt;When you're ready, click on &lt;strong&gt;Create Instance&lt;/strong&gt; and wait for your machine to become ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update The Server
&lt;/h3&gt;

&lt;p&gt;It's a good idea to test your connection to the server. I recommend connecting to the server via SSH and updating via APT.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then,&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt upgrade&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Port Forwarding with MobaXterm&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Open MobaXterm and open up the tools sidebar by clicking on the icon on the left hand side.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Then click on &lt;strong&gt;MobaSSHTunnel (Port Forwarding)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  In the popup window, click on &lt;strong&gt;New SSH Tunnel&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qV_7sScl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/mobaxterm-tools-ssh-tunnel-port-forwarding.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qV_7sScl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/mobaxterm-tools-ssh-tunnel-port-forwarding.png" alt="Create lightsail instance" width="216" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now see this window popup window, here we can specify the details of your port forwarding chain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JiDxGkJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/local-port-forwarding-window.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JiDxGkJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/local-port-forwarding-window.png" alt="Mobaxterm port forwarding window" width="776" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep the &lt;strong&gt;Local Port Forwarding&lt;/strong&gt; radio button selected. And enter the below details based on your unique setup;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Fordwarded Port&lt;/strong&gt;: The local port that you wish to forward&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Server&lt;/strong&gt;: The IP address or name (if using DNS) of the server of the jump server.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Login&lt;/strong&gt;: The username that you'll use to login to the server. On AWS Lightsail this is usually "ubuntu" by default.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Port&lt;/strong&gt;: Usually port 22.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Remote Server&lt;/strong&gt;: The endpoint IP address or name of our database or server&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Remote Port&lt;/strong&gt;: This will usually be the same as the forwarded port. Something like 3306 for MYSQL or something different depending on what you're connecting to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have this information entered, click on the save button.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH Key
&lt;/h3&gt;

&lt;p&gt;You should now see the newly created port forwarding config on the SSH Tunnel list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3qwMs8tO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/saved-port-forwarding-entry-mobaxterm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3qwMs8tO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://andyp.dev/images/article/2020/use-a-jump-server-for-a-secure-tunnel-to-lightsail-databases/saved-port-forwarding-entry-mobaxterm.png" alt="Forwarded port setup in mobaxterm" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's now one final task. Click on the key icon and browse to the location where you saved the SSH key you downloaded earlier.&lt;/p&gt;

&lt;p&gt;Once you've opened the SSH key saved earlier, click on the start button to start your SSH tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;And that's it you should now be able to make a connection to the endpoint via your jump server.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ssh</category>
      <category>aws</category>
      <category>tunnel</category>
    </item>
    <item>
      <title>Learn 6 ES6 Javascript Equivalents to C# LINQ Methods</title>
      <dc:creator>Andy Preston</dc:creator>
      <pubDate>Mon, 23 Mar 2020 15:13:50 +0000</pubDate>
      <link>https://dev.to/andy_preston/learn-6-es6-javascript-equivalents-to-c-linq-methods-53ef</link>
      <guid>https://dev.to/andy_preston/learn-6-es6-javascript-equivalents-to-c-linq-methods-53ef</guid>
      <description>&lt;p&gt;I like many others, have a love/hate relationship with Javascript.&lt;/p&gt;

&lt;p&gt;Most of my time is spent developing enterprise IT systems in C# but i've recently promised myself that I'd spend some time to really learn modern Javascript.&lt;/p&gt;

&lt;p&gt;I've been using modern Javascript for several years but always found myself forgetting what all the ES6 methods mean. In my opinion, "Map" isn't really memorable, even to a native English speaker when compared to the C# equivalent "Select".&lt;/p&gt;

&lt;p&gt;So I made a list of some commonly used ES6 methods in Javascript, with a short explanation and details of their C#/LINQ counterparts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Dataset
&lt;/h2&gt;

&lt;p&gt;If you need to test any of the methods i'm going to talk about, feel free to to test them in Javascript using the below dataset.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var people = [ 
    { name: "James", age: 20 }, 
    { name: "Bob", age: 31 }, 
    { name: "Harry", age: 18 }, 
    { name: "Linus", age: 23 }, 
    { name: "Barry", age: 50 }, 
    { name: "Hillary", age: 45 },
    { name: "Peter", age: 22 } 
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Where &amp;amp; Filter
&lt;/h2&gt;

&lt;p&gt;Javascript's Filter method works the same way you would expect the Where method to work in LINQ.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var items = people.filter(function (item) {
    return item.age &amp;gt; 25;
});

console.log("People older than 25");
console.log(items);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Filter creates a new array of items which pass the specified logic. In our example, we create a list of people older than 25.&lt;/p&gt;

&lt;h2&gt;
  
  
  Select &amp;amp; Map
&lt;/h2&gt;

&lt;p&gt;The Map method in Javascript works the same way you would expect Select to work in LINQ.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var names = people.map(function (item) {
    return item.name;
});

console.log("Just the names of people in the array");
console.log(names);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Essentially by using the Map method we create a new array of entries from the old array, however we only pull the values that we have specified.&lt;/p&gt;

&lt;p&gt;Think of this like creating a new dataset based on specific values from a database, but we're specifying the column names and only pulling values from the columns that we have specified.&lt;/p&gt;

&lt;h2&gt;
  
  
  All &amp;amp; Every
&lt;/h2&gt;

&lt;p&gt;The Every method in Javascript is equivalent to All in LINQ.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var allUnder20 = people.every(function (item) {
    return  item.age &amp;lt; 20;
});

console.log("Are all people older than 20");
console.log(allUnder20);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every lets us test that all items within an array match the test criteria. You will receive either a true or false response from the method. &lt;/p&gt;

&lt;h2&gt;
  
  
  Some &amp;amp; Any
&lt;/h2&gt;

&lt;p&gt;In javascript we have the Some method which is equivalent to Any in LINQ.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var anyOver20 = people.some(function (item) {
    return  item.age &amp;gt; 20;
});

console.log("Are any people over 20?"); 
console.log(anyOver20 );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By using Some, we can check if any items within an array match the criteria that we have specified. If a single item matches our criteria then a true response is sent, otherwise it will be false.&lt;/p&gt;

&lt;h2&gt;
  
  
  OrderBy &amp;amp; Sort
&lt;/h2&gt;

&lt;p&gt;The Sort method in Javascript is a close match to the OrderBy LINQ method.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var sortedByName = people.sort(function (a, b) {
    return  a.name &amp;gt; b.name ? 1 : 0;
})

console.log("The list ordered by name"); 
console.log(sortedByName);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Sort allows us to sort the array of elements by a specific value shared between all items within the array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregate &amp;amp; Reduce
&lt;/h2&gt;

&lt;p&gt;The final method on our list is Reduce, which is the equvalent to the Aggregate method we use in LINQ. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var aggregate = people.reduce(function (item1, item2) {
    return  { name: '', age: item1.age + item2.age };
});

console.log("Aggregate age"); 
console.log(aggregate.age);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Essentially the Reduce method lets us reduce all of the specified values in the array into a single value. In our example we are adding all of the ages from all people, and then printing the output to the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;Many of the naming conventions used for the JS methods seem strange to many, including myself. That said, I'd recommend anyone who ever touches the frontend to learn (or at least be aware of) these modern JS methods.&lt;/p&gt;

&lt;p&gt;I hope this has been useful.&lt;/p&gt;

</description>
      <category>c</category>
      <category>javascript</category>
      <category>es6</category>
      <category>net</category>
    </item>
    <item>
      <title>ASP.NET Core Identity - ConfigureServices Essentials</title>
      <dc:creator>Andy Preston</dc:creator>
      <pubDate>Thu, 16 Jan 2020 16:33:16 +0000</pubDate>
      <link>https://dev.to/andy_preston/asp-net-core-identity-configureservices-essentials-42mc</link>
      <guid>https://dev.to/andy_preston/asp-net-core-identity-configureservices-essentials-42mc</guid>
      <description>&lt;p&gt;This article was originally &lt;a href="https://andyp.dev/posts/asp-net-core-identity-configure-services-essentials" rel="noopener noreferrer"&gt;posted from my blog.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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's some changes in 3.1 which really felt like gotchas, and initially didn't made sense to me based on the information in the official documentation.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Hopefully the contents of this article will teach you the essentials needed to setup your ConfigureServices method.&lt;/p&gt;

&lt;h2&gt;Startup.cs and ConfigureServices - Configure Identity services&lt;/h2&gt;

&lt;p&gt;Indentity services are added inside &lt;em&gt;ConfigureServices&lt;/em&gt;, which is located inside your project's your &lt;em&gt;startup.cs&lt;/em&gt; file.&lt;/p&gt;

&lt;h3&gt;An Example &lt;em&gt;ConfigureServices &lt;/em&gt;method.&lt;/h3&gt;

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

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fandyp.dev%2Fimages%2Farticle%2F2020%2Fasp-net-core-identity-cookie-expiry-essentials%2Findividual-user-accounts.PNG" class="article-body-image-wrapper"&gt;&lt;img alt="Enable user accounts" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fandyp.dev%2Fimages%2Farticle%2F2020%2Fasp-net-core-identity-cookie-expiry-essentials%2Findividual-user-accounts.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Services.addDbContext&lt;/h3&gt;

&lt;p&gt;Here is where would usually configure the connection string for Identity. &lt;/p&gt;

&lt;p&gt;I've found that the best approach is 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.&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;
              options.UseSqlServer(
                  Configuration.GetConnectionString("DefaultConnection")));&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;AddDefaultIdentity&lt;/h3&gt;

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

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

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

&lt;h3&gt;Add AuthorizeFilter() to MVC service&lt;/h3&gt;

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

&lt;pre&gt;
&lt;code&gt;services.AddMvc(options =&amp;gt;
{
    options.Filters.Add(new AuthorizeFilter());
});&lt;/code&gt;&lt;/pre&gt;

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

&lt;pre&gt;
&lt;code&gt;var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .RequireRole("Admin", "SuperUser")
        .Build();

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

&lt;h3&gt;Specify your password requirements - services.Configure&amp;lt;IdentityOptions&amp;gt;&lt;/h3&gt;

&lt;p&gt;At some point you'll likely want to define the level of complexity of user passwords in your ASP.NET Core web app. &lt;/p&gt;

&lt;p&gt;You can specify the minimum length of user'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. &lt;/p&gt;

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

&lt;pre&gt;
&lt;code&gt;services.Configure&amp;lt;IdentityOptions&amp;gt;(options =&amp;gt;
            {
                options.Password.RequiredLength = 8;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = true;

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

            });&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;ConfigureApplicationCookie&lt;/h3&gt;

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

&lt;h3&gt;options.Cookie.HttpOnly&lt;/h3&gt;

&lt;p&gt;The HttpOnly flag instructs the browser that the cookie should only be accessed by the server and not any clientside 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're doing!&lt;/p&gt;

&lt;h3&gt;options.LoginPath&lt;/h3&gt;

&lt;p&gt;Just as it sounds. This is the path that your users will be redirected to, if they need to login.&lt;/p&gt;

&lt;h3&gt;options.AccessDeniedPath&lt;/h3&gt;

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

&lt;h3&gt;options.SlidingExpiration&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;options.ExpireTimeSpan&lt;/h3&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;options.Cookie.Name&lt;/h3&gt;

&lt;p&gt;Configure the name of the Identity cookie with options.Cookie.Name.&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;services.ConfigureApplicationCookie(options =&amp;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
            });&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;AddDataProtection&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;With services.AddDataProtection, you can instruct your application to save the keys to the hard disk for recovery when the app is restarted. Note that this is suitable 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.&lt;/p&gt;

&lt;h3&gt;PersistKeysToFileSystem&lt;/h3&gt;

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

&lt;h3&gt;SetApplicationName&lt;/h3&gt;

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

&lt;h3&gt;SetDefaultKeyLifetime&lt;/h3&gt;

&lt;p&gt;The default key lifetime specifies the default lifespan of the keys in the key vault.&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;services.AddDataProtection()
                .PersistKeysToFileSystem(new DirectoryInfo("\\DataProtection\\Keys"))
                .SetApplicationName("myExampleApp")
                .SetDefaultKeyLifetime(TimeSpan.FromDays(90)
    );&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;A Full Example ConfigureServices Method&lt;/h2&gt;

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

&lt;pre&gt;
&lt;code&gt;public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

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

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

    // Set requirements for security
    services.Configure&amp;lt;IdentityOptions&amp;gt;(options =&amp;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 =&amp;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));
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt; &lt;/p&gt;

</description>
      <category>c</category>
      <category>dotnet</category>
      <category>identity</category>
      <category>aspnet</category>
    </item>
  </channel>
</rss>
