Skip to content

Commit

Permalink
Merge pull request #619 from JoeShook/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
JoeShook authored Sep 7, 2024
2 parents 6dfbec1 + 60b1efa commit 8659e0e
Show file tree
Hide file tree
Showing 45 changed files with 1,644 additions and 530 deletions.
5 changes: 3 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<!-- https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges -->
<PackageVersion Include="Duende.IdentityServer.Storage" Version="7.0.6" />
<PackageVersion Include="Google.Apis.Auth" Version="1.68.0" />
<PackageVersion Include="Hl7.Fhir.R4B" Version="5.9.1" />
<PackageVersion Include="LazyCache" Version="2.4.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="[6.0.32,8.0.8]" />
<PackageVersion Include="AspNetCoreRateLimit" Version="5.0.0" />
Expand Down Expand Up @@ -36,7 +37,7 @@
<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="8.0.1" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.2" />
<PackageVersion Include="OpenTelemetry" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
Expand All @@ -47,7 +48,7 @@
<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="8.0.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.0.2" />
<PackageVersion Include="Udap.Metadata.Server" Version="0.3.24" />
<PackageVersion Include="Yarp.ReverseProxy" Version="2.1.0" />
</ItemGroup>
Expand Down
40 changes: 25 additions & 15 deletions Udap.Common/Metadata/UdapMetaDataBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Web;
using IdentityModel;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Udap.Common.Certificates;
using Udap.Model;
Expand All @@ -22,17 +24,17 @@ namespace Udap.Common.Metadata;

