Skip to content

Commit

Permalink
OpenIddict
Browse files Browse the repository at this point in the history
  • Loading branch information
phongnguyend committed Dec 29, 2024
1 parent 1b2eaae commit 8b024c9
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ClassifiedAds.Domain.Entities;
using ClassifiedAds.IdentityServer.Extensions;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
Expand All @@ -19,13 +20,16 @@ public class AuthorizationController : Controller
{
private readonly UserManager<User> _userManager;
private readonly SignInManager<User> _signInManager;
private readonly IOpenIddictScopeManager _scopeManager;

public AuthorizationController(
UserManager<User> userManager,
SignInManager<User> signInManager)
SignInManager<User> signInManager,
IOpenIddictScopeManager scopeManager)
{
_userManager = userManager;
_signInManager = signInManager;
_scopeManager = scopeManager;
}

[HttpGet("~/connect/authorize")]
Expand Down Expand Up @@ -66,6 +70,7 @@ public async Task<IActionResult> Authorize()

// Set requested scopes (this is not done automatically)
claimsPrincipal.SetScopes(request.GetScopes());
claimsPrincipal.SetResources(await _scopeManager.ListResourcesAsync(claimsPrincipal.GetScopes()).ToListAsync());

// Signing in with the OpenIddict authentiction scheme trigger OpenIddict to issue a code (which can be exchanged for an access token)
return SignIn(claimsPrincipal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using OpenIddict.Abstractions;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace ClassifiedAds.IdentityServer.Extensions;

public static class PrincipalExtensions
public static class Extensions
{
public static string GetDisplayName(this ClaimsPrincipal principal)
{
Expand All @@ -21,4 +23,12 @@ public static string GetDisplayName(this ClaimsPrincipal principal)

return string.Empty;
}

public static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items)
{
var evaluatedItems = new List<T>();
await foreach (var item in items)
evaluatedItems.Add(item);
return evaluatedItems;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.Extensions.Hosting;
using OpenIddict.Abstractions;
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -20,6 +21,13 @@ public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();

await RegisterApplicationsAsync(scope, cancellationToken);

await RegisterScopesAsync(scope.ServiceProvider);
}

private static async Task RegisterApplicationsAsync(IServiceScope scope, CancellationToken cancellationToken)
{
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();

await UpsertClientApplication(manager, new OpenIddictApplicationDescriptor
Expand Down Expand Up @@ -320,4 +328,30 @@ private static async Task UpsertClientApplication(IOpenIddictApplicationManager
await manager.UpdateAsync(client, openIddictApplicationDescriptor, cancellationToken);
}
}

static async Task RegisterScopesAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictScopeManager>();

var scope = await manager.FindByNameAsync("ClassifiedAds.WebAPI");

if (scope is null)
{
await manager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "ClassifiedAds.WebAPI",
DisplayName = "ClassifiedAds WebAPI",
Resources = { "ClassifiedAds.WebAPI" }
});
}
else
{
await manager.UpdateAsync(scope, new OpenIddictScopeDescriptor
{
Name = "ClassifiedAds.WebAPI",
DisplayName = "ClassifiedAds WebAPI",
Resources = { "ClassifiedAds.WebAPI" }
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ public void ConfigureServices(IServiceCollection services)
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableUserinfoEndpointPassthrough();

options.DisableAccessTokenEncryption();
})
.AddValidation(options =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />
<PackageReference Include="Castle.Core" Version="5.1.1" />
<PackageReference Include="CryptographyHelper" Version="2.0.0" />
<PackageReference Include="CryptographyHelper" Version="3.0.0" />
<PackageReference Include="Dapper.StrongName" Version="2.1.24" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ spec:
spec:
containers:
- env:
- name: IdentityServerAuthentication__Authority
- name: Authentication__IdentityServer__Authority
value: http://{{ .Release.Name}}-identityserver
- name: IdentityServerAuthentication__RequireHttpsMetadata
- name: Authentication__IdentityServer__RequireHttpsMetadata
value: "false"
envFrom:
- configMapRef:
Expand Down
4 changes: 2 additions & 2 deletions src/Monolith/.k8s/webapi.deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ spec:
spec:
containers:
- env:
- name: IdentityServerAuthentication__Authority
- name: Authentication__IdentityServer__Authority
value: http://identityserver
- name: IdentityServerAuthentication__RequireHttpsMetadata
- name: Authentication__IdentityServer__RequireHttpsMetadata
value: "false"
envFrom:
- configMapRef:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class AppSettings

public MonitoringOptions Monitoring { get; set; }

public IdentityServerAuthentication IdentityServerAuthentication { get; set; }
public AuthenticationOptions Authentication { get; set; }

public string AllowedHosts { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@

namespace ClassifiedAds.WebAPI.ConfigurationOptions;

public class IdentityServerAuthentication
public class AuthenticationOptions
{
public string Provider { get; set; }

public IdentityServerOptions IdentityServer { get; set; }

public JwtOptions Jwt { get; set; }
}

public class IdentityServerOptions
{
public string Authority { get; set; }

public string ApiName { get; set; }
public string Audience { get; set; }

public bool RequireHttpsMetadata { get; set; }

public OpenIddictOptions OpenIddict { get; set; }
}

public class OpenIddictOptions
public class JwtOptions
{
public string IssuerUri { get; set; }

public string Audience { get; set; }

public CertificateOption TokenDecryptionCertificate { get; set; }

public CertificateOption IssuerSigningCertificate { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public async Task<ActionResult> SendResetPasswordEmail(Guid id)
if (user != null)
{
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
var resetUrl = $"{_appSettings.IdentityServerAuthentication.Authority}/Account/ResetPassword?token={HttpUtility.UrlEncode(token)}&email={user.Email}";
var resetUrl = $"{_appSettings.Authentication.IdentityServer.Authority}/Account/ResetPassword?token={HttpUtility.UrlEncode(token)}&email={user.Email}";

await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand<EmailMessage>(new EmailMessage
{
Expand Down Expand Up @@ -186,7 +186,7 @@ public async Task<ActionResult> SendConfirmationEmailAddressEmail(Guid id)
{
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);

var confirmationEmail = $"{_appSettings.IdentityServerAuthentication.Authority}/Account/ConfirmEmailAddress?token={HttpUtility.UrlEncode(token)}&email={user.Email}";
var confirmationEmail = $"{_appSettings.Authentication.IdentityServer.Authority}/Account/ConfirmEmailAddress?token={HttpUtility.UrlEncode(token)}&email={user.Email}";

await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand<EmailMessage>(new EmailMessage
{
Expand Down
28 changes: 14 additions & 14 deletions src/Monolith/ClassifiedAds.WebAPI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,26 @@

services.AddAuthentication(options =>
{
options.DefaultScheme = appSettings.IdentityServerAuthentication.Provider switch
options.DefaultScheme = appSettings.Authentication.Provider switch
{
"OpenIddict" => "OpenIddict",
"Jwt" => "Jwt",
_ => JwtBearerDefaults.AuthenticationScheme
};
})
.AddJwtBearer(options =>
{
options.Authority = appSettings.IdentityServerAuthentication.Authority;
options.Audience = appSettings.IdentityServerAuthentication.ApiName;
options.RequireHttpsMetadata = appSettings.IdentityServerAuthentication.RequireHttpsMetadata;
options.Authority = appSettings.Authentication.IdentityServer.Authority;
options.Audience = appSettings.Authentication.IdentityServer.Audience;
options.RequireHttpsMetadata = appSettings.Authentication.IdentityServer.RequireHttpsMetadata;
})
.AddJwtBearer("OpenIddict", options =>
.AddJwtBearer("Jwt", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidIssuer = appSettings.IdentityServerAuthentication.OpenIddict.IssuerUri,
TokenDecryptionKey = new X509SecurityKey(appSettings.IdentityServerAuthentication.OpenIddict.TokenDecryptionCertificate.FindCertificate()),
IssuerSigningKey = new X509SecurityKey(appSettings.IdentityServerAuthentication.OpenIddict.IssuerSigningCertificate.FindCertificate()),
ValidIssuer = appSettings.Authentication.Jwt.IssuerUri,
ValidAudience = appSettings.Authentication.Jwt.Audience,
TokenDecryptionKey = new X509SecurityKey(appSettings.Authentication.Jwt.TokenDecryptionCertificate.FindCertificate()),
IssuerSigningKey = new X509SecurityKey(appSettings.Authentication.Jwt.IssuerSigningCertificate.FindCertificate()),
};
});

Expand Down Expand Up @@ -178,8 +178,8 @@
{
AuthorizationCode = new OpenApiOAuthFlow
{
TokenUrl = new Uri(appSettings.IdentityServerAuthentication.Authority + "/connect/token", UriKind.Absolute),
AuthorizationUrl = new Uri(appSettings.IdentityServerAuthentication.Authority + "/connect/authorize", UriKind.Absolute),
TokenUrl = new Uri(appSettings.Authentication.IdentityServer.Authority + "/connect/token", UriKind.Absolute),
AuthorizationUrl = new Uri(appSettings.Authentication.IdentityServer.Authority + "/connect/authorize", UriKind.Absolute),
Scopes = new Dictionary<string, string>
{
{ "openid", "OpenId" },
Expand All @@ -189,7 +189,7 @@
},
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri(appSettings.IdentityServerAuthentication.Authority + "/connect/token", UriKind.Absolute),
TokenUrl = new Uri(appSettings.Authentication.IdentityServer.Authority + "/connect/token", UriKind.Absolute),
Scopes = new Dictionary<string, string>
{
{ "ClassifiedAds.WebAPI", "ClassifiedAds WebAPI" },
Expand Down Expand Up @@ -230,7 +230,7 @@
healthQuery: "SELECT 1;",
name: "Sql Server",
failureStatus: HealthStatus.Degraded)
.AddHttp(appSettings.IdentityServerAuthentication.Authority,
.AddHttp(appSettings.Authentication.IdentityServer.Authority,
name: "Identity Server",
failureStatus: HealthStatus.Degraded)
.AddStorageManagerHealthCheck(appSettings.Storage);
Expand Down
15 changes: 9 additions & 6 deletions src/Monolith/ClassifiedAds.WebAPI/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
"ConnectionStrings": {
"ClassifiedAds": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true;Encrypt=False"
},
"IdentityServerAuthentication": {
"Provider": "IdentityServer4",
"Authority": "https://localhost:44367",
"ApiName": "ClassifiedAds.WebAPI",
"RequireHttpsMetadata": "true",
"OpenIddict": {
"Authentication": {
"Provider": "IdentityServer",
"IdentityServer": {
"Authority": "https://localhost:44367",
"Audience": "ClassifiedAds.WebAPI",
"RequireHttpsMetadata": "true"
},
"Jwt": {
"IssuerUri": "https://localhost:44367/",
"Audience": "ClassifiedAds.WebAPI",
"TokenDecryptionCertificate": {
"Thumbprint": null,
"Path": "Certs/classifiedads.identityserver.pfx",
Expand Down
4 changes: 0 additions & 4 deletions src/Monolith/ClassifiedAds.WebMVC/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
"Endpoint": "https://localhost:44312",
"PublicEndpoint": "https://localhost:44312"
},
"NotificationServer": {
"Endpoint": "https://localhost:44390",
"PublicEndpoint": "https://localhost:44390"
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
4 changes: 2 additions & 2 deletions src/Monolith/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ services:
environment:
ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT}
ConnectionStrings__ClassifiedAds: ${ConnectionStrings__ClassifiedAds}
IdentityServerAuthentication__Authority: "http://host.docker.internal:9000"
IdentityServerAuthentication__RequireHttpsMetadata: "false"
Authentication__IdentityServer__Authority: "http://host.docker.internal:9000"
Authentication__IdentityServer__RequireHttpsMetadata: "false"
Storage__Provider: ${Storage__Provider}
Storage__Local__Path: ${Storage__Local__Path}
Storage__Azure__ConnectionString: ${Storage__Azure__ConnectionString}
Expand Down

0 comments on commit 8b024c9

Please sign in to comment.