adds aspnet.identity

broken/ef
Paul Schneider 3 years ago
parent a2f26f1e8e
commit dc37c9a9f0
26 changed files with 1106 additions and 122 deletions

@ -9,12 +9,11 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/nuget-host.dll", "program": "${workspaceFolder}/bin/Debug/netcoreapp2.1/nuget-host.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"stopAtEntry": false, "stopAtEntry": false,
"requireExactSource": false, "requireExactSource": false,
"OS-COMMENT5": "Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser",
"serverReadyAction": { "serverReadyAction": {
"action": "openExternally", "action": "openExternally",
"pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)" "pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)"

@ -21,6 +21,7 @@
"args": [ "args": [
"build", "build",
"${workspaceFolder}/nuget-host.csproj", "${workspaceFolder}/nuget-host.csproj",
"/p:Configuration=Debug",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary",
"--ignore-failed-sources" "--ignore-failed-sources"

@ -17,6 +17,7 @@ namespace nuget_host
public static IEnumerable<ApiResource> ApiResources => public static IEnumerable<ApiResource> ApiResources =>
new List<ApiResource> new List<ApiResource>
{ {
new ApiResource(IdentityServerConstants.LocalApi.ScopeName),
new ApiResource(scope_packages) new ApiResource(scope_packages)
}; };
@ -33,7 +34,8 @@ namespace nuget_host
AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedGrantTypes = GrantTypes.ClientCredentials,
// scopes that client has access to // scopes that client has access to
AllowedScopes = { scope_packages } AllowedScopes = { scope_packages,
IdentityServerConstants.LocalApi.ScopeName }
}, },
// interactive ASP.NET Core MVC client // interactive ASP.NET Core MVC client
@ -54,6 +56,7 @@ namespace nuget_host
{ {
IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.LocalApi.ScopeName,
scope_packages scope_packages
} }
} }

