Chapter 0 - jayharris/workshop-oidc GitHub Wiki

Chapter 0: Prologue

0.1: Create an empty solution

mkdir IdentityServerWorkshop
cd IdentityServerWorkshop

CLI Directory Reference

Within this document, each CLI example will include a starting path. For the purpose of this document, the path ./ will represent the root Identity Server Workshop directory, and all paths will be relative to this root directory.

# From ./
mkdir src

dotnet new sln
dotnet tool install -g dotnet-aspnet-codegenerator

0.2: Create the initial project

# From ./
dotnet new mvc --name IdentityProvider --auth Individual --output ./src/IdentityProvider/
dotnet sln add ./src/IdentityProvider/IdentityProvider.csproj

cd ./src/IdentityProvider/

dotnet ef database update

0.3: Install IdentityModel NuGet Packages

# From ./src/IdentityProvider
dotnet add package IdentityModel

0.4: Turn off HTTPS

Do not do this for production. Because this is a workshop about Identity Server and not SSL configuration, we will disable HTTPS and leave that for another workshop.

IdentityProvider\Startup.cs within the Configure method

//app.UseHttpsRedirection();

0.5: Add a Seed User

IdentityProvider\SeedData.cs

using System;
using System.Linq;
using System.Security.Claims;
using IdentityModel;
using IdentityProvider.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace IdentityProvider
{
  public class SeedData
  {
    public static void EnsureSeedData(string connectionString)
    {
      var services = new ServiceCollection();
      services.AddDbContext<ApplicationDbContext>(options =>
         options.UseSqlite(connectionString));

      services.AddDefaultIdentity<IdentityUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

      using (var serviceProvider = services.BuildServiceProvider())
      using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
      {
        var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
        context.Database.Migrate();

        var userMgr = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
        var adminUser = userMgr.FindByNameAsync("[email protected]").Result;
        if (adminUser != null)
        {
          Console.WriteLine("admin already exists");
          return;
        }

        adminUser = new IdentityUser
        {
          UserName = "[email protected]"
        };
        var result = userMgr.CreateAsync(adminUser, "AwesomePassword4U!").Result;
        if (!result.Succeeded)
        {
          throw new Exception(result.Errors.First().Description);
        }

        result = userMgr.AddClaimsAsync(adminUser, new Claim[]{
            new Claim(JwtClaimTypes.Name, "Admin User"),
            new Claim(JwtClaimTypes.GivenName, "Admin"),
            new Claim(JwtClaimTypes.FamilyName, "User"),
            new Claim(JwtClaimTypes.Email, "[email protected]"),
            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
            new Claim(JwtClaimTypes.WebSite, "http://example.com"),
            new Claim(JwtClaimTypes.Address, @"{ 'street_address': '350 Fifth Avenue', 'locality': 'New York', 'postal_code': 10118, 'country': 'United States' }", "json")
          }).Result;
        if (!result.Succeeded)
        {
          throw new Exception(result.Errors.First().Description);
        }
        Console.WriteLine("admin created");
      }
    }
  }
}

IdentityProvider\Program.cs add using Microsoft.Extensions.DependencyInjection; and replace Main method contents

using Microsoft.Extensions.DependencyInjection;
var seed = args.Any(x => x == "/seed");
if (seed) args = args.Except(new[] { "/seed" }).ToArray();

var host = CreateWebHostBuilder(args).Build();

if (seed)
{
  var config = host.Services.GetRequiredService<IConfiguration>();
  var connectionString = config.GetConnectionString("DefaultConnection");
  SeedData.EnsureSeedData(connectionString);
  return;
}

host.Run();

0.6: Seed the Administrative User

# From ./src/IdentityProvider
dotnet run /seed

0.7: Run the Site

# From ./src/IdentityProvider
dotnet run

Be sure to log in as [email protected] with AwesomePassword4U!. A successful login will ensure that Entity Framework and ASP.NET Identity are propertly configured before proceeding into Identity Server.