Skip to content

Commit ceefb84

Browse files
committed
feat: Add IdentityServer
- add and setup IdentityServer for login, registration, logout - add mvc project to test identity server in simple way
1 parent b124848 commit ceefb84

File tree

98 files changed

+75460
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+75460
-11
lines changed

CoinyProject.sln

+14-7
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinyProject.Infrastructure
2121
EndProject
2222
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinyProject.Shared", "src\CoinyProject.Shared\CoinyProject.Shared.csproj", "{70C1241D-C297-4D3C-B26F-B7AA953EA24C}"
2323
EndProject
24-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinyProject.IdentityServer", "src\CoinyProject.IdentityServer\CoinyProject.IdentityServer.csproj", "{15B0F024-EB65-42A9-897D-9232CE14EA7B}"
25-
EndProject
2624
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoinyProject.UnitTests", "tests\CoinyProject.Application.AlbumService.Tests\CoinyProject.UnitTests.csproj", "{E76EE636-DCC4-4954-AAB2-A099FDE0728F}"
2725
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoinyProject.IdentityServer", "src\CoinyProject.IdentityServer\CoinyProject.IdentityServer.csproj", "{6FB01F55-5016-457F-9D0E-F42F5928D39A}"
27+
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mvc", "src\mvc\mvc.csproj", "{7A01A352-EF24-4D73-BD06-E74F1B9DB12F}"
29+
EndProject
2830
Global
2931
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3032
Debug|Any CPU = Debug|Any CPU
@@ -59,14 +61,18 @@ Global
5961
{70C1241D-C297-4D3C-B26F-B7AA953EA24C}.Debug|Any CPU.Build.0 = Debug|Any CPU
6062
{70C1241D-C297-4D3C-B26F-B7AA953EA24C}.Release|Any CPU.ActiveCfg = Release|Any CPU
6163
{70C1241D-C297-4D3C-B26F-B7AA953EA24C}.Release|Any CPU.Build.0 = Release|Any CPU
62-
{15B0F024-EB65-42A9-897D-9232CE14EA7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63-
{15B0F024-EB65-42A9-897D-9232CE14EA7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
64-
{15B0F024-EB65-42A9-897D-9232CE14EA7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
65-
{15B0F024-EB65-42A9-897D-9232CE14EA7B}.Release|Any CPU.Build.0 = Release|Any CPU
6664
{E76EE636-DCC4-4954-AAB2-A099FDE0728F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
6765
{E76EE636-DCC4-4954-AAB2-A099FDE0728F}.Debug|Any CPU.Build.0 = Debug|Any CPU
6866
{E76EE636-DCC4-4954-AAB2-A099FDE0728F}.Release|Any CPU.ActiveCfg = Release|Any CPU
6967
{E76EE636-DCC4-4954-AAB2-A099FDE0728F}.Release|Any CPU.Build.0 = Release|Any CPU
68+
{6FB01F55-5016-457F-9D0E-F42F5928D39A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69+
{6FB01F55-5016-457F-9D0E-F42F5928D39A}.Debug|Any CPU.Build.0 = Debug|Any CPU
70+
{6FB01F55-5016-457F-9D0E-F42F5928D39A}.Release|Any CPU.ActiveCfg = Release|Any CPU
71+
{6FB01F55-5016-457F-9D0E-F42F5928D39A}.Release|Any CPU.Build.0 = Release|Any CPU
72+
{7A01A352-EF24-4D73-BD06-E74F1B9DB12F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
73+
{7A01A352-EF24-4D73-BD06-E74F1B9DB12F}.Debug|Any CPU.Build.0 = Debug|Any CPU
74+
{7A01A352-EF24-4D73-BD06-E74F1B9DB12F}.Release|Any CPU.ActiveCfg = Release|Any CPU
75+
{7A01A352-EF24-4D73-BD06-E74F1B9DB12F}.Release|Any CPU.Build.0 = Release|Any CPU
7076
EndGlobalSection
7177
GlobalSection(SolutionProperties) = preSolution
7278
HideSolutionNode = FALSE
@@ -79,8 +85,9 @@ Global
7985
{43B37B4B-9052-46D5-845D-AB21E74D5057} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
8086
{ADEE36F4-01B3-46F9-A1EB-D3B9BE2D4A7A} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
8187
{70C1241D-C297-4D3C-B26F-B7AA953EA24C} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
82-
{15B0F024-EB65-42A9-897D-9232CE14EA7B} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
8388
{E76EE636-DCC4-4954-AAB2-A099FDE0728F} = {20847965-4158-4A31-B5A6-C1F15024776C}
89+
{6FB01F55-5016-457F-9D0E-F42F5928D39A} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
90+
{7A01A352-EF24-4D73-BD06-E74F1B9DB12F} = {518A20A7-1C7F-4E74-8D37-34C56740DFBF}
8491
EndGlobalSection
8592
GlobalSection(ExtensibilityGlobals) = postSolution
8693
SolutionGuid = {F9FC4428-8E26-4BB5-A59F-8357620396C6}

src/CoinyProject.Core.Domain/Entities/User.cs

+7
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ namespace CoinyProject.Core.Domain.Entities
99
{
1010
public class User : IdentityUser
1111
{
12+
1213
public string FirstName { get; set; }
1314
public string LastName { get; set; }
15+
public User(string userName, string firstName, string lastName) : base(userName)
16+
{
17+
FirstName = firstName;
18+
LastName = lastName;
19+
Email = userName;
20+
}
1421

1522
public ICollection<Album>? Albums { get; set; }
1623
public ICollection<AuctionBet> AuctionBets { get; set; }
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>net8.0</TargetFramework>
5-
<ImplicitUsings>enable</ImplicitUsings>
65
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="IdentityServer4" Version="4.1.2" />
11+
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
12+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
</PackageReference>
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="..\CoinyProject.Infrastructure.Data\CoinyProject.Infrastructure.Data.csproj" />
20+
<ProjectReference Include="..\CoinyProject.Shared\CoinyProject.Shared.csproj" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<Compile Update="Properties\Resources.Designer.cs">
25+
<DesignTime>True</DesignTime>
26+
<AutoGen>True</AutoGen>
27+
<DependentUpon>Resources.resx</DependentUpon>
28+
</Compile>
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<EmbeddedResource Update="Properties\Resources.resx">
33+
<Generator>ResXFileCodeGenerator</Generator>
34+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
35+
</EmbeddedResource>
36+
</ItemGroup>
37+
938
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using IdentityServer4;
2+
using IdentityServer4.Models;
3+
4+
namespace CoinyProject.IdentityServer
5+
{
6+
public static class Configuration
7+
{
8+
public static IEnumerable<ApiResource> GetApis() =>
9+
new List<ApiResource>
10+
{};
11+
public static IEnumerable<IdentityResource> GetIdentityResources() =>
12+
new List<IdentityResource>
13+
{
14+
new IdentityResources.OpenId(),
15+
new IdentityResources.Profile(),
16+
17+
};
18+
19+
public static IEnumerable<Client> GetClients() =>
20+
new List<Client>
21+
{
22+
new Client
23+
{
24+
ClientId = "client_id_mvc",
25+
ClientSecrets = { new Secret("client_secret_mvc".Sha256()) },
26+
//7184
27+
//7115 mvc
28+
AllowedGrantTypes = GrantTypes.Code,
29+
RedirectUris = { "https://localhost:7115/signin-oidc" },
30+
PostLogoutRedirectUris = { "https://localhost:7115/Home/Index" },
31+
AllowedScopes = {
32+
IdentityServerConstants.StandardScopes.OpenId,
33+
IdentityServerConstants.StandardScopes.Profile
34+
},
35+
/*AlwaysIncludeUserClaimsInIdToken = true,*/
36+
RequireConsent = false
37+
},
38+
new Client
39+
{
40+
ClientId = "client_id_ui",
41+
ClientSecrets = { new Secret("client_secret_ui".Sha256()) },
42+
//7184
43+
//7115 mvc
44+
AllowedGrantTypes = GrantTypes.Code,
45+
RedirectUris = { "https://localhost:7184/signin-oidc" },
46+
PostLogoutRedirectUris = { "https://localhost:7184/Home/Index" },
47+
AllowedScopes = {
48+
IdentityServerConstants.StandardScopes.OpenId,
49+
IdentityServerConstants.StandardScopes.Profile
50+
},
51+
AlwaysIncludeUserClaimsInIdToken = true,
52+
RequireConsent = false
53+
}
54+
};
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using CoinyProject.Core.Domain.Entities;
2+
using CoinyProject.IdentityServer.Models;
3+
using IdentityServer4.Services;
4+
using Microsoft.AspNetCore.Identity;
5+
using Microsoft.AspNetCore.Mvc;
6+
7+
namespace CoinyProject.IdentityServer.Controllers
8+
{
9+
public class AuthController : Controller
10+
{
11+
private readonly SignInManager<User> _signInManager;
12+
private readonly UserManager<User> _userManager;
13+
IIdentityServerInteractionService _interactionService;
14+
public AuthController(SignInManager<User> signInManager, UserManager<User> userManager,
15+
IIdentityServerInteractionService interactionService)
16+
{
17+
_signInManager = signInManager;
18+
_userManager = userManager;
19+
_interactionService = interactionService;
20+
}
21+
public async Task<IActionResult> Logout(string logoutId)
22+
{
23+
await _signInManager.SignOutAsync();
24+
25+
var logoutRequest = await _interactionService.GetLogoutContextAsync(logoutId);
26+
27+
if (string.IsNullOrEmpty(logoutRequest.PostLogoutRedirectUri))
28+
{
29+
return RedirectToAction("Home", "Index");
30+
}
31+
return Redirect(logoutRequest.PostLogoutRedirectUri);
32+
33+
}
34+
35+
public IActionResult Login(string returnUrl)
36+
{
37+
return View(new LoginViewModel { ReturnUrl = returnUrl});
38+
}
39+
[HttpPost]
40+
public async Task<IActionResult> Login(LoginViewModel model)
41+
{
42+
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, false, false);
43+
44+
if (result.Succeeded)
45+
{
46+
return Redirect(model.ReturnUrl);
47+
}
48+
return BadRequest();
49+
}
50+
51+
public IActionResult Register(string returnUrl)
52+
{
53+
return View(new RegisterViewModel { ReturnUrl = returnUrl });
54+
}
55+
[HttpPost]
56+
public async Task<IActionResult> Register(RegisterViewModel model)
57+
{
58+
if(!ModelState.IsValid)
59+
{
60+
return View(model);
61+
}
62+
63+
var user = new User(model.Email, model.Firstname, model.Lastname);
64+
var result = await _userManager.CreateAsync(user, model.Password);
65+
if (result.Succeeded)
66+
{
67+
await _signInManager.SignInAsync(user, false);
68+
return Redirect(model.ReturnUrl);
69+
}
70+
return BadRequest();
71+
}
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace CoinyProject.IdentityServer.Models
2+
{
3+
public class LoginViewModel
4+
{
5+
public string Username { get; set; }
6+
public string Password { get; set; }
7+
public string ReturnUrl { get; set; }
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace CoinyProject.IdentityServer.Models
4+
{
5+
public class RegisterViewModel
6+
{
7+
[Required]
8+
public string Firstname { get; set; }
9+
[Required]
10+
public string Lastname { get; set; }
11+
[Required]
12+
[DataType(DataType.EmailAddress)]
13+
public string Email { get; set; }
14+
[Required]
15+
[DataType(DataType.Password)]
16+
public string Password { get; set; }
17+
[Required]
18+
[DataType(DataType.Password)]
19+
[Compare("Password", ErrorMessage = "Passwords do not match")]
20+
public string ConfirmPassword { get; set; }
21+
public string ReturnUrl { get; set; }
22+
}
23+
}
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using CoinyProject.Core.Domain.Entities;
2+
using CoinyProject.IdentityServer;
3+
using CoinyProject.Infrastructure.Data;
4+
using CoinyProject.Shared.Extensions;
5+
using Microsoft.AspNetCore.Authentication.Cookies;
6+
using Microsoft.AspNetCore.Identity;
7+
using Microsoft.EntityFrameworkCore;
8+
using Microsoft.Extensions.Configuration;
9+
10+
var builder = WebApplication.CreateBuilder(args);
11+
12+
builder.Services.AddControllersWithViews();
13+
14+
builder.Services.AddDBConnection(builder.Configuration);
15+
builder.Services.ConfigurateIdentityOptions();
16+
builder.Services.AddIdentityUser();
17+
18+
19+
builder.Services.ConfigureApplicationCookie(options =>
20+
{
21+
options.Cookie.Name = "CoinyProject.IdentityServer.Cookie";
22+
options.LoginPath = "/Auth/Login";
23+
options.LogoutPath = "/Auth/Logout";
24+
});
25+
26+
27+
var migrationsAssembly = typeof(Program).Assembly.GetName().Name;
28+
29+
builder.Services.AddIdentityServer()
30+
.AddAspNetIdentity<User>()
31+
/*.AddConfigurationStore(options =>
32+
{
33+
options.ConfigureDbContext = b =>
34+
b.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"),
35+
sql => sql.MigrationsAssembly(migrationsAssembly));
36+
})
37+
.AddOperationalStore(options =>
38+
{
39+
options.ConfigureDbContext = b =>
40+
b.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"),
41+
sql => sql.MigrationsAssembly(migrationsAssembly));
42+
})*/
43+
.AddInMemoryApiResources(Configuration.GetApis())
44+
.AddInMemoryClients(Configuration.GetClients())
45+
.AddInMemoryIdentityResources(Configuration.GetIdentityResources())
46+
.AddDeveloperSigningCredential();
47+
48+
49+
var app = builder.Build();
50+
51+
// Configure the HTTP request pipeline.
52+
if (!app.Environment.IsDevelopment())
53+
{
54+
app.UseExceptionHandler("/Home/Error");
55+
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
56+
app.UseHsts();
57+
}
58+
59+
using var scope = app.Services.CreateScope();
60+
{
61+
var userManager = scope.ServiceProvider
62+
.GetRequiredService<UserManager<User>>();
63+
}
64+
65+
app.UseRouting();
66+
app.UseIdentityServer();
67+
app.UseAuthentication();
68+
69+
70+
app.UseEndpoints(endpoints =>
71+
{
72+
endpoints.MapDefaultControllerRoute();
73+
});
74+
75+
app.Run();

0 commit comments

Comments
 (0)