Skip to content

Commit 917d263

Browse files
committed
Duende Identity Server
1 parent 87110e6 commit 917d263

File tree

16 files changed

+235
-79
lines changed

16 files changed

+235
-79
lines changed

src/IdentityServer/Duende/ClassifiedAds.Application/ClassifiedAds.Application.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<AnalysisMode>Recommended</AnalysisMode>
66
<AnalysisModeSecurity>All</AnalysisModeSecurity>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
11-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
10+
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.1" />
11+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
1212
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
1313
<PrivateAssets>all</PrivateAssets>
1414
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<AnalysisMode>Recommended</AnalysisMode>
66
<AnalysisModeSecurity>All</AnalysisModeSecurity>
77
</PropertyGroup>
@@ -11,7 +11,7 @@
1111
<PrivateAssets>all</PrivateAssets>
1212
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1313
</PackageReference>
14-
<PackageReference Include="System.Text.Json" Version="8.0.0" />
14+
<PackageReference Include="System.Text.Json" Version="9.0.1" />
1515
</ItemGroup>
1616

1717
</Project>

src/IdentityServer/Duende/ClassifiedAds.Domain/ClassifiedAds.Domain.csproj

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<AnalysisMode>Recommended</AnalysisMode>
66
<AnalysisModeSecurity>All</AnalysisModeSecurity>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
10+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
1111
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
1212
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
1313
<PrivateAssets>all</PrivateAssets>
1414
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1515
</PackageReference>
1616
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
17-
<PackageReference Include="System.Text.Json" Version="8.0.0" />
1817
</ItemGroup>
1918

2019
<ItemGroup>

src/IdentityServer/Duende/ClassifiedAds.Domain/Entities/User.cs

+2
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@ public class User : Entity<Guid>, IAggregateRoot
3939

4040
public IList<UserRole> UserRoles { get; set; }
4141

42+
public IList<UserLogin> UserLogins { get; set; }
43+
4244
public IList<PasswordHistory> PasswordHistories { get; set; }
4345
}

src/IdentityServer/Duende/ClassifiedAds.Domain/Entities/UserClaim.cs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace ClassifiedAds.Domain.Entities;
55
public class UserClaim : Entity<Guid>
66
{
77
public string Type { get; set; }
8+
89
public string Value { get; set; }
910

1011
public User User { get; set; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace ClassifiedAds.Domain.Entities;
4+
5+
public class UserLogin : Entity<Guid>, IAggregateRoot
6+
{
7+
public Guid UserId { get; set; }
8+
9+
public string LoginProvider { get; set; }
10+
11+
public string ProviderKey { get; set; }
12+
13+
public string ProviderDisplayName { get; set; }
14+
15+
public User User { get; set; }
16+
}

src/IdentityServer/Duende/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<AnalysisMode>Recommended</AnalysisMode>
77
<AnalysisModeSecurity>All</AnalysisModeSecurity>
88
<UserSecretsId>aae914a2-80ef-4814-891a-8ed4e63c8c79</UserSecretsId>
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="6.3.7" />
13-
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.0" />
14-
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.0" />
15-
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.0" />
16-
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.0" />
17-
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
12+
<PackageReference Include="AutoMapper" Version="13.0.1" />
13+
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.1.0" />
14+
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="9.0.1" />
15+
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.1" />
16+
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="9.0.1" />
17+
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.1" />
18+
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
1819
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
1920
<PrivateAssets>all</PrivateAssets>
2021
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

src/IdentityServer/Duende/ClassifiedAds.IdentityServer/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
22
WORKDIR /ClassifiedAds.IdentityServer
33

44
# Copy csproj and restore as distinct layers
@@ -17,7 +17,7 @@ COPY . ./
1717
RUN dotnet publish ./ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj -c Release -o out
1818

1919
# Build runtime image
20-
FROM mcr.microsoft.com/dotnet/aspnet:8.0
20+
FROM mcr.microsoft.com/dotnet/aspnet:9.0
2121
WORKDIR /ClassifiedAds.IdentityServer
2222
COPY --from=build-env /ClassifiedAds.IdentityServer/out .
2323

src/IdentityServer/Duende/ClassifiedAds.IdentityServer/Pages/ExternalLogin/Callback.cshtml.cs

+73-23
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public Callback(
3535
_logger = logger;
3636
_events = events;
3737
}
38-
38+
3939
public async Task<IActionResult> OnGet()
4040
{
4141
// read external identity from the temporary cookie
@@ -57,11 +57,32 @@ public async Task<IActionResult> OnGet()
5757
// try to determine the unique id of the external user (issued by the provider)
5858
// the most common claim type for that are the sub claim and the NameIdentifier
5959
// depending on the external provider, some other claim type might be used
60-
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
61-
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
62-
throw new Exception("Unknown userid");
6360

6461
var provider = result.Properties.Items["scheme"];
62+
63+
Claim userIdClaim = null;
64+
65+
if (provider == "AAD")
66+
{
67+
userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier);
68+
}
69+
else if (provider == "Microsoft")
70+
{
71+
userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier);
72+
}
73+
else if (provider == "Google")
74+
{
75+
userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier);
76+
}
77+
else if (provider == "Facebook")
78+
{
79+
userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier);
80+
}
81+
else
82+
{
83+
userIdClaim = externalUser.FindFirst(ClaimTypes.NameIdentifier);
84+
}
85+
6586
var providerUserId = userIdClaim.Value;
6687

6788
// find external user
@@ -109,22 +130,35 @@ public async Task<IActionResult> OnGet()
109130

