();
19 | }
20 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 | @* Copyright © 2017 Dmitry Sikorsky. All rights reserved. *@
2 | @* Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. *@
3 |
4 |
5 |
6 |
7 | ASP.NET Core Custom User Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 | @RenderBody()
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ASP.NET Core Custom User Manager
2 | This is a demo web application for the
3 | “[ASP.NET Core Custom User Manager](https://medium.com/@dmitrysikorsky/asp-net-core-custom-user-manager-a7206e718a90)”
4 | post on the [Dmitry Sikorsky’s blog](https://medium.com/@dmitrysikorsky). It demonstrates how to build custom
5 | user manager on ASP.NET Core if you don’t want to use Identity.
6 |
7 | The result looks like this:
8 | 
9 | *Custom ASP.NET Core user manager*
10 |
11 | Updated to use .NET Core 6.
12 |
13 | ## Using the Application
14 |
15 | 1. Run the application.
16 | 2. Click Login user button.
17 | 3. Click Logout user button.
18 |
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Models/Credential.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | namespace AspNetCoreCustomUserManager.Models
5 | {
6 | public class Credential
7 | {
8 | public int Id { get; set; }
9 | public int UserId { get; set; }
10 | public int CredentialTypeId { get; set; }
11 | public string Identifier { get; set; }
12 | public string Secret { get; set; }
13 | public string Extra { get; set; }
14 |
15 | public virtual User User { get; set; }
16 | public virtual CredentialType CredentialType { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:60233/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "AspNetCoreCustomUserManager": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:60234"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Pbkdf2Hasher.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2018 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Security.Cryptography;
6 | using Microsoft.AspNetCore.Cryptography.KeyDerivation;
7 |
8 | namespace AspNetCoreCustomUserManager
9 | {
10 | public static class Pbkdf2Hasher
11 | {
12 | public static string ComputeHash(string password, byte[] salt)
13 | {
14 | return Convert.ToBase64String(
15 | KeyDerivation.Pbkdf2(
16 | password: password,
17 | salt: salt,
18 | prf: KeyDerivationPrf.HMACSHA1,
19 | iterationCount: 10000,
20 | numBytesRequested: 256 / 8
21 | )
22 | );
23 | }
24 |
25 | public static byte[] GenerateRandomSalt()
26 | {
27 | byte[] salt = new byte[128 / 8];
28 |
29 | using (var rng = RandomNumberGenerator.Create())
30 | rng.GetBytes(salt);
31 |
32 | return salt;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26403.7
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreCustomUserManager", "AspNetCoreCustomUserManager\AspNetCoreCustomUserManager.csproj", "{6170B3CB-6E0E-44B6-AC59-951CEA7F6869}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {6170B3CB-6E0E-44B6-AC59-951CEA7F6869}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {6170B3CB-6E0E-44B6-AC59-951CEA7F6869}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {6170B3CB-6E0E-44B6-AC59-951CEA7F6869}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {6170B3CB-6E0E-44B6-AC59-951CEA7F6869}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace AspNetCoreCustomUserManager
7 | {
8 | public class HomeController : Controller
9 | {
10 | private IUserManager userManager;
11 |
12 | public HomeController(IUserManager userManager)
13 | {
14 | this.userManager = userManager;
15 | }
16 |
17 | [HttpGet]
18 | public IActionResult Index()
19 | {
20 | return this.View();
21 | }
22 |
23 | [HttpPost]
24 | public IActionResult Login()
25 | {
26 | ValidateResult validateResult = this.userManager.Validate("Email", "admin@example.com", "admin");
27 |
28 | if (validateResult.Success)
29 | this.userManager.SignIn(this.HttpContext, validateResult.User, false);
30 |
31 | return this.RedirectToAction("Index");
32 | }
33 |
34 | [HttpPost]
35 | public IActionResult Logout()
36 | {
37 | this.userManager.SignOut(this.HttpContext);
38 | return this.RedirectToAction("Index");
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/wwwroot/css/app.css:
--------------------------------------------------------------------------------
1 | /* Copyright © 2017 Dmitry Sikorsky. All rights reserved. */
2 | /* Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. */
3 |
4 | /* Global things */
5 | html, body {
6 | color: #323232;
7 | font: normal 20px 'Open Sans', sans-serif;
8 | line-height: 1.5;
9 | text-align: center;
10 | margin: 0;
11 | padding: 0;
12 | }
13 |
14 | body {
15 | padding: 50px;
16 | }
17 |
18 | h1, h2 {
19 | font-weight: normal;
20 | margin: 0;
21 | padding: 0;
22 | }
23 |
24 | h1 {
25 | font-size: 60px;
26 | }
27 |
28 | h2 {
29 | font-size: 40px;
30 | margin-top: 40px;
31 | }
32 |
33 | select, button, label {
34 | font: normal 20px 'Open Sans', sans-serif;
35 | }
36 |
37 | /* Field */
38 | .field {
39 | }
40 |
41 | .field__validation-message {
42 | color: red;
43 | font-style: italic;
44 | display: block;
45 | margin-top: 10px;
46 | }
47 |
48 | /* Buttons */
49 | .buttons {
50 | }
51 |
52 | /* Form */
53 | .form {
54 | }
55 |
56 | .form__field {
57 | margin-top: 20px;
58 | }
59 |
60 | .form__buttons {
61 | margin-top: 40px;
62 | }
63 |
64 | /* Marker */
65 | .marker {
66 | }
67 |
68 | .marker--secondary {
69 | color: #999;
70 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @* Copyright © 2017 Dmitry Sikorsky. All rights reserved. *@
2 | @* Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. *@
3 | @model AspNetCoreCustomUserManager.IndexViewModel
4 | @inject AspNetCoreCustomUserManager.IUserManager UserManager
5 | @using System.Security.Claims
6 | ASP.NET Core
Custom User Manager
7 |
8 | User.Identity.IsAuthenticated: @User.Identity.IsAuthenticated
9 |
10 | @if (this.User.Identity.IsAuthenticated)
11 | {
12 |
13 | UserManager.GetCurrentUser(this.Context).Name: @UserManager.GetCurrentUser(this.Context).Name
14 |
15 |
16 | User.HasClaim(ClaimTypes.Role, "Administrator"): @User.HasClaim(ClaimTypes.Role, "Administrator")
17 |
18 |
19 | User.HasClaim("Permission", "DoEverything"): @User.HasClaim("Permission", "DoEverything")
20 |
21 | }
22 | @if (this.User.Identity.IsAuthenticated)
23 | {
24 |
29 | }
30 | else
31 | {
32 |
37 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Startup.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using AspNetCoreCustomUserManager.Data;
6 | using Microsoft.AspNetCore.Authentication.Cookies;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.Configuration;
11 | using Microsoft.Extensions.DependencyInjection;
12 | using Microsoft.Extensions.Hosting;
13 |
14 | namespace AspNetCoreCustomUserManager
15 | {
16 | public class Startup
17 | {
18 | public IConfiguration Configuration { get; }
19 |
20 | public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
21 | {
22 | this.Configuration = configuration;
23 | }
24 |
25 | public void ConfigureServices(IServiceCollection services)
26 | {
27 | services.AddDbContext(
28 | options => options.UseSqlite(this.Configuration.GetConnectionString("DefaultConnection"))
29 | );
30 |
31 | services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
32 | .AddCookie(options =>
33 | {
34 | options.ExpireTimeSpan = TimeSpan.FromDays(7);
35 | }
36 | );
37 |
38 | services.AddScoped();
39 | services.AddMvc();
40 | }
41 |
42 | public void Configure(IApplicationBuilder applicationBuilder, IWebHostEnvironment webHostEnvironment)
43 | {
44 | if (webHostEnvironment.IsDevelopment())
45 | applicationBuilder.UseDeveloperExceptionPage();
46 |
47 | applicationBuilder.UseAuthentication();
48 | applicationBuilder.UseStaticFiles();
49 | applicationBuilder.UseRouting();
50 | applicationBuilder.UseEndpoints(builder => builder.MapDefaultControllerRoute());
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/IUserManager.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System.Threading.Tasks;
5 | using AspNetCoreCustomUserManager.Models;
6 | using Microsoft.AspNetCore.Http;
7 |
8 | namespace AspNetCoreCustomUserManager
9 | {
10 | public enum SignUpResultError
11 | {
12 | CredentialTypeNotFound
13 | }
14 |
15 | public class SignUpResult
16 | {
17 | public User User { get; set; }
18 | public bool Success { get; set; }
19 | public SignUpResultError? Error { get; set; }
20 |
21 | public SignUpResult(User user = null, bool success = false, SignUpResultError? error = null)
22 | {
23 | this.User = user;
24 | this.Success = success;
25 | this.Error = error;
26 | }
27 | }
28 |
29 | public enum ValidateResultError
30 | {
31 | CredentialTypeNotFound,
32 | CredentialNotFound,
33 | SecretNotValid
34 | }
35 |
36 | public class ValidateResult
37 | {
38 | public User User { get; set; }
39 | public bool Success { get; set; }
40 | public ValidateResultError? Error { get; set; }
41 |
42 | public ValidateResult(User user = null, bool success = false, ValidateResultError? error = null)
43 | {
44 | this.User = user;
45 | this.Success = success;
46 | this.Error = error;
47 | }
48 | }
49 |
50 | public enum ChangeSecretResultError
51 | {
52 | CredentialTypeNotFound,
53 | CredentialNotFound
54 | }
55 |
56 | public class ChangeSecretResult
57 | {
58 | public bool Success { get; set; }
59 | public ChangeSecretResultError? Error { get; set; }
60 |
61 | public ChangeSecretResult(bool success = false, ChangeSecretResultError? error = null)
62 | {
63 | this.Success = success;
64 | this.Error = error;
65 | }
66 | }
67 |
68 | public interface IUserManager
69 | {
70 | SignUpResult SignUp(string name, string credentialTypeCode, string identifier);
71 | SignUpResult SignUp(string name, string credentialTypeCode, string identifier, string secret);
72 | void AddToRole(User user, string roleCode);
73 | void AddToRole(User user, Role role);
74 | void RemoveFromRole(User user, string roleCode);
75 | void RemoveFromRole(User user, Role role);
76 | ChangeSecretResult ChangeSecret(string credentialTypeCode, string identifier, string secret);
77 | ValidateResult Validate(string credentialTypeCode, string identifier);
78 | ValidateResult Validate(string credentialTypeCode, string identifier, string secret);
79 | Task SignIn(HttpContext httpContext, User user, bool isPersistent = false);
80 | Task SignOut(HttpContext httpContext);
81 | int GetCurrentUserId(HttpContext httpContext);
82 | User GetCurrentUser(HttpContext httpContext);
83 | }
84 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/Data/Storage.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using AspNetCoreCustomUserManager.Models;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace AspNetCoreCustomUserManager.Data
8 | {
9 | public class Storage : DbContext
10 | {
11 | public DbSet Users { get; set; }
12 | public DbSet CredentialTypes { get; set; }
13 | public DbSet Credentials { get; set; }
14 | public DbSet Roles { get; set; }
15 | public DbSet UserRoles { get; set; }
16 | public DbSet Permissions { get; set; }
17 | public DbSet RolePermissions { get; set; }
18 |
19 | public Storage(DbContextOptions options) : base(options) { }
20 |
21 | protected override void OnModelCreating(ModelBuilder modelBuilder)
22 | {
23 | base.OnModelCreating(modelBuilder);
24 | modelBuilder.Entity(etb =>
25 | {
26 | etb.HasKey(e => e.Id);
27 | etb.Property(e => e.Id).ValueGeneratedOnAdd();
28 | etb.Property(e => e.Name).IsRequired().HasMaxLength(64);
29 | etb.ToTable("Users");
30 | }
31 | );
32 |
33 | modelBuilder.Entity(etb =>
34 | {
35 | etb.HasKey(e => e.Id);
36 | etb.Property(e => e.Id).ValueGeneratedOnAdd();
37 | etb.Property(e => e.Code).IsRequired().HasMaxLength(32);
38 | etb.Property(e => e.Name).IsRequired().HasMaxLength(64);
39 | etb.ToTable("CredentialTypes");
40 | }
41 | );
42 |
43 | modelBuilder.Entity(etb =>
44 | {
45 | etb.HasKey(e => e.Id);
46 | etb.Property(e => e.Id).ValueGeneratedOnAdd();
47 | etb.Property(e => e.Identifier).IsRequired().HasMaxLength(64);
48 | etb.Property(e => e.Secret).HasMaxLength(1024);
49 | etb.ToTable("Credentials");
50 | }
51 | );
52 |
53 | modelBuilder.Entity(etb =>
54 | {
55 | etb.HasKey(e => e.Id);
56 | etb.Property(e => e.Id).ValueGeneratedOnAdd();
57 | etb.Property(e => e.Code).IsRequired().HasMaxLength(32);
58 | etb.Property(e => e.Name).IsRequired().HasMaxLength(64);
59 | etb.ToTable("Roles");
60 | }
61 | );
62 |
63 | modelBuilder.Entity(etb =>
64 | {
65 | etb.HasKey(e => new { e.UserId, e.RoleId });
66 | etb.ToTable("UserRoles");
67 | }
68 | );
69 |
70 | modelBuilder.Entity(etb =>
71 | {
72 | etb.HasKey(e => e.Id);
73 | etb.Property(e => e.Id).ValueGeneratedOnAdd();
74 | etb.Property(e => e.Code).IsRequired().HasMaxLength(32);
75 | etb.Property(e => e.Name).IsRequired().HasMaxLength(64);
76 | etb.ToTable("Permissions");
77 | }
78 | );
79 |
80 | modelBuilder.Entity(etb =>
81 | {
82 | etb.HasKey(e => new { e.RoleId, e.PermissionId });
83 | etb.ToTable("RolePermissions");
84 | }
85 | );
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/AspNetCoreCustomUserManager/UserManager.cs:
--------------------------------------------------------------------------------
1 | // Copyright © 2017 Dmitry Sikorsky. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Security.Claims;
8 | using System.Threading.Tasks;
9 | using AspNetCoreCustomUserManager.Data;
10 | using AspNetCoreCustomUserManager.Models;
11 | using Microsoft.AspNetCore.Authentication;
12 | using Microsoft.AspNetCore.Authentication.Cookies;
13 | using Microsoft.AspNetCore.Http;
14 |
15 | namespace AspNetCoreCustomUserManager
16 | {
17 | public class UserManager : IUserManager
18 | {
19 | private Storage storage;
20 |
21 | public UserManager(Storage storage)
22 | {
23 | this.storage = storage;
24 | }
25 |
26 | public SignUpResult SignUp(string name, string credentialTypeCode, string identifier)
27 | {
28 | return this.SignUp(name, credentialTypeCode, identifier, null);
29 | }
30 |
31 | public SignUpResult SignUp(string name, string credentialTypeCode, string identifier, string secret)
32 | {
33 | User user = new User();
34 |
35 | user.Name = name;
36 | user.Created = DateTime.Now;
37 | this.storage.Users.Add(user);
38 | this.storage.SaveChanges();
39 |
40 | CredentialType credentialType = this.storage.CredentialTypes.FirstOrDefault(ct => ct.Code.ToLower() == credentialTypeCode.ToLower());
41 |
42 | if (credentialType == null)
43 | return new SignUpResult(success: false, error: SignUpResultError.CredentialTypeNotFound);
44 |
45 | Credential credential = new Credential();
46 |
47 | credential.UserId = user.Id;
48 | credential.CredentialTypeId = credentialType.Id;
49 | credential.Identifier = identifier;
50 |
51 | if (!string.IsNullOrEmpty(secret))
52 | {
53 | byte[] salt = Pbkdf2Hasher.GenerateRandomSalt();
54 | string hash = Pbkdf2Hasher.ComputeHash(secret, salt);
55 |
56 | credential.Secret = hash;
57 | credential.Extra = Convert.ToBase64String(salt);
58 | }
59 |
60 | this.storage.Credentials.Add(credential);
61 | this.storage.SaveChanges();
62 | return new SignUpResult(user: user, success: true);
63 | }
64 |
65 | public void AddToRole(User user, string roleCode)
66 | {
67 | Role role = this.storage.Roles.FirstOrDefault(r => r.Code.ToLower() == roleCode.ToLower());
68 |
69 | if (role == null)
70 | return;
71 |
72 | this.AddToRole(user, role);
73 | }
74 |
75 | public void AddToRole(User user, Role role)
76 | {
77 | UserRole userRole = this.storage.UserRoles.Find(user.Id, role.Id);
78 |
79 | if (userRole != null)
80 | return;
81 |
82 | userRole = new UserRole();
83 | userRole.UserId = user.Id;
84 | userRole.RoleId = role.Id;
85 | this.storage.UserRoles.Add(userRole);
86 | this.storage.SaveChanges();
87 | }
88 |
89 | public void RemoveFromRole(User user, string roleCode)
90 | {
91 | Role role = this.storage.Roles.FirstOrDefault(r => r.Code.ToLower() == roleCode.ToLower());
92 |
93 | if (role == null)
94 | return;
95 |
96 | this.RemoveFromRole(user, role);
97 | }
98 |
99 | public void RemoveFromRole(User user, Role role)
100 | {
101 | UserRole userRole = this.storage.UserRoles.Find(user.Id, role.Id);
102 |
103 | if (userRole == null)
104 | return;
105 |
106 | this.storage.UserRoles.Remove(userRole);
107 | this.storage.SaveChanges();
108 | }
109 |
110 | public ChangeSecretResult ChangeSecret(string credentialTypeCode, string identifier, string secret)
111 | {
112 | CredentialType credentialType = this.storage.CredentialTypes.FirstOrDefault(ct => ct.Code.ToLower() == credentialTypeCode.ToLower());
113 |
114 | if (credentialType == null)
115 | return new ChangeSecretResult(success: false, error: ChangeSecretResultError.CredentialTypeNotFound);
116 |
117 | Credential credential = this.storage.Credentials.FirstOrDefault(c => c.CredentialTypeId == credentialType.Id && c.Identifier == identifier);
118 |
119 | if (credential == null)
120 | return new ChangeSecretResult(success: false, error: ChangeSecretResultError.CredentialNotFound);
121 |
122 | byte[] salt = Pbkdf2Hasher.GenerateRandomSalt();
123 | string hash = Pbkdf2Hasher.ComputeHash(secret, salt);
124 |
125 | credential.Secret = hash;
126 | credential.Extra = Convert.ToBase64String(salt);
127 | this.storage.Credentials.Update(credential);
128 | this.storage.SaveChanges();
129 | return new ChangeSecretResult(success: true);
130 | }
131 |
132 | public ValidateResult Validate(string credentialTypeCode, string identifier)
133 | {
134 | return this.Validate(credentialTypeCode, identifier, null);
135 | }
136 |
137 | public ValidateResult Validate(string credentialTypeCode, string identifier, string secret)
138 | {
139 | CredentialType credentialType = this.storage.CredentialTypes.FirstOrDefault(ct => ct.Code.ToLower() == credentialTypeCode.ToLower());
140 |
141 | if (credentialType == null)
142 | return new ValidateResult(success: false, error: ValidateResultError.CredentialTypeNotFound);
143 |
144 | Credential credential = this.storage.Credentials.FirstOrDefault(c => c.CredentialTypeId == credentialType.Id && c.Identifier == identifier);
145 |
146 | if (credential == null)
147 | return new ValidateResult(success: false, error: ValidateResultError.CredentialNotFound);
148 |
149 | if (!string.IsNullOrEmpty(secret))
150 | {
151 | byte[] salt = Convert.FromBase64String(credential.Extra);
152 | string hash = Pbkdf2Hasher.ComputeHash(secret, salt);
153 |
154 | if (credential.Secret != hash)
155 | return new ValidateResult(success: false, error: ValidateResultError.SecretNotValid);
156 | }
157 |
158 | return new ValidateResult(user: this.storage.Users.Find(credential.UserId), success: true);
159 | }
160 |
161 | public async Task SignIn(HttpContext httpContext, User user, bool isPersistent = false)
162 | {
163 | ClaimsIdentity identity = new ClaimsIdentity(this.GetUserClaims(user), CookieAuthenticationDefaults.AuthenticationScheme);
164 | ClaimsPrincipal principal = new ClaimsPrincipal(identity);
165 |
166 | await httpContext.SignInAsync(
167 | CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties() { IsPersistent = isPersistent }
168 | );
169 | }
170 |
171 | public async Task SignOut(HttpContext httpContext)
172 | {
173 | await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
174 | }
175 |
176 | public int GetCurrentUserId(HttpContext httpContext)
177 | {
178 | if (!httpContext.User.Identity.IsAuthenticated)
179 | return -1;
180 |
181 | Claim claim = httpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
182 |
183 | if (claim == null)
184 | return -1;
185 |
186 | int currentUserId;
187 |
188 | if (!int.TryParse(claim.Value, out currentUserId))
189 | return -1;
190 |
191 | return currentUserId;
192 | }
193 |
194 | public User GetCurrentUser(HttpContext httpContext)
195 | {
196 | int currentUserId = this.GetCurrentUserId(httpContext);
197 |
198 | if (currentUserId == -1)
199 | return null;
200 |
201 | return this.storage.Users.Find(currentUserId);
202 | }
203 |
204 | private IEnumerable GetUserClaims(User user)
205 | {
206 | List claims = new List();
207 |
208 | claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
209 | claims.Add(new Claim(ClaimTypes.Name, user.Name));
210 | claims.AddRange(this.GetUserRoleClaims(user));
211 | return claims;
212 | }
213 |
214 | private IEnumerable GetUserRoleClaims(User user)
215 | {
216 | List claims = new List();
217 | IEnumerable roleIds = this.storage.UserRoles.Where(ur => ur.UserId == user.Id).Select(ur => ur.RoleId).ToList();
218 |
219 | if (roleIds != null)
220 | {
221 | foreach (int roleId in roleIds)
222 | {
223 | Role role = this.storage.Roles.Find(roleId);
224 |
225 | claims.Add(new Claim(ClaimTypes.Role, role.Code));
226 | claims.AddRange(this.GetUserPermissionClaims(role));
227 | }
228 | }
229 |
230 | return claims;
231 | }
232 |
233 | private IEnumerable GetUserPermissionClaims(Role role)
234 | {
235 | List claims = new List();
236 | IEnumerable permissionIds = this.storage.RolePermissions.Where(rp => rp.RoleId == role.Id).Select(rp => rp.PermissionId).ToList();
237 |
238 | if (permissionIds != null)
239 | {
240 | foreach (int permissionId in permissionIds)
241 | {
242 | Permission permission = this.storage.Permissions.Find(permissionId);
243 |
244 | claims.Add(new Claim("Permission", permission.Code));
245 | }
246 | }
247 |
248 | return claims;
249 | }
250 | }
251 | }
--------------------------------------------------------------------------------