Skip to content

Commit

Permalink
Merge pull request #517 from JoeShook/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
JoeShook authored May 17, 2024
2 parents af3c9bd + ecf5e6b commit f71cab7
Show file tree
Hide file tree
Showing 76 changed files with 1,276 additions and 1,884 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.4",
"version": "8.0.5",
"commands": [
"dotnet-ef"
]
Expand Down
30 changes: 15 additions & 15 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
<ItemGroup>
<!-- https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges -->
<PackageVersion Include="Duende.IdentityServer.Storage" Version="6.3.7" />
<PackageVersion Include="Google.Apis.Auth" Version="1.67.0" />
<PackageVersion Include="Google.Apis.Auth" Version="1.68.0" />
<PackageVersion Include="LazyCache" Version="2.4.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageVersion Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageVersion Include="Hl7.Fhir.Specification.R4B" Version="5.3.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.4" />
<PackageVersion Include="Hl7.Fhir.Specification.R4B" Version="5.8.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="8.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.5.1" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.5.1" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.5.2" />
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="7.5.2" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageVersion Include="MSTest.TestFramework" Version="3.1.1" />
<PackageVersion Include="IdentityModel" Version="7.0.0" />
Expand All @@ -27,28 +27,28 @@
<PackageVersion Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.4" />
<PackageVersion Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="6.2.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="[7.0.13,8.0.1]" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[7.0.13,8.0.0]" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="[7.0.14,8.0.1]" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[6.0.0,7.0.0]" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="[6.0.0,7.0.1]" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.1" />
<PackageVersion Include="OpenTelemetry" Version="1.7.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.7.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.2" />
<PackageVersion Include="OpenTelemetry" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.0.0-rc9.14" />
<PackageVersion Include="Serilog.AspNetCore" Version="[6.1.0,7.0.0]" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="[3.1.0,7.0.0]" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.5.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.5.2" />
<PackageVersion Include="Udap.Metadata.Server" Version="0.3.24" />
<PackageVersion Include="Yarp.ReverseProxy" Version="2.1.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net.Http;
using Hl7.Fhir.Utility;
using Udap.Common;
using Udap.Common.Certificates;
using Udap.Common.Extensions;
Expand Down Expand Up @@ -126,7 +126,7 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app,
{
EnsureMvcControllerUnloads(app);

app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}",
app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}",
async (
[FromServices] UdapMetaDataEndpoint endpoint,
HttpContext httpContext,
Expand All @@ -136,13 +136,13 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app,
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound); // community doesn't exist

app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}/communities",
app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}/communities",
([FromServices] UdapMetaDataEndpoint endpoint) => endpoint.GetCommunities())
.AllowAnonymous()
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound); // community doesn't exist