public class UdapMetaDataBuilder
{
private UdapMetadata _udapMetadata;
private readonly IOptionsMonitor<UdapMetadataOptions> _optionsMonitor;
private readonly IPrivateCertificateStore _certificateStore;
private readonly ILogger<UdapMetaDataBuilder> _logger;


public UdapMetaDataBuilder(
UdapMetadata udapMetadata,
IOptionsMonitor<UdapMetadataOptions> optionsMonitor,
IPrivateCertificateStore certificateStore,
ILogger<UdapMetaDataBuilder> logger)
{
_udapMetadata = udapMetadata;
_optionsMonitor = optionsMonitor;
_certificateStore = certificateStore;
_logger = logger;
}
Expand All @@ -43,7 +45,10 @@ public UdapMetaDataBuilder(
/// <returns></returns>
public ICollection<string> GetCommunities()
{
return _udapMetadata.Communities();
var options = _optionsMonitor.CurrentValue;
var udapMetaData = new UdapMetadata(options);

return udapMetaData.Communities();
}

/// <summary>
Expand All @@ -53,7 +58,10 @@ public ICollection<string> GetCommunities()
/// <returns></returns>
public string GetCommunitiesAsHtml(string path)
{
return _udapMetadata.CommunitiesAsHtml(path);
var options = _optionsMonitor.CurrentValue;
var udapMetaData = new UdapMetadata(options);

return udapMetaData.CommunitiesAsHtml(path);
}

/// <summary>
Expand All @@ -64,25 +72,27 @@ public string GetCommunitiesAsHtml(string path)
/// <exception cref="System.NotImplementedException"></exception>
public async Task<UdapMetadata?> SignMetaData(string baseUrl, string? community = null, CancellationToken token = default)
{
var udapMetaData = _udapMetadata.Clone();
var options = _optionsMonitor.CurrentValue;
var udapMetaData = new UdapMetadata(options);

var udapMetadataConfig = udapMetaData.GetUdapMetadataConfig(community);

if (udapMetadataConfig == null)
{
_logger.LogWarning($"Missing metadata for community: {System.Web.HttpUtility.UrlEncode(community)}");
_logger.LogWarning($"Missing metadata for community: {HttpUtility.UrlEncode(community)}");
return null;
}

udapMetaData.AuthorizationEndpoint = udapMetadataConfig.SignedMetadataConfig.AuthorizationEndpoint;
udapMetaData.TokenEndpoint = udapMetadataConfig.SignedMetadataConfig.TokenEndpoint;
udapMetaData.RegistrationEndpoint = udapMetadataConfig.SignedMetadataConfig.RegistrationEndpoint;

if (Enumerable.Any<string>(udapMetadataConfig.SignedMetadataConfig.RegistrationSigningAlgorithms))
if (udapMetadataConfig.SignedMetadataConfig.RegistrationSigningAlgorithms.Any())
{
udapMetaData.RegistrationEndpointJwtSigningAlgValuesSupported = udapMetadataConfig.SignedMetadataConfig.RegistrationSigningAlgorithms;
}

if (Enumerable.Any<string>(udapMetadataConfig.SignedMetadataConfig.TokenSigningAlgorithms))
if (udapMetadataConfig.SignedMetadataConfig.TokenSigningAlgorithms.Any())
{
udapMetaData.TokenEndpointAuthSigningAlgValuesSupported = udapMetadataConfig.SignedMetadataConfig.TokenSigningAlgorithms;
}
Expand All @@ -91,7 +101,7 @@ public string GetCommunitiesAsHtml(string path)

if (certificate == null)
{
_logger.LogWarning($"Missing default community certificate: {System.Web.HttpUtility.UrlEncode(community)}");
_logger.LogWarning($"Missing default community certificate: {HttpUtility.UrlEncode(community)}");
return null;
}

Expand All @@ -114,15 +124,15 @@ public string GetCommunitiesAsHtml(string path)

var builder = SignedSoftwareStatementBuilder<ISoftwareStatementSerializer>.Create(certificate, jwtPayload);

if (Enumerable.First<string>(udapMetaData.RegistrationEndpointJwtSigningAlgValuesSupported).IsECDSA())
if (udapMetaData.RegistrationEndpointJwtSigningAlgValuesSupported.First().IsECDSA())
{
udapMetaData.SignedMetadata = builder.BuildECDSA(Enumerable.First<string>(udapMetaData.
RegistrationEndpointJwtSigningAlgValuesSupported));
udapMetaData.SignedMetadata = builder.BuildECDSA(udapMetaData.
RegistrationEndpointJwtSigningAlgValuesSupported.First());
}
else
{
udapMetaData.SignedMetadata = builder.Build(Enumerable.First<string>(udapMetaData.
RegistrationEndpointJwtSigningAlgValuesSupported));
udapMetaData.SignedMetadata = builder.Build(udapMetaData.
RegistrationEndpointJwtSigningAlgValuesSupported.First());
}

return udapMetaData;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#region (c) 2023 Joseph Shook. All rights reserved.
#region (c) 2024 Joseph Shook. All rights reserved.
// /*
// Authors:
// Joseph Shook Joseph.Shook@Surescripts.com
Expand All @@ -7,27 +7,20 @@
// */
#endregion



//
// See reason for Microsoft.Extensions.DependencyInjection namespace
// here: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage
//
using Hl7.Fhir.Utility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Hl7.Fhir.Utility;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Udap.Common;
using Udap.Common.Certificates;
using Udap.Common.Extensions;
using Udap.Common.Metadata;
using Udap.Metadata.Server;
using Udap.Model;
using Constants = Udap.Common.Constants;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;
Expand All @@ -36,23 +29,24 @@ public static class ServiceCollectionExtensions
{
public static IServiceCollection AddUdapMetadataServer(
this IServiceCollection services,
IConfiguration configuration,
string? applicationName = null)
IConfiguration configuration)
{
var udapMetadataOptions = new UdapMetadataOptions();
configuration.GetSection("UdapMetadataOptions").Bind(udapMetadataOptions);
// var udapMetadataOptions = new UdapMetadataOptions();
// configuration.GetSection("UdapMetadataOptions").Bind(udapMetadataOptions);

services.Configure<UdapMetadataOptions>(configuration.GetSection("UdapMetadataOptions"));


//TODO: this could use some DI work...
var udapMetadata = new UdapMetadata(
udapMetadataOptions!,
new List<string>
{
"openid", "patient/*.read", "user/*.read", "system/*.read", "patient/*.rs", "user/*.rs", "system/*.rs"
});
// var udapMetadata = new UdapMetadata(
// udapMetadataOptions!
// new List<string>
// {
// "openid", "patient/*.read", "user/*.read", "system/*.read", "patient/*.rs", "user/*.rs", "system/*.rs"
// }
// );

services.AddSingleton(udapMetadata);
// services.AddSingleton<UdapMetaDataEndpoint>(udapMetadata);
services.TryAddScoped<UdapMetaDataBuilder>();
services.AddScoped<UdapMetaDataEndpoint>();

Expand Down
13 changes: 12 additions & 1 deletion Udap.Metadata.Server/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Add UseUdapMetaData to program.cs

```

```AddUdapMetaDataServer``` extension will find the UdapMetadataOptions in AppSettings. These settings will match the issued certificate.
```AddUdapMetaDataServer``` extension will find the UdapMetadataOptions in AppSettings. These settings will match the IssuedCerts settings in UdapFileCertStoreManifest settings below.

Reference [Required UDAP Metadata](http://hl7.org/fhir/us/udap-security/discovery.html#signed-metadata-elements).

Expand All @@ -73,6 +73,17 @@ Issuer and Subject must match the issued certificates, Subject Alternative Name
```json

"UdapMetadataOptions": {
"UdapVersionsSupported": [ "1" ],
"UdapProfilesSupported": [ "udap_dcr", "udap_authn", "udap_authz", "udap_to" ],
"UdapAuthorizationExtensionsSupported": [ "hl7-b2b" ],
"UdapAuthorizationExtensionsRequired": [ "hl7-b2b" ],
"ScopesSupported": [ "openid", "system/*.read", "user/*.read", "patient/*.read" ],
"UdapCertificationsSupported": [ "http://MyUdapCertification", "http://MyUdapCertification2" ],
"UdapCertificationsRequired": [ "http://MyUdapCertification" ],
"GrantTypesSupported": [ "authorization_code", "refresh_token", "client_credentials" ],
//"TokenEndpointAuthSigningAlgValuesSupported": [ "RS256", "RS384", "ES256", "ES384" ],
//"RegistrationEndpointJwtSigningAlgValuesSupported": [ "RS256", "RS384", "ES256", "ES384" ],

"UdapMetadataConfigs": [
{
"Community": "http://localhost",
Expand Down
36 changes: 12 additions & 24 deletions Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
Expand Down Expand Up @@ -69,24 +70,14 @@ public AccessTokenRequestForClientCredentialsBuilder WithScope(string scope)
_scope = scope;
return this;
}

// private string BuildHl7B2BExtensions()
// {
// return "{\"version\": \"1\", \"subject_name\": \"todo. more work to do here\"}";
// }

private Dictionary<string, B2BAuthorizationExtension>? _extensions;

public AccessTokenRequestForClientCredentialsBuilder WithExtension(string key, B2BAuthorizationExtension value)
{
//TODO: Hack for connect-a-thon.
if (_extensions == null)
{
_extensions = new Dictionary<string, B2BAuthorizationExtension>();
}
private Dictionary<string, object> _extensions = new Dictionary<string, object>();


_extensions[key] = value;

public AccessTokenRequestForClientCredentialsBuilder WithExtension<T>(string key, T value) where T : class
{
var jsonElement = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(value));
_extensions[key] = jsonElement;
return this;
}

Expand Down Expand Up @@ -116,25 +107,22 @@ public UdapClientCredentialsTokenRequest Build(string? algorithm = UdapConstants

private string BuildClientAssertion(string algorithm)
{
JwtPayLoadExtension jwtPayload;

//HL7 FHIR IG profile
jwtPayload = new JwtPayLoadExtension(
var jwtPayload =
//HL7 FHIR IG profile
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
_tokenEndoint,
_claims,
_now,
_now.AddMinutes(5)
);

if (_extensions != null)
if (_extensions.Any())
{
var payload = jwtPayload as Dictionary<string, object>;
payload.Add(UdapConstants.JwtClaimTypes.Extensions, _extensions);
}

Console.WriteLine(JsonSerializer.Serialize(jwtPayload, new JsonSerializerOptions{WriteIndented = true}));

return SignedSoftwareStatementBuilder<JwtPayLoadExtension>
.Create(_certificate, jwtPayload)
.Build(algorithm);
Expand Down
3 changes: 3 additions & 0 deletions Udap.Model/Access/UdapClientCredentialsTokenRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
// */
#endregion

using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using IdentityModel.Client;
using Udap.Model.UdapAuthenticationExtensions;

namespace Udap.Model.Access;

Expand Down
Loading

0 comments on commit 8659e0e

Please sign in to comment.