@ -13,12 +13,14 @@ using IdentityServer4.Test;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using nuget_host.Models;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace nuget_host.Models namespace nuget_host.Controllers
{ {
/// <summary> /// <summary>
/// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// This sample controller implements a typical login/logout/provision workflow for local and external accounts.
@ -29,27 +31,28 @@ namespace nuget_host.Models
[AllowAnonymous] [AllowAnonymous]
public class AccountController : Controller public class AccountController : Controller
{ {
private readonly TestUserStore _users;
private readonly IIdentityServerInteractionService _interaction; private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore; private readonly IClientStore _clientStore;
private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IAuthenticationSchemeProvider _schemeProvider;
private readonly IEventService _events; private readonly IEventService _events;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public AccountController( public AccountController(
IIdentityServerInteractionService interaction, IIdentityServerInteractionService interaction,
IClientStore clientStore, IClientStore clientStore,
IAuthenticationSchemeProvider schemeProvider, IAuthenticationSchemeProvider schemeProvider,
IEventService events, IEventService events,
TestUserStore users = null) SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager)
{ {
// if the TestUserStore is not in DI, then we'll just use the global users collection
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
_users = users ?? new TestUserStore(TestUsers.Users);
_interaction = interaction; _interaction = interaction;
_clientStore = clientStore; _clientStore = clientStore;
_schemeProvider = schemeProvider; _schemeProvider = schemeProvider;
_events = events; _events = events;
_signInManager = signInManager;
_userManager = userManager;
} }
/// <summary> /// <summary>
@ -110,11 +113,13 @@ namespace nuget_host.Models
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
// validate username/password against in-memory store // validate username/password
if (_users.ValidateCredentials(model.Username, model.Password)) var user = await _userManager.FindByNameAsync(model.Username);
var signResult = await _signInManager.CheckPasswordSignInAsync(user, model.Password, true);
if (signResult.Succeeded)
{ {
var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.FullName, true, clientId: context?.ClientId));
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.ClientId));
// only set explicit expiration here if user chooses "remember me". // only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware. // otherwise we rely upon expiration configured in cookie middleware.
@ -129,9 +134,9 @@ namespace nuget_host.Models
}; };
// issue authentication cookie with subject ID and username // issue authentication cookie with subject ID and username
var isuser = new IdentityServerUser(user.SubjectId) var isuser = new IdentityServerUser(user.Id)
{ {
DisplayName = user.Username DisplayName = user.UserName
}; };
await HttpContext.SignInAsync(isuser, props); await HttpContext.SignInAsync(isuser, props);
@ -150,6 +155,8 @@ namespace nuget_host.Models
} }
// request for a local page // request for a local page
await _signInManager.SignInAsync(user, model.RememberLogin && AccountOptions.AllowRememberLogin);
if (Url.IsLocalUrl(model.ReturnUrl)) if (Url.IsLocalUrl(model.ReturnUrl))
{ {
return Redirect(model.ReturnUrl); return Redirect(model.ReturnUrl);

@ -11,11 +11,11 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Validation;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using IdentityServer4.Models; using nuget_host.Models;
namespace nuget_host.Models
namespace nuget_host.Controllers
{ {
/// <summary> /// <summary>
/// This controller processes the consent UI /// This controller processes the consent UI

@ -9,13 +9,14 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using nuget_host.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace nuget_host.Models namespace nuget_host.Controllers
{ {
[SecurityHeaders] [SecurityHeaders]
[AllowAnonymous] [AllowAnonymous]
@ -65,7 +66,7 @@ namespace nuget_host.Models
RedirectUri = Url.Action(nameof(Callback)), RedirectUri = Url.Action(nameof(Callback)),
Items = Items =
{ {
{ "returnUrl", returnUrl }, { "returnUrl", returnUrl },
{ "scheme", scheme }, { "scheme", scheme },
} }
}; };

@ -4,9 +4,12 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Services; using IdentityServer4.Services;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using nuget_host.Entities;
using nuget_host.Models; using nuget_host.Models;
namespace nuget_host.Controllers namespace nuget_host.Controllers
@ -15,13 +18,20 @@ namespace nuget_host.Controllers
{ {
private readonly IIdentityServerInteractionService _interaction; private readonly IIdentityServerInteractionService _interaction;
private readonly ILogger _logger; private readonly ILogger _logger;
IHostingEnvironment _environment; readonly IHostingEnvironment _environment;
public IDataProtector DataProtector { get; }
public SmtpSettings Options { get; } //set only via Secret Manager
public HomeController(IIdentityServerInteractionService interaction, Microsoft.AspNetCore.Hosting.IHostingEnvironment environment, ILogger<HomeController> logger) public HomeController(
IOptions<SmtpSettings> smtpSettings,
IDataProtectionProvider provider,
IIdentityServerInteractionService interaction, Microsoft.AspNetCore.Hosting.IHostingEnvironment environment, ILogger<HomeController> logger)
{ {
_interaction = interaction; _interaction = interaction;
_environment = environment; _environment = environment;
_logger = logger; _logger = logger;
Options = smtpSettings.Value;
DataProtector = provider.CreateProtector(Options.ProtectionTitle);
} }
public IActionResult Index() public IActionResult Index()
@ -43,9 +53,9 @@ namespace nuget_host.Controllers
return View(); return View();
} }
public IActionResult Test() public IActionResult Privacy()
{ {
ViewData["Message"] = "Your test page."; ViewData["Message"] = "Your Privacy page.";
return Ok(ViewData); return Ok(ViewData);
} }

@ -13,8 +13,8 @@ namespace nuget_host.Controllers
{ {
public class PackagesController : Controller public class PackagesController : Controller
{ {
private ILogger<PackagesController> logger; private readonly ILogger<PackagesController> logger;
private IDataProtector protector; private readonly IDataProtector protector;
public PackagesController(ILoggerFactory loggerFactory, IDataProtectionProvider provider) public PackagesController(ILoggerFactory loggerFactory, IDataProtectionProvider provider)
{ {

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using nuget_host.Models;
namespace nuget_host.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
}

@ -0,0 +1,13 @@
namespace nuget_host.Entities
{
public class SmtpSettings
{
public string Server {get; set;}
public int Port {get; set;}
public string SenderName {get; set;}
public string SenderEMail {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
public string ProtectionTitle {get; set;}
}
}

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace nuget_host.Interfaces
{
public interface IMailer
{
Task SendMailAsync(string name, string email, string subjet, string body);
}
}

@ -0,0 +1,230 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using nuget_host.Data;
namespace nugethost.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20210424155323_init")]
partial class init
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("nuget_host.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FullName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,219 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace nugethost.Migrations
{
public partial class init : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false),
FullName = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

@ -0,0 +1,228 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using nuget_host.Data;
namespace nugethost.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("nuget_host.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<string>("FullName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("nuget_host.Models.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace nuget_host.Models
{
public class RegisterViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 2)]
[Display(Name = "FullName")]
public string FullName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
}

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Identity;
namespace nuget_host.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
}
}

@ -0,0 +1,68 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using MailKit.Net.Smtp;
using MimeKit;
using System;
using nuget_host.Interfaces;
using nuget_host.Entities;
namespace nuget_host.Services
{
public class EmailSender : IEmailSender, IMailer
{
public EmailSender(IOptions<SmtpSettings> smtpSettings,
Microsoft.AspNetCore.Hosting.IHostingEnvironment env)
{
Options = smtpSettings.Value;
Env = env;
}
public SmtpSettings Options { get; } //set only via Secret Manager
public Microsoft.AspNetCore.Hosting.IHostingEnvironment Env { get; }
public Task SendEmailAsync(string email, string subject, string message)
{
return Execute(Options.SenderName, subject, message, email);
}
public async Task Execute(string name, string subject, string message, string email)
{
await SendMailAsync(name, email, subject, message);
}
public async Task SendMailAsync(string name, string email, string subjet, string body)
{
try {
var message = new MimeMessage();
message.From.Add(new MailboxAddress(Options.SenderName, Options.SenderEMail));
message.To.Add(new MailboxAddress(name??email, email));
message.Body = new TextPart("html") { Text = body };
using (var client = new SmtpClient())
{
client.ServerCertificateValidationCallback = (s,c,h,e)=>true;
if (Env.IsDevelopment() || Options.UserName == null)
{
await client.ConnectAsync(Options.Server, Options.Port, MailKit.Security.SecureSocketOptions.None);
}
else
{
await client.ConnectAsync(Options.Server);
}
if (Options.UserName != null)
{
await client.AuthenticateAsync(Options.UserName, Options.Password);
}
await client.SendAsync(message);
await client.DisconnectAsync(true);
}
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message);
}
}
}
}

@ -2,12 +2,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Hosting;
using nuget_host.Data;
using nuget_host.Interfaces;
using nuget_host.Services;
using nuget_host.Entities;
using nuget_host.Models;
namespace nuget_host namespace nuget_host
{ {
@ -26,42 +36,51 @@ namespace nuget_host
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) services.AddDbContext<ApplicationDbContext>(options =>
.AddJwtBearer(options => options.UseNpgsql(
{ Configuration.GetConnectionString("DefaultConnection")));
// base-address of your identityserver
options.Authority = ExternalUrl;
// if you are using API resources, you can specify the name here services.AddIdentity<ApplicationUser, IdentityRole>()
options.Audience = "packages"; .AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultUI()
.AddDefaultTokenProviders();
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("local_api", "~/localapi");
})
.AddAspNetIdentity<ApplicationUser>()
.AddInMemoryClients(Config.Clients)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiResources(Config.ApiResources)
.AddDeveloperSigningCredential()
.AddTestUsers(Config.TestUsers);
});
services.AddMvc(); services.AddMvc();
services.AddDataProtection(); services.AddDataProtection();
services.AddTransient<IMailer, EmailSender>();
services.AddTransient<IEmailSender, EmailSender>();
services.AddIdentityServer() var smtpSettingsconf = Configuration.GetSection("SmtpSettings");
.AddInMemoryClients(Config.Clients) services.Configure<SmtpSettings>(smtpSettingsconf);
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiResources(Config.ApiResources)
.AddDeveloperSigningCredential()
.AddTestUsers(Config.TestUsers);
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env)
{ {
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
} }
else else
{ {
app.UseExceptionHandler("/Home/Error"); app.UseExceptionHandler("/Home/Error");
app.UseHsts();
} }
ExternalUrl = Configuration["NuGet:ExternalUrl"]; ExternalUrl = Configuration["NuGet:ExternalUrl"];
SourceDir = Configuration["NuGet:SourceDir"]; SourceDir = Configuration["NuGet:SourceDir"];
@ -69,6 +88,11 @@ namespace nuget_host
app.UseStaticFiles(); app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapRoute( routes.MapRoute(

@ -0,0 +1,49 @@
@model RegisterViewModel
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"].</h1>
<form asp-controller="Account" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="FullName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="FullName" class="form-control" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Register</button>
</div>
</div>
</form>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

@ -1,32 +1,33 @@
@using System.Diagnostics @{
ViewData["Title"] = "Home Page";
@{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }
}
<div class="text-center">
<div class="welcome-page"> <h1 class="display-4">Welcome</h1>
<h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<img src="~/icon.jpg"> <h1>
Welcome to IdentityServer4 <img src="~/icon.jpg">
<small class="text-muted">(version @version)</small> Welcome to IdentityServer4
</h1> <small class="text-muted">(version @version)</small>
</h1>
<ul>
<li> <ul>
IdentityServer publishes a <li>
<a href="~/.well-known/openid-configuration">discovery document</a> IdentityServer publishes a
where you can find metadata and links to all the endpoints, key material, etc. <a href="~/.well-known/openid-configuration">discovery document</a>
</li> where you can find metadata and links to all the endpoints, key material, etc.
<li> </li>
Click <a href="~/diagnostics">here</a> to see the claims for your current session. <li>
</li> Click <a href="~/diagnostics">here</a> to see the claims for your current session.
<li> </li>
Click <a href="~/grants">here</a> to manage your stored grants. <li>
</li> Click <a href="~/grants">here</a> to manage your stored grants.
<li> </li>
Here are links to the <li>
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>, Here are links to the
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>. <a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
</li> and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
</ul> </li>
</div> </ul>
</div>

@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

@ -1,28 +1,49 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" /> <title>@ViewData["Title"] - nuget host</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<title>IdentityServer4</title> <link rel="stylesheet" href="~/css/site.css" />
</head>
<link rel="icon" type="image/x-icon" href="~/favicon.ico" /> <body>
<link rel="shortcut icon" type="image/x-icon" href="~/favicon.ico" /> <header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> <div class="container">
<link rel="stylesheet" href="~/css/site.css" /> <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">mvc_ident</a>
</head> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
<body> aria-expanded="false" aria-label="Toggle navigation">
<partial name="_Nav" /> <span class="navbar-toggler-icon"></span>
</button>
<div class="container body-container"> <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
@RenderBody() <ul class="navbar-nav flex-grow-1">
</div> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
<script src="~/lib/jquery/dist/jquery.slim.min.js"></script> </li>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
@RenderSection("scripts", required: false) </li>
</body> </ul>
</html> <partial name="_LoginPartial" />
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2021 - mvc_ident - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

@ -0,0 +1,26 @@
@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
</li>
<li class="nav-item">
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>

@ -1,6 +1,6 @@
{ {
"NuGet": { "NuGet": {
"ExternalUrl" : "<http://localhost:5000/Packages", "ExternalUrl" : "<http://localhost:5000/packages",
"SourceDir" : "packages" "SourceDir" : "packages"
}, },
"RootApiKeySecret": "secret-key" "RootApiKeySecret": "secret-key"

@ -4,8 +4,18 @@
"ExternalUrl" : "<http://your-external.url", "ExternalUrl" : "<http://your-external.url",
"SourceDir" : "<your-Source-dir>" "SourceDir" : "<your-Source-dir>"
}, },
"ConnectionStrings": {
"DefaultConnection": "Server=<pqserver>;Port=<pqport>;Database=<dbname>;Username=<dbusername>;Password=<dbpass>;"
},
"AllowedHosts": "*",
"SmtpSettings": {
"Server": "localhost",
"Port": 25,
"SenderName": "Paul Schneider",
"SenderEmail": "paul@pschneider.fr",
"ProtectionTitle": "protected-data-v1"
},
"Logging": { "Logging": {
"IncludeScopes": false,
"LogLevel": { "LogLevel": {
"Default": "Warning" "Default": "Warning"
} }

@ -7,28 +7,27 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="1.0.0" /> <PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NuGet.Packaging.Core" Version="5.9.0" /> <PackageReference Include="NuGet.Packaging.Core" Version="5.9.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.1" />
<PackageReference Include="IdentityServer4" Version="2.5.4" /> <PackageReference Include="IdentityServer4" Version="2.5.4" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.5.4" />
<PackageReference Include="MailKit" Version="2.11.1" /> <PackageReference Include="MailKit" Version="2.11.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.0" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" IncludeAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.1.0-preview1-final" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Loading…