app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}/communities/ashtml",
app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}/communities/ashtml",
(
[FromServices] UdapMetaDataEndpoint endpoint,
HttpContext httpContext) => endpoint.GetCommunitiesAsHtml(httpContext))
Expand All @@ -156,7 +156,7 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app,
public static IApplicationBuilder UseUdapMetadataServer(this IApplicationBuilder app, string? prefixRoute = null)
{

app.Map($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}", path =>
app.Map($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}", path =>
{
path.Run(async ctx =>
{
Expand Down
1 change: 0 additions & 1 deletion Udap.Metadata.Server/UdapMetaDataEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public IResult GetCommunities()
return Results.Ok(_metaDataBuilder.GetCommunities());
}


public IResult GetCommunitiesAsHtml(HttpContext httpContext)
{
var html = _metaDataBuilder.GetCommunitiesAsHtml(httpContext.Request.GetDisplayUrl().GetBaseUrlFromMetadataUrl());
Expand Down
48 changes: 12 additions & 36 deletions Udap.Model/Access/AccessTokenRequestForAuthorizationCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,13 @@ public AccessTokenRequestForAuthorizationCodeBuilder WithClaim(Claim claim)
}

/// <summary>
/// Legacy refers to the current udap.org/UDAPTestTool behavior as documented in
/// udap.org profiles. The HL7 Security IG has the following constraint to make it
/// more friendly with OIDC and SMART launch frameworks.
/// sub == iss == client_id
/// Where as the Legacy is the following behavior
/// sub == iis == SubAlt Name
/// Build an <see cref="UdapAuthorizationCodeTokenRequest"/>
/// </summary>
/// <param name="legacy"></param>
/// <param name="algorithm"></param>
/// <returns></returns>
public UdapAuthorizationCodeTokenRequest Build(
bool legacy = false,
string? algorithm = UdapConstants.SupportedAlgorithm.RS256)
public UdapAuthorizationCodeTokenRequest Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256)
{
var clientAssertion = BuildClientAssertion(algorithm, legacy);
var clientAssertion = BuildClientAssertion(algorithm);

return new UdapAuthorizationCodeTokenRequest()
{
Expand All @@ -99,34 +91,18 @@ public UdapAuthorizationCodeTokenRequest Build(
};
}

private string? BuildClientAssertion(string algorithm, bool legacy = false)
private string? BuildClientAssertion(string algorithm)
{
JwtPayLoadExtension jwtPayload;

if (legacy)
{
//udap.org profile
jwtPayload = new JwtPayLoadExtension(
_certificate.GetNameInfo(X509NameType.UrlName,
false), //TODO:: Let user pick the subject alt name. Create will need extra param.
_tokenEndpoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);
}

else
{
//HL7 FHIR IG profile
jwtPayload = new JwtPayLoadExtension(
_clientId,
_tokenEndpoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);
}
//HL7 FHIR IG profile
jwtPayload = new JwtPayLoadExtension(
_clientId,
_tokenEndpoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);

return SignedSoftwareStatementBuilder<JwtPayLoadExtension>
.Create(_certificate, jwtPayload)
Expand Down
50 changes: 13 additions & 37 deletions Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,13 @@ public AccessTokenRequestForClientCredentialsBuilder WithExtension(string key, B
}

/// <summary>
/// Legacy refers to the current udap.org/UDAPTestTool behavior as documented in
/// udap.org profiles. The HL7 Security IG has the following constraint to make it
/// more friendly with OIDC and SMART launch frameworks.
/// sub == iss == client_id
/// Where as the Legacy is the following behavior
/// sub == iis == SubAlt Name
/// Build an <see cref="UdapClientCredentialsTokenRequest"/>
/// </summary>
/// <param name="legacy"></param>
/// <param name="algorithm"></param>
/// <returns></returns>
public UdapClientCredentialsTokenRequest Build(
bool legacy = false,
string? algorithm = UdapConstants.SupportedAlgorithm.RS256)
public UdapClientCredentialsTokenRequest Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256)
{
var clientAssertion = BuildClientAssertion(algorithm, legacy);
var clientAssertion = BuildClientAssertion(algorithm);

return new UdapClientCredentialsTokenRequest
{
Expand All @@ -122,34 +114,18 @@ public UdapClientCredentialsTokenRequest Build(
}


private string BuildClientAssertion(string algorithm, bool legacy = false)
private string BuildClientAssertion(string algorithm)
{
JwtPayLoadExtension jwtPayload;

if (legacy)
{
//udap.org profile
jwtPayload = new JwtPayLoadExtension(
_certificate.GetNameInfo(X509NameType.UrlName,
false), //TODO:: Let user pick the subject alt name. Create will need extra param.
_tokenEndoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);
}

else
{
//HL7 FHIR IG profile
jwtPayload = new JwtPayLoadExtension(
_clientId, //TODO:: Let user pick the subject alt name. Create will need extra param.
_tokenEndoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);
}

//HL7 FHIR IG profile
jwtPayload = new JwtPayLoadExtension(
_clientId, //TODO:: Let user pick the subject alt name. Create will need extra param.
_tokenEndoint, //The FHIR Authorization Server's token endpoint URL
_claims,
_now,
_now.AddMinutes(5)
);

if (_extensions != null)
{
Expand Down
13 changes: 5 additions & 8 deletions Udap.Model/Statement/SignedSoftwareStatementBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,18 @@ public static SignedSoftwareStatementBuilder<T> Create(X509Certificate2 certific
// we could add more builder methods
//

public string Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256)
public string Build(string? algorithm = null)
{
algorithm ??= UdapConstants.SupportedAlgorithm.RS256;

#if NET5_0_OR_GREATER
//
// Short circuit to ECDSA
//
if (_certificate.GetECDsaPublicKey() != null)
{
return BuildECDSA();
return BuildECDSA(algorithm);
}
#endif
algorithm ??= UdapConstants.SupportedAlgorithm.RS256;
var securityKey = new X509SecurityKey(_certificate);
var signingCredentials = new SigningCredentials(securityKey, algorithm);

Expand All @@ -75,11 +74,9 @@ public string Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256)

#if NET5_0_OR_GREATER

public string BuildECDSA(string? algorithm = UdapConstants.SupportedAlgorithm.ES384)
public string BuildECDSA(string? algorithm = null)
{

algorithm ??= UdapConstants.SupportedAlgorithm.ES384;

algorithm ??= UdapConstants.SupportedAlgorithm.ES256;
var key = _certificate.GetECDsaPrivateKey();
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP384);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public static IUdapServiceBuilder AddUdapResponseGenerators(this IUdapServiceBui

public static IUdapServiceBuilder AddPrivateFileStore(this IUdapServiceBuilder builder, string? resourceServerName = null)
{
builder.Services.AddSingleton<IPrivateCertificateStore>(sp =>
builder.Services.TryAddSingleton<IPrivateCertificateStore>(sp =>
new IssuedCertificateStore(
sp.GetRequiredService<IOptionsMonitor<UdapFileCertStoreManifest>>(),
sp.GetRequiredService<ILogger<IssuedCertificateStore>>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using Udap.Server.Mappers;
using Udap.Server.Options;
using Udap.Server.ResponseHandling;
using Udap.Server.Security.Authentication.TieredOAuth;
using Udap.Server.Stores;
using Udap.Server.Validation;
using static Udap.Server.Constants;
Expand Down Expand Up @@ -166,6 +167,7 @@ public static IUdapServiceBuilder AddUdapServerAsIdentityProvider(
services.Configure(setupAction);
}

builder.Services.TryAddSingleton<IPostConfigureOptions<ServerSettings>, TieredIdpServerSettings>();
builder.AddUdapSigningCredentials();
services.AddSingleton(resolver => resolver.GetRequiredService<IOptions<ServerSettings>>().Value);
builder.AddRegistrationEndpointToOpenIdConnectMetadata(baseUrl);
Expand Down
18 changes: 7 additions & 11 deletions Udap.Server/Configuration/ServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
namespace Udap.Server.Configuration;
public class ServerSettings
{
[JsonPropertyName("ServerSupport")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public ServerSupport ServerSupport { get; set; }

[JsonPropertyName("DefaultSystemScopes")]
public string? DefaultSystemScopes { get; set; }

Expand All @@ -34,7 +30,13 @@ public class ServerSettings
/// </summary>
[JsonPropertyName("ForceStateParamOnAuthorizationCode")]
public bool ForceStateParamOnAuthorizationCode { get; set; } = false;


/// <summary>
/// Indicate if the IdentityServer can act as a UDAP enabled IdP.
/// </summary>
[JsonIgnore]
public bool TieredIdp { get; set; } = false;

[JsonPropertyName("LogoRequired")]
public bool LogoRequired { get; set; } = true;

Expand All @@ -50,12 +52,6 @@ public class ServerSettings
}


public enum ServerSupport
{
UDAP = 0,
Hl7SecurityIG = 1
}

public static class ConfigurationExtension
{
public static TOptions GetOption<TOptions>(this IConfiguration configuration, string settingKey)
Expand Down
17 changes: 12 additions & 5 deletions Udap.Server/Extensions/ClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,13 @@ public static class ClientExtensions
return null;
}

public static Task<List<SecurityKey>> GetUdapKeysAsync(this ParsedSecret secret)
public static IEnumerable<SecurityKey>? GetUdapKeys(this ParsedSecret secret)
{
var jsonWebToken = new JsonWebToken(secret.Credential as string);
var x5cArray = jsonWebToken.GetHeaderValue<List<string>>("x5c");
if (!jsonWebToken.TryGetHeaderValue<List<string>>("x5c", out var x5cArray))
{
return null;
}

var certificates = x5cArray
.Select(s => new X509Certificate2(Convert.FromBase64String(s.ToString())))
Expand All @@ -81,13 +84,17 @@ public static Task<List<SecurityKey>> GetUdapKeysAsync(this ParsedSecret secret)
})
.ToList();

return Task.FromResult(certificates);
return certificates;
}

public static X509Certificate2 GetUdapEndCertAsync(this ParsedSecret secret)
public static X509Certificate2? GetUdapEndCertAsync(this ParsedSecret secret)
{
var jsonWebToken = new JsonWebToken(secret.Credential as string);
var x5cArray = jsonWebToken.GetHeaderValue<List<string>>("x5c");

if(!jsonWebToken.TryGetHeaderValue<List<string>>("x5c", out var x5cArray))
{
return null;
}

return new X509Certificate2(Convert.FromBase64String(x5cArray.First()));
}
Expand Down
Loading

0 comments on commit f71cab7

Please sign in to comment.