Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.2.4 release #71

Merged
merged 1 commit into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.2.4] - 2023-08-22
### Fixed
- Refactored code and fixed code smells

## [1.2.3] - 2023-07-27
### Fixed
- Bug limiting the amount of DCR messages that can be queued. [Sandbox Issue 31](https://github.com/ConsumerDataRight/sandbox/issues/31)

## [1.2.2] - 2023-06-20
### Changed
- Regenerated all mTLS, SSA and TLS certificates to allow for another five years before they expire.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![Consumer Data Right Logo](https://raw.githubusercontent.com/ConsumerDataRight/mock-data-recipient/main/cdr-logo.png)

[![Consumer Data Standards v1.24.0](https://img.shields.io/badge/Consumer%20Data%20Standards-v1.24.0-blue.svg)](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.24.0/)
[![Consumer Data Standards v1.25.0](https://img.shields.io/badge/Consumer%20Data%20Standards-v1.25.0-blue.svg)](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.25.0/#introduction)
[![Conformance Test Suite 3.2](https://img.shields.io/badge/Conformance%20Test%20Suite-v3.2-darkblue.svg)](https://www.cdr.gov.au/for-providers/conformance-test-suite-data-recipients)
[![made-with-dotnet](https://img.shields.io/badge/Made%20with-.NET-1f425Ff.svg)](https://dotnet.microsoft.com/)
[![made-with-csharp](https://img.shields.io/badge/Made%20with-C%23-1f425Ff.svg)](https://docs.microsoft.com/en-us/dotnet/csharp/)
Expand All @@ -14,7 +14,7 @@ This repository contains a mock implementation of a Data Recipient and is offere

## Mock Data Recipient - Alignment
The Mock Data Recipient in this release:
* aligns to [v1.24.0](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.24.0) of the [Consumer Data Standards](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.24.0);
* aligns to [v1.25.0](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.25.0/#introduction) of the [Consumer Data Standards](https://consumerdatastandardsaustralia.github.io/standards-archives/standards-1.25.0/#introduction);
* passed v3.2 of the [Conformance Test Suite for Data Recipients](https://www.cdr.gov.au/for-providers/conformance-test-suite-data-recipients); and
* can connect to and complete authentication against both [FAPI 1.0 Migration Phase 2 and Phase 3](https://consumerdatastandardsaustralia.github.io/standards/#authentication-flows) compliant data holders.

Expand Down
6 changes: 3 additions & 3 deletions Source/CDR.DCR/CDR.DCR.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
<Version>1.2.2</Version>
<FileVersion>1.2.2</FileVersion>
<AssemblyVersion>1.2.2</AssemblyVersion>
<Version>1.2.4</Version>
<FileVersion>1.2.4</FileVersion>
<AssemblyVersion>1.2.4</AssemblyVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Queues" Version="12.12.0" />
Expand Down
101 changes: 54 additions & 47 deletions Source/CDR.DCR/DCR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using CDR.DCR.Models;

namespace CDR.DCR
{
Expand Down Expand Up @@ -99,7 +100,18 @@ public static async Task DCR([QueueTrigger("dynamicclientregistration", Connecti
Response<Token> tokenResponse = await GetAccessToken(tokenEndpoint, softwareProductId, clientCertificate, signCertificate, log, ignoreServerCertificateErrors);
if (tokenResponse.IsSuccessful)
{
var ssa = await GetSoftwareStatementAssertion(ssaEndpoint, xv, tokenResponse.Data.AccessToken, clientCertificate, brandId, softwareProductId, log, ignoreServerCertificateErrors);
var softwareStatementAssertion = new SoftwareStatementAssertion()
{
SsaEndpoint = ssaEndpoint,
Version = xv,
AccessToken = tokenResponse.Data.AccessToken,
ClientCertificate = clientCertificate,
BrandId = brandId,
SoftwareProductId = softwareProductId,
Log = log,
IgnoreServerCertificateErrors = ignoreServerCertificateErrors
};
var ssa = await GetSoftwareStatementAssertion(softwareStatementAssertion);
if (ssa.IsSuccessful)
{
//DOES the Data Holder Brand EXIST in the REPO?
Expand Down Expand Up @@ -135,13 +147,21 @@ public static async Task DCR([QueueTrigger("dynamicclientregistration", Connecti
if (oidcDiscovery != null)
{
regEndpoint = oidcDiscovery.RegistrationEndpoint;

(string errorMessage, string dcrRequestJwt) = PopulateDCRRequestJwt(softwareProductId, redirectUris, ssa.Data, oidcDiscovery.Issuer,
oidcDiscovery.ResponseTypesSupported,
oidcDiscovery.AuthorizationSigningResponseAlgValuesSupported,
oidcDiscovery.AuthorizationEncryptionResponseEncValuesSupported,
oidcDiscovery.AuthorizationEncryptionResponseAlgValuesSupported,
signCertificate);

var dcrRequest = new DcrRequest()
{
SoftwareProductId = softwareProductId,
RedirectUris = redirectUris,
Ssa = ssa.Data,
Audience = oidcDiscovery.Issuer,
ResponseTypesSupported = oidcDiscovery.ResponseTypesSupported,
AuthorizationSigningResponseAlgValuesSupported = oidcDiscovery.AuthorizationSigningResponseAlgValuesSupported,
AuthorizationEncryptionResponseEncValuesSupported = oidcDiscovery.AuthorizationEncryptionResponseEncValuesSupported,
AuthorizationEncryptionResponseAlgValuesSupported = oidcDiscovery.AuthorizationEncryptionResponseAlgValuesSupported,
SignCertificate = signCertificate
};

(string errorMessage, string dcrRequestJwt) = PopulateDCRRequestJwt(dcrRequest);

// Process Registration - retry 3 times
bool regSuccess = false;
Expand Down Expand Up @@ -335,23 +355,15 @@ private static async Task<Response<Token>> GetAccessToken(
/// <summary>
/// Generate the SSA
/// </summary>
private static async Task<Response<string>> GetSoftwareStatementAssertion(
string ssaEndpoint,
string version,
string accessToken,
X509Certificate2 clientCertificate,
string brandId,
string softwareProductId,
ILogger log,
bool ignoreServerCertificateErrors = false)
private static async Task<Response<string>> GetSoftwareStatementAssertion(SoftwareStatementAssertion softwareStatementAssertion)
{
// Setup the request to the get ssa endpoint.
var endpoint = $"{ssaEndpoint}{brandId}/software-products/{softwareProductId}/ssa";
var endpoint = $"{softwareStatementAssertion.SsaEndpoint}{softwareStatementAssertion.BrandId}/software-products/{softwareStatementAssertion.SoftwareProductId}/ssa";

// Setup the http client.
var client = GetHttpClient(clientCertificate, accessToken, version, ignoreServerCertificateErrors: ignoreServerCertificateErrors);
var client = GetHttpClient(softwareStatementAssertion.ClientCertificate, softwareStatementAssertion.AccessToken, softwareStatementAssertion.Version, ignoreServerCertificateErrors: softwareStatementAssertion.IgnoreServerCertificateErrors);

log.LogInformation("Retrieving SSA from the Register: {ssaEndpoint}", endpoint);
softwareStatementAssertion.Log.LogInformation("Retrieving SSA from the Register: {ssaEndpoint}", endpoint);

// Make the request to the get data holder brands endpoint.
var response = await client.GetAsync(endpoint);
Expand All @@ -361,7 +373,7 @@ private static async Task<Response<string>> GetSoftwareStatementAssertion(
StatusCode = response.StatusCode
};

log.LogInformation("SSA response: {statusCode} - {body}", ssaResponse.StatusCode, body);
softwareStatementAssertion.Log.LogInformation("SSA response: {statusCode} - {body}", ssaResponse.StatusCode, body);

if (response.IsSuccessStatusCode)
{
Expand Down Expand Up @@ -408,12 +420,7 @@ private static async Task<Response<OidcDiscovery>> GetOidcDiscovery(string infos
/// <summary>
/// Generate the DCR Request JWT
/// </summary>
private static (string, string) PopulateDCRRequestJwt(string softwareProductId, string redirectUris, string ssa, string audience,
string[] responseTypesSupported,
string[] authorizationSigningResponseAlgValuesSupported,
string[] authorizationEncryptionResponseEncValuesSupported,
string[] authorizationEncryptionResponseAlgValuesSupported,
X509Certificate2 signCertificate)
private static (string, string) PopulateDCRRequestJwt(DcrRequest dcrRequest)
{
string errorMessage = string.Empty;
var claims = new List<Claim>
Expand All @@ -427,27 +434,27 @@ private static (string, string) PopulateDCRRequestJwt(string softwareProductId,
new Claim("id_token_encrypted_response_alg", "RSA-OAEP"),
new Claim("id_token_encrypted_response_enc", "A256GCM"),
new Claim("request_object_signing_alg", "PS256"),
new Claim("software_statement", ssa ?? ""),
new Claim("software_statement", dcrRequest.Ssa ?? ""),
new Claim("grant_types", "client_credentials"),
new Claim("grant_types", "authorization_code"),
new Claim("grant_types", "refresh_token")
};

// response_types updated below "code, code id_token" both types are returned and added below
// A response type is mandatory
if (!responseTypesSupported.Contains("code") && !responseTypesSupported.Contains("code id_token"))
if (!dcrRequest.ResponseTypesSupported.Contains("code") && !dcrRequest.ResponseTypesSupported.Contains("code id_token"))
{
// Return the error
errorMessage = ErrorMessage + " response_types";
return (errorMessage, "");
}

var responseTypesList = responseTypesSupported.Where(x => x.ToLower().Equals("code") || x.ToLower().Equals("code id_token")).ToList();
var responseTypesList = dcrRequest.ResponseTypesSupported.Where(x => x.ToLower().Equals("code") || x.ToLower().Equals("code id_token")).ToList();
claims.Add(new Claim("response_types", JsonConvert.SerializeObject(responseTypesList), JsonClaimValueTypes.JsonArray));


var isCodeFlow = responseTypesSupported.Contains("code");
if (isCodeFlow && !authorizationSigningResponseAlgValuesSupported.Any())
var isCodeFlow = dcrRequest.ResponseTypesSupported.Contains("code");
if (isCodeFlow && !dcrRequest.AuthorizationSigningResponseAlgValuesSupported.Any())
{
// Log error message to the mandatory claim missing
errorMessage = ErrorMessage + " authorization_signed_response_alg";
Expand All @@ -457,65 +464,65 @@ private static (string, string) PopulateDCRRequestJwt(string softwareProductId,
// Mandatory for code flow
if (isCodeFlow)
{
if (!authorizationSigningResponseAlgValuesSupported.Contains("PS256") && !authorizationSigningResponseAlgValuesSupported.Contains("ES256"))
if (!dcrRequest.AuthorizationSigningResponseAlgValuesSupported.Contains("PS256") && !dcrRequest.AuthorizationSigningResponseAlgValuesSupported.Contains("ES256"))
{
// Return the error
errorMessage = ErrorMessage + " authorization_signed_response_alg";
return (errorMessage, "");
}

if (authorizationSigningResponseAlgValuesSupported.Contains("PS256"))
if (dcrRequest.AuthorizationSigningResponseAlgValuesSupported.Contains("PS256"))
{
claims.Add(new Claim("authorization_signed_response_alg", "PS256"));
}
else if (authorizationSigningResponseAlgValuesSupported.Contains("ES256"))
else if (dcrRequest.AuthorizationSigningResponseAlgValuesSupported.Contains("ES256"))
{
claims.Add(new Claim("authorization_signed_response_alg", "ES256"));
}
}

// Check if the enc is empty but a alg is specified.
if ((authorizationEncryptionResponseEncValuesSupported == null || !authorizationEncryptionResponseEncValuesSupported.Any()) // No enc specified
&& authorizationEncryptionResponseAlgValuesSupported != null && authorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP-256")
&& authorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP")) // but alg specified.
if ((dcrRequest.AuthorizationEncryptionResponseEncValuesSupported == null || !dcrRequest.AuthorizationEncryptionResponseEncValuesSupported.Any()) // No enc specified
&& dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported != null && dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP-256")
&& dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP")) // but alg specified.
{
errorMessage = ErrorMessage + " authorization_encrypted_response_enc";
return (errorMessage, "");
}


if (authorizationEncryptionResponseEncValuesSupported != null && authorizationEncryptionResponseEncValuesSupported.Contains("A128CBC-HS256"))
if (dcrRequest.AuthorizationEncryptionResponseEncValuesSupported != null && dcrRequest.AuthorizationEncryptionResponseEncValuesSupported.Contains("A128CBC-HS256"))
{
claims.Add(new Claim("authorization_encrypted_response_enc", "A128CBC-HS256"));
}
else if (authorizationEncryptionResponseEncValuesSupported != null && authorizationEncryptionResponseEncValuesSupported.Contains("A256GCM"))
else if (dcrRequest.AuthorizationEncryptionResponseEncValuesSupported != null && dcrRequest.AuthorizationEncryptionResponseEncValuesSupported.Contains("A256GCM"))
{
claims.Add(new Claim("authorization_encrypted_response_enc", "A256GCM"));
}

// Conditional: Optional for response_type "code" if authorization_encryption_enc_values_supported is present
if (isCodeFlow && authorizationEncryptionResponseAlgValuesSupported != null && authorizationEncryptionResponseAlgValuesSupported.Any())
if (isCodeFlow && dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported != null && dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported.Any())
{
if (authorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP-256"))
if (dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP-256"))
{
claims.Add(new Claim("authorization_encrypted_response_alg", "RSA-OAEP-256"));
}
else if (authorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP"))
else if (dcrRequest.AuthorizationEncryptionResponseAlgValuesSupported.Contains("RSA-OAEP"))
{
claims.Add(new Claim("authorization_encrypted_response_alg", "RSA-OAEP"));
}
}

char[] delimiters = { ',', ' '};
var redirectUrisList = redirectUris?.Split(delimiters).ToList();
var redirectUrisList = dcrRequest.RedirectUris?.Split(delimiters).ToList();
claims.Add(new Claim("redirect_uris", JsonConvert.SerializeObject(redirectUrisList), JsonClaimValueTypes.JsonArray));

var jwt = new JwtSecurityToken(
issuer: softwareProductId,
audience: audience,
issuer: dcrRequest.SoftwareProductId,
audience: dcrRequest.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(5),
signingCredentials: new X509SigningCredentials(signCertificate, SecurityAlgorithms.RsaSsaPssSha256));
signingCredentials: new X509SigningCredentials(dcrRequest.SignCertificate, SecurityAlgorithms.RsaSsaPssSha256));

var tokenHandler = new JwtSecurityTokenHandler();
return (errorMessage, tokenHandler.WriteToken(jwt));
Expand Down
18 changes: 18 additions & 0 deletions Source/CDR.DCR/Models/DcrRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

using System.Security.Cryptography.X509Certificates;

namespace CDR.DCR.Models
{
public class DcrRequest
{
public string SoftwareProductId { get; set; }
public string RedirectUris { get; set; }
public string Ssa { get; set; }
public string Audience { get; set; }
public string[] ResponseTypesSupported { get; set; }
public string[] AuthorizationSigningResponseAlgValuesSupported { get; set; }
public string[] AuthorizationEncryptionResponseEncValuesSupported { get; set; }
public string[] AuthorizationEncryptionResponseAlgValuesSupported { get; set; }
public X509Certificate2 SignCertificate { get; set; }
}
}
17 changes: 17 additions & 0 deletions Source/CDR.DCR/Models/SoftwareStatementAssertion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.Extensions.Logging;
using System.Security.Cryptography.X509Certificates;

namespace CDR.DCR.Models
{
public class SoftwareStatementAssertion
{
public string SsaEndpoint { get; set; }
public string Version { get; set; }
public string AccessToken { get; set; }
public X509Certificate2 ClientCertificate { get; set; }
public string BrandId { get; set; }
public string SoftwareProductId { get; set; }
public ILogger Log { get; set; }
public bool IgnoreServerCertificateErrors { get; set; } = false;
}
}
Loading
Loading