110131
private async Task<User> AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable<Claim> claims)
111132
{
112-
var sub = Guid.NewGuid();
113-
114-
var user = new User
115-
{
116-
Id = sub,
117-
UserName = sub.ToString(), // don't need a username, since the user will be using an external provider to login
118-
};
119-
120133
// email
121-
var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ??
122-
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value;
123-
if (email != null)
134+
Claim emailClaim = null;
135+
136+
if (provider == "AAD")
124137
{
125-
user.Email = email;
138+
emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email) ??
139+
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
126140
}
127-
141+
else if (provider == "Microsoft")
142+
{
143+
emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email) ??
144+
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
145+
}
146+
else if (provider == "Google")
147+
{
148+
emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email) ??
149+
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
150+
}
151+
else if (provider == "Facebook")
152+
{
153+
emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email) ??
154+
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
155+
}
156+
else
157+
{
158+
emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email) ??
159+
claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
160+
}
161+
128162
// create a list of claims that we want to transfer into our store
129163
var filtered = new List<Claim>();
130164

@@ -155,17 +189,33 @@ private async Task<User> AutoProvisionUserAsync(string provider, string provider
155189
}
156190
}
157191

158-
var identityResult = await _userManager.CreateAsync(user);
159-
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
192+
var userName = $"{provider}_{providerUserId}";
193+
194+
var user = await _userManager.FindByNameAsync(userName);
195+
196+
if (user == null)
197+
{
198+
user = new User
199+
{
200+
UserName = userName,
201+
Email = emailClaim?.Value,
202+
};
203+
204+
var createResult = await _userManager.CreateAsync(user);
205+
if (!createResult.Succeeded)
206+
{
207+
throw new Exception(createResult.Errors.First().Description);
208+
}
209+
}
160210

161211
if (filtered.Any())
162212
{
163-
identityResult = await _userManager.AddClaimsAsync(user, filtered);
164-
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
213+
var addClaimResult = await _userManager.AddClaimsAsync(user, filtered);
214+
if (!addClaimResult.Succeeded) throw new Exception(addClaimResult.Errors.First().Description);
165215
}
166216

167-
identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider));
168-
if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description);
217+
var addLoginResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider));
218+
if (!addLoginResult.Succeeded) throw new Exception(addLoginResult.Errors.First().Description);
169219

170220
return user;
171221
}

src/IdentityServer/Duende/ClassifiedAds.IdentityServer/appsettings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"Certificate": {
99
"Thumbprint": null,
1010
"Path": "Certs/classifiedads.identityserver.pfx",
11-
"Password": "password1234"
11+
"Password": "password1234",
12+
"X509KeyStorageFlags": "EphemeralKeySet"
1213
}
1314
},
1415
"Logging": {

src/IdentityServer/Duende/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj

+25-25
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<AnalysisMode>Recommended</AnalysisMode>
66
<AnalysisModeSecurity>All</AnalysisModeSecurity>
77
</PropertyGroup>
88

99
<ItemGroup>
1010
<FrameworkReference Include="Microsoft.AspNetCore.App" />
11-
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.3.0" />
12-
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
13-
<PackageReference Include="Azure.Identity" Version="1.10.4" />
11+
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.5.0" />
12+
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
13+
<PackageReference Include="Azure.Identity" Version="1.13.2" />
1414
<PackageReference Include="Castle.Core" Version="5.1.1" />
15-
<PackageReference Include="CryptographyHelper" Version="3.0.0" />
16-
<PackageReference Include="Dapper.StrongName" Version="2.1.24" />
17-
<PackageReference Include="IdentityModel" Version="6.2.0" />
15+
<PackageReference Include="CryptographyHelper" Version="3.1.0" />
16+
<PackageReference Include="Dapper.StrongName" Version="2.1.35" />
17+
<PackageReference Include="IdentityModel" Version="7.0.0" />
1818
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
19-
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.0" />
20-
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.0" />
21-
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="8.0.0" />
22-
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="7.0.0" />
23-
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
24-
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
25-
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.0" />
26-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
27-
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="8.0.0" />
28-
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.0.0" />
29-
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.3.8" />
30-
<PackageReference Include="MiniProfiler.EntityFrameworkCore" Version="4.3.8" />
31-
<PackageReference Include="MiniProfiler.Providers.SqlServer" Version="4.3.8" />
32-
<PackageReference Include="Quartz" Version="3.8.0" />
33-
<PackageReference Include="Serilog" Version="3.1.1" />
34-
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
35-
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.3.0" />
19+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.1" />
20+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.1" />
21+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="9.0.1" />
22+
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0" />
23+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
24+
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" />
25+
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.1" />
26+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
27+
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="9.0.1" />
28+
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="9.0.1" />
29+
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
30+
<PackageReference Include="MiniProfiler.EntityFrameworkCore" Version="4.5.4" />
31+
<PackageReference Include="MiniProfiler.Providers.SqlServer" Version="4.5.4" />
32+
<PackageReference Include="Quartz" Version="3.13.1" />
33+
<PackageReference Include="Serilog" Version="4.2.0" />
34+
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
35+
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
3636
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
3737
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
3838
<PrivateAssets>all</PrivateAssets>
3939
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4040
</PackageReference>
41-
<PackageReference Include="VaultSharp" Version="1.13.0.1" />
41+
<PackageReference Include="VaultSharp" Version="1.17.5.1" />
4242
</ItemGroup>
4343

4444
<ItemGroup>

0 commit comments

Comments
 (0)