From aff3c5f12be53328c265650983cc508f43776ccc Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:37:08 +0000 Subject: [PATCH 01/49] Update .NET dependencies --- .../OpenBanking.Library.BankApiModels.csproj | 2 +- .../OpenBanking.Library.Connector.GenericHost.csproj | 6 +++--- .../Mapping/ApiVariantMappingConfiguration.cs | 4 +++- .../OpenBanking.Library.Connector.csproj | 6 +++--- .../OpenBanking.WebApp.Connector.csproj | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj index 87254ad55..80779701b 100644 --- a/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj +++ b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index e4d17a85c..28ae72ee6 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,9 +24,9 @@ - - - + + + diff --git a/src/OpenBanking.Library.Connector/Mapping/ApiVariantMappingConfiguration.cs b/src/OpenBanking.Library.Connector/Mapping/ApiVariantMappingConfiguration.cs index 9aef948a2..dc6235c7e 100644 --- a/src/OpenBanking.Library.Connector/Mapping/ApiVariantMappingConfiguration.cs +++ b/src/OpenBanking.Library.Connector/Mapping/ApiVariantMappingConfiguration.cs @@ -7,6 +7,7 @@ using FinnovationLabs.OpenBanking.Library.BankApiModels; using FinnovationLabs.OpenBanking.Library.Connector.Converters; using FinnovationLabs.OpenBanking.Library.Connector.Extensions; +using Microsoft.Extensions.Logging.Abstractions; using ClientRegistrationModelsPublic = FinnovationLabs.OpenBanking.Library.BankApiModels.UKObDcr.V3p3.Models; @@ -95,7 +96,8 @@ public MapperConfiguration CreateMapperConfiguration() // Discovered mappings ApplyApiVariantTypeMaps(cfg); - }); + }, + NullLoggerFactory.Instance); } private void ApplyGenericTypeMaps(IMapperConfigurationExpression cfg) diff --git a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index dda8b74e9..50c5ac7ba 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -23,14 +23,14 @@ - + - + - + diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index ef4671fd3..985c3e049 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -21,8 +21,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + From 9a00943dad4eb57d197315de1a9b12cc7fef47e8 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:07:59 +0000 Subject: [PATCH 02/49] Update Revolut --- .../Generators/RevolutGenerator.cs | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs index c7a357119..b14bfbe69 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs @@ -27,7 +27,7 @@ public override BankProfile GetBankProfile( IInstrumentationClient instrumentationClient) => new( _bankGroupData.GetBankProfile(bank), - "https://oba.revolut.com", // from https://developer.revolut.com/docs/guides/build-banking-apps/register-your-application-using-dcr/open-id-configuration-urls + "https://oba-auth.revolut.com", // from https://developer.revolut.com/updates/2025/12/15/open-banking-minor-updates "001580000103UAvAAM", // from https://developer.revolut.com/docs/guides/build-banking-apps/tutorials/get-account-and-transaction-information new AccountAndTransactionApi { BaseUrl = GetBaseUrl() }, null, @@ -51,13 +51,7 @@ public override BankProfile GetBankProfile( { AddRedundantOAuth2NonceRequestParameter = true, IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour - { - IdTokenMayNotHaveAuthTimeClaim = true, - IssClaim = "https://oba.revolut.com" - }, - AudClaim = - "https://oba-auth.revolut.com" // from https://developer.revolut.com/updates/2025-03-04-open-banking-fapi1-advanced#new-requirements-for-request-jwts + new IdTokenProcessingCustomBehaviour { IdTokenMayNotHaveAuthTimeClaim = true } }, AccountAccessConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour @@ -69,8 +63,7 @@ public override BankProfile GetBankProfile( IdTokenMayNotHaveConsentIdClaim = true, IdTokenMayNotHaveAcrClaim = true, IdTokenExpirationTimeClaimJsonConverter = - DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat, - IssClaim = "https://oba.revolut.com" + DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat } }, AccountAccessConsentRefreshTokenGrantPost = @@ -79,13 +72,7 @@ public override BankProfile GetBankProfile( { AddRedundantOAuth2NonceRequestParameter = true, IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour - { - IdTokenMayNotHaveAuthTimeClaim = true, - IssClaim = "https://oba.revolut.com" - }, - AudClaim = - "https://oba-auth.revolut.com" + new IdTokenProcessingCustomBehaviour { IdTokenMayNotHaveAuthTimeClaim = true } }, DomesticPaymentConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour { @@ -97,8 +84,7 @@ public override BankProfile GetBankProfile( IdTokenMayNotHaveConsentIdClaim = true, IdTokenMayNotHaveAcrClaim = true, IdTokenExpirationTimeClaimJsonConverter = - DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat, - IssClaim = "https://oba.revolut.com" + DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat } }, DomesticPayment = new DomesticPaymentCustomBehaviour @@ -107,7 +93,11 @@ public override BankProfile GetBankProfile( ResponseDataDebtorMayBeMissingOrWrong = true } }, - BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationDeleteEndpoint = true }, + BankConfigurationApiSettings = new BankConfigurationApiSettings + { + UseRegistrationGetEndpoint = true, + UseRegistrationDeleteEndpoint = true + }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { AccountAccessConsentTemplateExternalApiRequestAdjustments = externalApiRequest => From 604974e603da42dadc31465bdf84c29cf8fd8617 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:18:00 +0000 Subject: [PATCH 03/49] Update Barclays --- .../BankProfiles/Generators/BarclaysGenerator.cs | 4 +--- .../OpenBanking.WebApp.Connector.csproj | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs index fc297da67..eae8478fd 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs @@ -199,9 +199,7 @@ or BarclaysBank.BarclaycardCommercialPayments DomesticVrp = new DomesticVrpCustomBehaviour { PostResponseLinksMayOmitId = true, - PreferMisspeltContractPresentIndicator = true, - ResponseDataStatusMayBeMissingOrWrong = true, - ResponseDataRefundMayBeMissingOrWrong = true + PreferMisspeltContractPresentIndicator = true } }, VariableRecurringPaymentsApiSettings = new VariableRecurringPaymentsApiSettings diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 985c3e049..0e6667e2c 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 18.2.0 + 18.2.1 Finnovation Labs Limited and Contributors Finnovation Labs Limited false From 031c212b13b2d74bf8823aaa9853c362d63c15bb Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 31 Mar 2026 01:31:29 +0100 Subject: [PATCH 04/49] Update .NET dependencies --- .../OpenBanking.Library.BankApiModels.csproj | 2 +- .../OpenBanking.Library.Connector.GenericHost.csproj | 6 +++--- .../OpenBanking.Library.Connector.Web.csproj | 6 +++--- .../OpenBanking.Library.Connector.csproj | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj index 80779701b..86823b679 100644 --- a/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj +++ b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index 28ae72ee6..904da6519 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,9 +24,9 @@ - - - + + + diff --git a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj index 5d298e78a..486290bc4 100644 --- a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj +++ b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj @@ -27,9 +27,9 @@ - - - + + + diff --git a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index 50c5ac7ba..b61529639 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -26,14 +26,14 @@ - + - + From b547a65de6d4c2ee5541007b42f97f2032204d9b Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 31 Mar 2026 01:27:37 +0100 Subject: [PATCH 05/49] Add Wise bank --- .../BankProfiles/BankGroups/BankGroup.cs | 5 +- .../BankGroups/BankGroupExtensions.cs | 3 +- .../BankProfiles/BankGroups/Wise.cs | 23 ++++ .../BankProfiles/BankProfileEnum.cs | 5 +- .../BankProfiles/BankProfileExtensions.cs | 1 + .../BankProfiles/BankProfileService.cs | 4 +- .../BankProfiles/Generators/WiseGenerator.cs | 119 ++++++++++++++++++ .../GlobalUsings.cs | 4 + .../Management/BankRegistrationOperations.cs | 1 + .../BankTests/AppTests.cs | 4 + .../GlobalUsings.cs | 4 + 11 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Wise.cs create mode 100644 src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs index efefcee45..fb8a0a9ad 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs @@ -54,5 +54,8 @@ public enum BankGroup Tide, [EnumMember(Value = "Tsb")] - Tsb + Tsb, + + [EnumMember(Value = "Wise")] + Wise } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs index f03d766f4..4b5bb46f1 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs @@ -24,7 +24,8 @@ public static class BankGroupExtensions [BankGroup.Santander] = new Santander(), [BankGroup.Starling] = new Starling(), [BankGroup.Tide] = new Tide(), - [BankGroup.Tsb] = new Tsb() + [BankGroup.Tsb] = new Tsb(), + [BankGroup.Wise] = new Wise() }; public static IBankGroupData GetBankGroupData(this BankGroup bankGroup) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Wise.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Wise.cs new file mode 100644 index 000000000..fd01745f2 --- /dev/null +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Wise.cs @@ -0,0 +1,23 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; + +public enum WiseBank +{ + Wise +} + +public class Wise() : BankGroupBase(BankGroup.Wise) +{ + protected override ConcurrentDictionary BankProfileToBank { get; } = + new() { [BankProfileEnum.Wise_Wise] = WiseBank.Wise }; + + public override WiseRegistrationGroup GetRegistrationGroup( + WiseBank bank, + RegistrationScopeEnum registrationScopeEnum) => bank; +} diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index fc797e4ce..717fa0dba 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs @@ -178,5 +178,8 @@ public enum BankProfileEnum Tide_Tide, [EnumMember(Value = "Tsb_Tsb")] - Tsb_Tsb + Tsb_Tsb, + + [EnumMember(Value = "Wise_Wise")] + Wise_Wise } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs index d93456c7d..811eea873 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs @@ -66,6 +66,7 @@ public static BankGroup GetBankGroup(this BankProfileEnum bankProfileEnum) => BankProfileEnum.Starling_Starling => BankGroup.Starling, BankProfileEnum.Tide_Tide => BankGroup.Tide, BankProfileEnum.Tsb_Tsb => BankGroup.Tsb, + BankProfileEnum.Wise_Wise => BankGroup.Wise, _ => throw new ArgumentOutOfRangeException(nameof(bankProfileEnum), bankProfileEnum, null) }; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs index f724c0dbe..90d3028fd 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs @@ -55,7 +55,8 @@ public BankProfileService( [BankGroup.Santander] = new SantanderGenerator(bankProfilesSettingsProvider), [BankGroup.Starling] = new StarlingGenerator(bankProfilesSettingsProvider), [BankGroup.Tide] = new TideGenerator(bankProfilesSettingsProvider), - [BankGroup.Tsb] = new TsbGenerator(bankProfilesSettingsProvider) + [BankGroup.Tsb] = new TsbGenerator(bankProfilesSettingsProvider), + [BankGroup.Wise] = new WiseGenerator(bankProfilesSettingsProvider) }; } @@ -82,6 +83,7 @@ public BankProfile GetBankProfile(BankProfileEnum bankProfileEnum) => BankGroup.Starling => GetBankProfile(profileEnum, _instrumentationClient), BankGroup.Tide => GetBankProfile(profileEnum, _instrumentationClient), BankGroup.Tsb => GetBankProfile(profileEnum, _instrumentationClient), + BankGroup.Wise => GetBankProfile(profileEnum, _instrumentationClient), _ => throw new ArgumentOutOfRangeException() }, LazyThreadSafetyMode.ExecutionAndPublication)) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs new file mode 100644 index 000000000..e36c6e89b --- /dev/null +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs @@ -0,0 +1,119 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour.PaymentInitiation; +using FinnovationLabs.OpenBanking.Library.Connector.Configuration; +using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Configuration; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.Generators; + +public class WiseGenerator : BankProfileGeneratorBase +{ + public WiseGenerator( + ISettingsProvider bankProfilesSettingsProvider) : base( + bankProfilesSettingsProvider, + BankGroup.Wise) { } + + public override BankProfile GetBankProfile( + WiseBank bank, + IInstrumentationClient instrumentationClient) + { + var authCodeGrantPostCustomBehaviour = + new AuthCodeGrantPostCustomBehaviour + { + ResponseTokenTypeCaseMayBeIncorrect = true, + ResponseScopeMayIncludeExtraValues = true + }; + return new BankProfile( + _bankGroupData.GetBankProfile(bank), + "https://openbanking.transferwise.com", // from https://docs.wise.com/guides/developer/open-banking + GetFinancialId(bank), + new AccountAndTransactionApi { BaseUrl = GetApiBaseUrl("aisp") }, + null, + new PaymentInitiationApi { BaseUrl = GetApiBaseUrl("pisp") }, + null, + null, + null, + true, + instrumentationClient) + { + DynamicClientRegistrationApiVersion = DynamicClientRegistrationApiVersion.Version3p3, + AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings + { + AccountAccessConsentTemplateExternalApiRequestAdjustments = externalApiRequest => + { + List elementsToRemove = + [ + AccountAndTransactionModelsPublic.Permissions.ReadBeneficiariesBasic, + AccountAndTransactionModelsPublic.Permissions.ReadBeneficiariesDetail, + AccountAndTransactionModelsPublic.Permissions.ReadOffers, + AccountAndTransactionModelsPublic.Permissions.ReadPAN, + AccountAndTransactionModelsPublic.Permissions.ReadParty, + AccountAndTransactionModelsPublic.Permissions.ReadPartyPSU, + AccountAndTransactionModelsPublic.Permissions.ReadProducts, + AccountAndTransactionModelsPublic.Permissions.ReadScheduledPaymentsBasic, + AccountAndTransactionModelsPublic.Permissions.ReadScheduledPaymentsDetail, + AccountAndTransactionModelsPublic.Permissions.ReadStandingOrdersBasic, + AccountAndTransactionModelsPublic.Permissions.ReadStandingOrdersDetail, + AccountAndTransactionModelsPublic.Permissions.ReadStatementsBasic, + AccountAndTransactionModelsPublic.Permissions.ReadStatementsDetail + ]; + foreach (AccountAndTransactionModelsPublic.Permissions element in + elementsToRemove) + { + externalApiRequest.Data.Permissions.Remove(element); + } + return externalApiRequest; + } + }, + PaymentInitiationApiSettings = new PaymentInitiationApiSettings + { + PreferPartyToPartyPaymentContextCode = true, + UseContractPresentIndicator = false, + UseReadRefundAccount = false + }, + CustomBehaviour = new CustomBehaviourClass + { + OpenIdConfigurationGet = new OpenIdConfigurationGetCustomBehaviour + { + Url = + "https://wise.com/openbanking/.well-known/openid-configuration" // from https://docs.wise.com/guides/developer/open-banking + }, + ClientCredentialsGrantPost = + new ClientCredentialsGrantPostCustomBehaviour + { + ResponseTokenTypeCaseMayBeIncorrect = true, + ResponseScopeMayIncludeExtraValues = true + }, + AccountAccessConsentAuthCodeGrantPost = + authCodeGrantPostCustomBehaviour, + AccountAccessConsentRefreshTokenGrantPost = new RefreshTokenGrantPostCustomBehaviour + { + ResponseTokenTypeCaseMayBeIncorrect = true, + ResponseScopeMayIncludeExtraValues = true, + IdTokenMayBeAbsent = true + }, + DomesticPaymentConsentAuthCodeGrantPost = + authCodeGrantPostCustomBehaviour, + DomesticPayment = new DomesticPaymentCustomBehaviour + { + ResponseDataRefundMayBeMissingOrWrong = true, + ResponseDataDebtorMayBeMissingOrWrong = true + } + }, + DefaultResponseMode = OAuth2ResponseMode.Query, + AspspBrandId = 0 + }; + } + + private static string GetApiBaseUrl(string suffix) => + $"https://openbanking.transferwise.com/open-banking/v3.1.11/{suffix}"; // from https://docs.wise.com/guides/developer/open-banking +} diff --git a/src/OpenBanking.Library.Connector/GlobalUsings.cs b/src/OpenBanking.Library.Connector/GlobalUsings.cs index 7e91a9519..2aa7accae 100644 --- a/src/OpenBanking.Library.Connector/GlobalUsings.cs +++ b/src/OpenBanking.Library.Connector/GlobalUsings.cs @@ -34,6 +34,10 @@ FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TideBank; global using TsbBank = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; +global using WiseBank = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; +global using WiseRegistrationGroup = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; global using TsbRegistrationGroup = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; diff --git a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs index f5e191443..a529dab47 100644 --- a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs @@ -285,6 +285,7 @@ await _configurationRead.GetOpenIdConfigurationAsync( BankGroup.Starling => GetExistingRegistration, BankGroup.Tide => GetExistingRegistration, BankGroup.Tsb => GetExistingRegistration, + BankGroup.Wise => GetExistingRegistration, _ => throw new ArgumentOutOfRangeException() }; (BankRegistrationEntity? existingGroupRegistration, diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index e8e17cd84..5b17b03ce 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs @@ -239,6 +239,10 @@ public static IEnumerable GetTestCases(TestType testType) bankProfileFromEnv, registrationScope, bankGroup), + BankGroup.Wise => GetAllInRegistrationGroup( + bankProfileFromEnv, + registrationScope, + bankGroup), _ => throw new ArgumentOutOfRangeException() }; } diff --git a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs index 7e91a9519..2aa7accae 100644 --- a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs +++ b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs @@ -34,6 +34,10 @@ FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TideBank; global using TsbBank = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; +global using WiseBank = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; +global using WiseRegistrationGroup = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; global using TsbRegistrationGroup = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; From ad1c3dcd35e0d4a847b57c9ebf2f82486cfe186a Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:47:25 +0100 Subject: [PATCH 06/49] [Docs] Update docs --- docs/apis/account-and-transaction/openapi-aisp.json | 2 +- docs/apis/auth-contexts/openapi-auth.json | 2 +- docs/apis/management/openapi-manage.json | 8 +++++--- docs/apis/payment-initiation/openapi-pisp.json | 2 +- docs/apis/variable-recurring-payments/openapi-vrp.json | 2 +- docs/bank-integrations.md | 3 ++- .../OpenBanking.WebApp.Connector.csproj | 2 +- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index b8f8dfb5e..7f5249960 100644 --- a/docs/apis/account-and-transaction/openapi-aisp.json +++ b/docs/apis/account-and-transaction/openapi-aisp.json @@ -3,7 +3,7 @@ "info": { "title": "Account and Transaction API", "description": "Account and Transaction API for Open Banking Connector Web App", - "version": "18.2.0" + "version": "18.3.0" }, "paths": { "/aisp/account-access-consent-auth-contexts": { diff --git a/docs/apis/auth-contexts/openapi-auth.json b/docs/apis/auth-contexts/openapi-auth.json index 6456bce83..ffac0dad1 100644 --- a/docs/apis/auth-contexts/openapi-auth.json +++ b/docs/apis/auth-contexts/openapi-auth.json @@ -3,7 +3,7 @@ "info": { "title": "Auth Contexts API", "description": "Auth Contexts API for Open Banking Connector Web App", - "version": "18.2.0" + "version": "18.3.0" }, "paths": { "/auth/redirect-delegate": { diff --git a/docs/apis/management/openapi-manage.json b/docs/apis/management/openapi-manage.json index f6694b022..7e6f5cce6 100644 --- a/docs/apis/management/openapi-manage.json +++ b/docs/apis/management/openapi-manage.json @@ -3,7 +3,7 @@ "info": { "title": "Management API", "description": "Management API for Open Banking Connector Web App", - "version": "18.2.0" + "version": "18.3.0" }, "paths": { "/manage/bank-registrations": { @@ -730,7 +730,8 @@ "Santander", "Starling", "Tide", - "Tsb" + "Tsb", + "Wise" ], "type": "string" }, @@ -790,7 +791,8 @@ "Santander_Corporate", "Starling_Starling", "Tide_Tide", - "Tsb_Tsb" + "Tsb_Tsb", + "Wise_Wise" ], "type": "string", "description": "Banks for which bank profiles have been created. Called BankProfileEnum to avoid confusion with the associated\r\nclass BankProfile." diff --git a/docs/apis/payment-initiation/openapi-pisp.json b/docs/apis/payment-initiation/openapi-pisp.json index 1a2a2fa42..22e7b1c60 100644 --- a/docs/apis/payment-initiation/openapi-pisp.json +++ b/docs/apis/payment-initiation/openapi-pisp.json @@ -3,7 +3,7 @@ "info": { "title": "Payment Initiation API", "description": "Payment Initiation API for Open Banking Connector Web App", - "version": "18.2.0" + "version": "18.3.0" }, "paths": { "/pisp/domestic-payment-consent-auth-contexts": { diff --git a/docs/apis/variable-recurring-payments/openapi-vrp.json b/docs/apis/variable-recurring-payments/openapi-vrp.json index 9300585b5..3ecc07909 100644 --- a/docs/apis/variable-recurring-payments/openapi-vrp.json +++ b/docs/apis/variable-recurring-payments/openapi-vrp.json @@ -3,7 +3,7 @@ "info": { "title": "Variable Recurring Payments API", "description": "Variable Recurring Payments API for Open Banking Connector Web App", - "version": "18.2.0" + "version": "18.3.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/docs/bank-integrations.md b/docs/bank-integrations.md index 1222991ac..f9ebfeaee 100644 --- a/docs/bank-integrations.md +++ b/docs/bank-integrations.md @@ -19,4 +19,5 @@ UK Open Banking uses the [Financial-grade API](https://openid.net/specs/openid-f | Santander | Personal
Business
Corporate | :white_large_square: | Santander_Personal
Santander_Business
Santander_Corporate | AISP,
PISP (domestic payments),
VRP | | Starling | All brands | :white_large_square: | Starling_Starling | AISP | | Tide | All brands | :white_large_square: | Tide_Tide | AISP,
PISP (domestic payments) | -| TSB | All brands | :white_large_square: | Tsb_Tsb | AISP,
PISP (domestic payments) | \ No newline at end of file +| TSB | All brands | :white_large_square: | Tsb_Tsb | AISP,
PISP (domestic payments) | +| Wise | All brands | :white_large_square: | Wise_Wise | AISP,
PISP (domestic payments) | diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 0e6667e2c..ef0bc789c 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 18.2.1 + 18.3.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false From 58b72b0e29d34e54dab3352dace03b8c44d829ad Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 1 May 2026 19:02:27 +0100 Subject: [PATCH 07/49] Update .NET dependencies --- ...nBanking.Library.Connector.GenericHost.csproj | 16 ++++++++-------- .../OpenBanking.Library.Connector.Web.csproj | 14 +++++++------- .../OpenBanking.Library.Connector.csproj | 8 ++++---- .../OpenBanking.WebApp.Connector.csproj | 4 ++-- ...penBanking.Library.Connector.BankTests.csproj | 12 ++++++------ ...ing.Library.Connector.IntegrationTests.csproj | 4 ++-- ...penBanking.Library.Connector.UnitTests.csproj | 4 ++-- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index 904da6519..3eada0f87 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,14 +24,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj index 486290bc4..b7572cd1e 100644 --- a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj +++ b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj @@ -25,14 +25,14 @@ - - - - - - + + + + + + - + diff --git a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index b61529639..5a892b115 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -27,13 +27,13 @@ - - - + + + - + diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index ef0bc789c..9acbfe087 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 66e57d79d..5043afaca 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -14,11 +14,11 @@ - - - - - + + + + + diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj index dcfb51202..d0d630afc 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj +++ b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj @@ -13,8 +13,8 @@ - - + + all diff --git a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj index 4ad644e82..d2bb352bb 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj +++ b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj @@ -13,8 +13,8 @@ - - + + all From 67eb03a8433b5311365e5398174b2dd785849cee Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 14 Apr 2026 12:27:02 +0100 Subject: [PATCH 08/49] Update Starling --- .../Generators/StarlingGenerator.cs | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs index d59b77306..162c76913 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs @@ -5,12 +5,14 @@ using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour.Management; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour.PaymentInitiation; using FinnovationLabs.OpenBanking.Library.Connector.Configuration; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Configuration; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.Generators; @@ -28,9 +30,9 @@ public override BankProfile GetBankProfile( _bankGroupData.GetBankProfile(bank), "https://api-openbanking.starlingbank.com/", // from https://developer.starlingbank.com/docs/open-banking#the-openid-connect-discovery-url-1 GetFinancialId(bank), - GetAccountAndTransactionApi(bank), - null, + new AccountAndTransactionApi { BaseUrl = GetApiBaseUrl("aisp") }, null, + new PaymentInitiationApi { BaseUrl = GetApiBaseUrl("pisp") }, null, null, null, @@ -57,20 +59,40 @@ public override BankProfile GetBankProfile( }, AccountAccessConsentRefreshTokenGrantPost = new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, + DomesticPaymentConsentAuthGet = new ConsentAuthGetCustomBehaviour + { + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenMayNotHaveAcrClaim = true } + }, + DomesticPaymentConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour + { + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenMayNotHaveAcrClaim = true }, + ExpectedResponseRefreshTokenMayBeAbsent = true + }, + DomesticPayment = new DomesticPaymentCustomBehaviour + { + PostResponseLinksMayOmitId = true, + ResponseDataDebtorMayBeMissingOrWrong = true + }, + DomesticPaymentConsent = + new DomesticPaymentConsentCustomBehaviour { PostResponseLinksMayOmitId = true }, OpenIdConfigurationGet = new OpenIdConfigurationGetCustomBehaviour { Url = "https://openbanking.starlingbank.com/.well-known/openid-configuration" // from https://developer.starlingbank.com/docs/open-banking#the-openid-connect-discovery-url-1 } }, + PaymentInitiationApiSettings = new PaymentInitiationApiSettings + { + PreferPartyToPartyPaymentContextCode = true, + UseContractPresentIndicator = false + }, DefaultResponseMode = OAuth2ResponseMode.Query, AspspBrandId = 1510 }; - private AccountAndTransactionApi GetAccountAndTransactionApi(StarlingBank bank) => - new() - { - BaseUrl = - "https://api-openbanking.starlingbank.com/open-banking/v3.1/aisp" // from https://developer.starlingbank.com/docs/open-banking#account-and-transaction-api-1 - }; + private static string GetApiBaseUrl(string suffix) => + $"https://api-openbanking.starlingbank.com/open-banking/v3.1/{suffix}"; // from https://developer.starlingbank.com/docs/open-banking#account-and-transaction-api-1, + // https://developer.starlingbank.com/docs/open-banking#payment-initiation-api-1 } From f5faa9a9687f40162c966f42d3234a1699e7c213 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 5 May 2026 16:50:33 +0100 Subject: [PATCH 09/49] Remove unused bank profile property --- .../BankProfiles/BankProfile.cs | 2 -- .../Generators/BarclaysGenerator.cs | 3 --- .../BankProfiles/Generators/ChaseGenerator.cs | 3 +-- .../Generators/CooperativeGenerator.cs | 3 +-- .../Generators/DanskeGenerator.cs | 3 +-- .../BankProfiles/Generators/HsbcGenerator.cs | 10 --------- .../Generators/LloydsGenerator.cs | 13 ------------ .../BankProfiles/Generators/MonzoGenerator.cs | 6 ------ .../Generators/NatWestGenerator.cs | 21 ------------------- .../Generators/NationwideGenerator.cs | 1 - .../BankProfiles/Generators/ObieGenerator.cs | 3 +-- .../Generators/RevolutGenerator.cs | 3 +-- .../Generators/SantanderGenerator.cs | 3 +-- .../Generators/StarlingGenerator.cs | 3 +-- .../BankProfiles/Generators/TideGenerator.cs | 3 +-- .../BankProfiles/Generators/TsbGenerator.cs | 3 +-- .../BankProfiles/Generators/WiseGenerator.cs | 3 +-- 17 files changed, 10 insertions(+), 76 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs index 9d2ce899f..56da813d2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs @@ -306,8 +306,6 @@ public OAuth2ResponseMode DefaultResponseMode public IApiClient ReplayApiClient { get; } - public required int AspspBrandId { get; init; } - public bool AispUseV4ByDefault { get; init; } = false; public bool PispUseV4ByDefault { get; init; } = false; diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs index eae8478fd..48ec4541b 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs @@ -211,9 +211,6 @@ or BarclaysBank.BarclaycardCommercialPayments }, UseDomesticVrpConsentPutEndpoint = true }, - AspspBrandId = bank is BarclaysBank.Sandbox - ? 10006 // sandbox - : 5, AispUseV4ByDefault = true, PispUseV4ByDefault = true, VrpUseV4ByDefault = true diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs index 837ddde8e..1409096ba 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs @@ -95,8 +95,7 @@ public override BankProfile GetBankProfile( ResponseDataRefundMayBeMissingOrWrong = true }, ClientCredentialsGrantPost = new ClientCredentialsGrantPostCustomBehaviour() - }, - AspspBrandId = 0 + } }; private static string GetApiBaseUrl(string suffix) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/CooperativeGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/CooperativeGenerator.cs index 1726492c2..1b13c2ad6 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/CooperativeGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/CooperativeGenerator.cs @@ -133,8 +133,7 @@ public override BankProfile GetBankProfile( return externalApiRequest; }, UseReauth = bank is not CooperativeBank.CooperativeSandbox - }, - AspspBrandId = 0 + } }; private static string GetIssuer(CooperativeBank bank) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/DanskeGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/DanskeGenerator.cs index 9d6897983..a2fda6ba9 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/DanskeGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/DanskeGenerator.cs @@ -78,7 +78,6 @@ public override BankProfile GetBankProfile( ResponseDataDebtorIdentificationMayBeMissingOrWrong = true, ResponseDataRefundMayBeMissingOrWrong = true } - }, - AspspBrandId = 10007 // sandbox + } }; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs index 9c60786ea..3d66f46ec 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs @@ -150,16 +150,6 @@ public override BankProfile GetBankProfile( DefaultResponseMode = bank is HsbcBank.Sandbox or HsbcBank.UkBusiness ? OAuth2ResponseMode.Query : OAuth2ResponseMode.Fragment, - AspspBrandId = bank switch - { - HsbcBank.FirstDirect => 7, - HsbcBank.Sandbox => 10005, // sandbox - HsbcBank.UkBusiness => 17, - HsbcBank.UkKinetic => 20, - HsbcBank.UkPersonal => 9, - HsbcBank.HsbcNetUk => 9, - _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) - }, AispUseV4ByDefault = true, PispUseV4ByDefault = true, VrpUseV4ByDefault = true diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs index 0189660c4..57530a0c4 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs @@ -247,19 +247,6 @@ or LloydsBank.BankOfScotlandBusiness return externalApiRequest; } }, - AspspBrandId = bank switch - { - LloydsBank.Sandbox => 10004, // sandbox - LloydsBank.LloydsPersonal - or LloydsBank.LloydsBusiness - or LloydsBank.LloydsCommerical => 10, - LloydsBank.HalifaxPersonal => 8, - LloydsBank.BankOfScotlandPersonal - or LloydsBank.BankOfScotlandBusiness - or LloydsBank.BankOfScotlandCommerical => 4, - LloydsBank.MbnaPersonal => 18, - _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) - }, AispUseV4ByDefault = true, PispUseV4ByDefault = true, VrpUseV4ByDefault = true diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/MonzoGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/MonzoGenerator.cs index fb892bb6b..10bc1977c 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/MonzoGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/MonzoGenerator.cs @@ -141,12 +141,6 @@ public override BankProfile GetBankProfile( return externalApiRequest; } : x => x - }, - AspspBrandId = bank switch - { - MonzoBank.Sandbox => 10003, // sandbox - MonzoBank.Monzo => 1430, - _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) } }; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs index daee9e8d6..4b150a50e 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs @@ -278,27 +278,6 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox } }, - AspspBrandId = bank switch - { - NatWestBank.NatWestSandbox => 100001, // sandbox - NatWestBank.NatWest - or NatWestBank.NatWestBankline - or NatWestBank.NatWestClearSpend - or NatWestBank.Mettle => 13, - NatWestBank.RoyalBankOfScotlandSandbox => 100002, // sandbox - NatWestBank.RoyalBankOfScotland - or NatWestBank.RoyalBankOfScotlandBankline - or NatWestBank.RoyalBankOfScotlandClearSpend - or NatWestBank.TheOne - or NatWestBank.NatWestOne - or NatWestBank.VirginOne => 14, - NatWestBank.UlsterBankNi - or NatWestBank.UlsterBankNiBankline - or NatWestBank.UlsterBankNiClearSpend => - 13, - NatWestBank.Coutts => 13, - _ => throw new ArgumentOutOfRangeException() - }, AispUseV4ByDefault = true, PispUseV4ByDefault = true, VrpUseV4ByDefault = true diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs index bd4b7997e..2c4b079a7 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs @@ -187,7 +187,6 @@ public override BankProfile GetBankProfile( }, UseGetPartyEndpoint = false }, - AspspBrandId = 12, AispUseV4ByDefault = true, PispUseV4ByDefault = true, VrpUseV4ByDefault = true diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ObieGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ObieGenerator.cs index b5f5e75d8..d2cdb090f 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ObieGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ObieGenerator.cs @@ -62,8 +62,7 @@ public override BankProfile GetBankProfile( ResponseDataDebtorAccountMayBeMissingOrWrong = true } }, - BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationDeleteEndpoint = true }, - AspspBrandId = 10000 // sandbox + BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationDeleteEndpoint = true } }; private static PaymentInitiationApi GetPaymentInitiationApi(ObieBank bank) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs index b14bfbe69..f3179cf99 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/RevolutGenerator.cs @@ -129,8 +129,7 @@ public override BankProfile GetBankProfile( PreferPartyToPartyPaymentContextCode = true, UseContractPresentIndicator = false }, - DefaultResponseMode = OAuth2ResponseMode.Query, - AspspBrandId = 1470 + DefaultResponseMode = OAuth2ResponseMode.Query }; private static string GetBaseUrl() => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs index 0a6234bc1..6a3df3440 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs @@ -148,8 +148,7 @@ bank is not SantanderBank.Santander }, AispUseV4ByDefault = bank is not SantanderBank.Santander, PispUseV4ByDefault = bank is not SantanderBank.Santander, - VrpUseV4ByDefault = bank is not SantanderBank.Santander, - AspspBrandId = 15 + VrpUseV4ByDefault = bank is not SantanderBank.Santander }; private static string GetV4ApiBaseUrl(SantanderBank bank, string suffix) => bank switch diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs index 162c76913..60043d1f4 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/StarlingGenerator.cs @@ -88,8 +88,7 @@ public override BankProfile GetBankProfile( PreferPartyToPartyPaymentContextCode = true, UseContractPresentIndicator = false }, - DefaultResponseMode = OAuth2ResponseMode.Query, - AspspBrandId = 1510 + DefaultResponseMode = OAuth2ResponseMode.Query }; private static string GetApiBaseUrl(string suffix) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TideGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TideGenerator.cs index 4d6b9f341..f756cf1bb 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TideGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TideGenerator.cs @@ -66,8 +66,7 @@ public override BankProfile GetBankProfile( UseReadRefundAccount = false, PreferPartyToPartyPaymentContextCode = true, UseContractPresentIndicator = false - }, - AspspBrandId = 0 + } }; private static string GetApiBaseUrl(string suffix) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs index 0f08602e9..c86f627e0 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs @@ -82,8 +82,7 @@ public override BankProfile GetBankProfile( ResponseDataRefundMayBeMissingOrWrong = true, ResponseDataDebtorMayBeMissingOrWrong = true } - }, - AspspBrandId = 0 + } }; private static string GetApiBaseUrl(bool isAisp) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs index e36c6e89b..d5d88ed11 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs @@ -109,8 +109,7 @@ public override BankProfile GetBankProfile( ResponseDataDebtorMayBeMissingOrWrong = true } }, - DefaultResponseMode = OAuth2ResponseMode.Query, - AspspBrandId = 0 + DefaultResponseMode = OAuth2ResponseMode.Query }; } From 5eaa08895384352b4af98c63007ac5fc30a7015b Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 5 May 2026 15:32:37 +0100 Subject: [PATCH 10/49] Add JsonConverter customisation options for DCR response --- .../Json/JsonConverterLabel.cs | 2 ++ .../V3p1/Models/OBClientRegistration1.cs | 8 ++++++-- .../V3p2/Models/OBClientRegistration1.cs | 8 ++++++-- .../V3p3/Models/OBClientRegistration1.cs | 8 ++++++-- .../BankRegistrationPostCustomBehaviour.cs | 6 ++++++ .../Management/BankRegistrationOperations.cs | 19 +++++++++++++++++++ 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/OpenBanking.Library.BankApiModels/Json/JsonConverterLabel.cs b/src/OpenBanking.Library.BankApiModels/Json/JsonConverterLabel.cs index 2c722d13d..9438aa6e8 100644 --- a/src/OpenBanking.Library.BankApiModels/Json/JsonConverterLabel.cs +++ b/src/OpenBanking.Library.BankApiModels/Json/JsonConverterLabel.cs @@ -14,6 +14,8 @@ namespace FinnovationLabs.OpenBanking.Library.BankApiModels.Json /// public enum JsonConverterLabel { + DcrRegIssuedAt, + DcrRegExpirationTime, DcrRegClientIdIssuedAt, DcrRegScope, IdTokenExpirationTimeClaim, diff --git a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p1/Models/OBClientRegistration1.cs b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p1/Models/OBClientRegistration1.cs index a5b9fa46f..04824ffae 100644 --- a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p1/Models/OBClientRegistration1.cs +++ b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p1/Models/OBClientRegistration1.cs @@ -126,7 +126,9 @@ public OBClientRegistration1( /// expressed as seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "iat")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegIssuedAt)] public DateTimeOffset Iat { get; set; } /// @@ -134,7 +136,9 @@ public OBClientRegistration1( /// seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "exp")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegExpirationTime)] public DateTimeOffset Exp { get; set; } /// diff --git a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p2/Models/OBClientRegistration1.cs b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p2/Models/OBClientRegistration1.cs index 615660488..507307074 100644 --- a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p2/Models/OBClientRegistration1.cs +++ b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p2/Models/OBClientRegistration1.cs @@ -127,7 +127,9 @@ public OBClientRegistration1( /// expressed as seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "iat")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegIssuedAt)] public DateTimeOffset Iat { get; set; } /// @@ -135,7 +137,9 @@ public OBClientRegistration1( /// seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "exp")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegExpirationTime)] public DateTimeOffset Exp { get; set; } /// diff --git a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p3/Models/OBClientRegistration1.cs b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p3/Models/OBClientRegistration1.cs index 4878fcbd7..59cb3c1d9 100644 --- a/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p3/Models/OBClientRegistration1.cs +++ b/src/OpenBanking.Library.BankApiModels/UkObDcr/V3p3/Models/OBClientRegistration1.cs @@ -124,7 +124,9 @@ public OBClientRegistration1() /// expressed as seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "iat")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegIssuedAt)] public DateTimeOffset Iat { get; set; } /// @@ -132,7 +134,9 @@ public OBClientRegistration1() /// seconds since 1970-01-01T00:00:00Z as measured in UTC /// [JsonProperty(PropertyName = "exp")] - [JsonConverter(typeof(DateTimeOffsetUnixConverter))] + [JsonConverter( + typeof(DateTimeOffsetUnixConverter), + JsonConverterLabel.DcrRegExpirationTime)] public DateTimeOffset Exp { get; set; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/Management/BankRegistrationPostCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/Management/BankRegistrationPostCustomBehaviour.cs index 9234755f1..7557247cf 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/Management/BankRegistrationPostCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/Management/BankRegistrationPostCustomBehaviour.cs @@ -16,6 +16,12 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehav /// public class BankRegistrationPostCustomBehaviour { + // JsonConverter to use for IssuedAt claim response + public DateTimeOffsetUnixConverterEnum? IssuedAtClaimResponseJsonConverter { get; set; } + + // JsonConverter to use for ExpirationTime claim response + public DateTimeOffsetUnixConverterEnum? ExpirationTimeClaimResponseJsonConverter { get; set; } + // JsonConverter to use for ClientIdIssuedAt claim response public DateTimeOffsetUnixConverterEnum? ClientIdIssuedAtClaimResponseJsonConverter { get; set; } diff --git a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs index a529dab47..aa7ce1ab6 100644 --- a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs @@ -1260,9 +1260,28 @@ private static void CheckRedirectUris( { var optionsDict = new Dictionary(); + + DateTimeOffsetUnixConverterEnum? issuedAtClaimResponseJsonConverter = + bankRegistrationPostCustomBehaviour + .IssuedAtClaimResponseJsonConverter; + DateTimeOffsetUnixConverterEnum? expirationTimeClaimResponseJsonConverter = + bankRegistrationPostCustomBehaviour + .ExpirationTimeClaimResponseJsonConverter; DateTimeOffsetUnixConverterEnum? clientIdIssuedAtClaimResponseJsonConverter = bankRegistrationPostCustomBehaviour .ClientIdIssuedAtClaimResponseJsonConverter; + if (issuedAtClaimResponseJsonConverter is not null) + { + optionsDict.Add( + JsonConverterLabel.DcrRegIssuedAt, + (int) issuedAtClaimResponseJsonConverter); + } + if (expirationTimeClaimResponseJsonConverter is not null) + { + optionsDict.Add( + JsonConverterLabel.DcrRegExpirationTime, + (int) expirationTimeClaimResponseJsonConverter); + } if (clientIdIssuedAtClaimResponseJsonConverter is not null) { optionsDict.Add( From f931822df19e474b58541c2446c34de4dd0c10b8 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Wed, 6 May 2026 06:43:20 +0100 Subject: [PATCH 11/49] Support explicit scope in certain token endpoint requests --- .../AuthCodeGrantPostCustomBehaviour.cs | 2 ++ .../RefreshTokenGrantPostCustomBehaviour.cs | 2 ++ .../Operations/ExternalApi/GrantPost.cs | 12 ++++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/AuthCodeGrantPostCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/AuthCodeGrantPostCustomBehaviour.cs index 8d3af4da1..9cd7c853a 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/AuthCodeGrantPostCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/AuthCodeGrantPostCustomBehaviour.cs @@ -16,4 +16,6 @@ public class AuthCodeGrantPostCustomBehaviour : GrantPostCustomBehaviour /// Use to specify custom scope. /// public string? Scope { get; init; } + + public bool? UseScopeInRequest { get; init; } } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/RefreshTokenGrantPostCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/RefreshTokenGrantPostCustomBehaviour.cs index 39259708f..78d4093cf 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/RefreshTokenGrantPostCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/RefreshTokenGrantPostCustomBehaviour.cs @@ -14,4 +14,6 @@ public class RefreshTokenGrantPostCustomBehaviour : GrantPostCustomBehaviour /// Use to specify custom scope. /// public string? Scope { get; init; } + + public bool? UseScopeInRequest { get; init; } } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs index 5070280be..6c01698c2 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs @@ -246,6 +246,12 @@ public async Task PostAuthCodeGrantAsync( { "code", authCode } }; + bool useScopeInRequest = authCodeGrantPostCustomBehaviour?.UseScopeInRequest ?? false; + if (useScopeInRequest) + { + keyValuePairs["scope"] = requestScope; + } + if (codeVerifier is not null) { keyValuePairs["code_verifier"] = codeVerifier; @@ -380,6 +386,12 @@ public async Task PostRefreshTokenGrantAsync( { "refresh_token", refreshToken } }; + bool useScopeInRequest = refreshTokenGrantPostCustomBehaviour?.UseScopeInRequest ?? false; + if (useScopeInRequest) + { + keyValuePairs["scope"] = refreshTokenScope; + } + if (tokenEndpointAuthMethod is TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt) { From 1fd96d53bd21409668ffb42bb5b029fc97c5136c Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Wed, 6 May 2026 08:55:56 +0100 Subject: [PATCH 12/49] Add BaseIdTokenProcessingCustomBehaviour and new IdTokenSubClaimType (ClientId) --- .../BankProfiles/BankProfile.cs | 5 - .../CustomBehaviour/CustomBehaviourClass.cs | 6 ++ .../IdTokenProcessingCustomBehaviour.cs | 60 ++++++++++++ .../Generators/BarclaysGenerator.cs | 5 +- .../BankProfiles/Generators/ChaseGenerator.cs | 5 +- .../Generators/LloydsGenerator.cs | 7 +- .../Generators/NatWestGenerator.cs | 7 +- .../Generators/SantanderGenerator.cs | 3 +- .../BankProfiles/Generators/TsbGenerator.cs | 5 +- .../Public/Management/IdTokenSubClaimType.cs | 5 +- .../AccountAndTransaction/AccountGet.cs | 6 +- .../AccountAndTransaction/BalanceGet.cs | 6 +- .../AccountAndTransaction/DirectDebitGet.cs | 6 +- .../AccountAndTransaction/MonzoPotGet.cs | 6 +- .../AccountAndTransaction/Party2Get.cs | 6 +- .../AccountAndTransaction/PartyGet.cs | 6 +- .../AccountAndTransaction/StandingOrderGet.cs | 6 +- .../AccountAndTransaction/TransactionGet.cs | 6 +- .../Operations/AuthContextUpdate.cs | 16 +-- .../Operations/ConsentAccessTokenGet.cs | 9 +- .../Operations/ExternalApi/GrantPost.cs | 97 +++++++++++++------ .../Operations/ExternalApi/IGrantPost.cs | 11 +-- .../PaymentInitiation/DomesticPayment.cs | 6 +- .../DomesticPaymentConsentOperations.cs | 6 +- .../VariableRecurringPayments/DomesticVrp.cs | 6 +- .../DomesticVrpConsentOperations.cs | 6 +- 26 files changed, 200 insertions(+), 113 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs index 56da813d2..cfcb06b58 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs @@ -112,11 +112,6 @@ public bool UseRegistrationGetEndpoint public TokenEndpointAuthMethodSupportedValues TokenEndpointAuthMethod { get; set; } = TokenEndpointAuthMethodSupportedValues.TlsClientAuth; - - /// - /// ID token "sub" claim type. - /// - public IdTokenSubClaimType IdTokenSubClaimType { get; set; } = IdTokenSubClaimType.ConsentId; } public delegate string? GetFinancialId(bool useV4NotV3); diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/CustomBehaviourClass.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/CustomBehaviourClass.cs index eb923cef4..9106a9eba 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/CustomBehaviourClass.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/CustomBehaviourClass.cs @@ -24,6 +24,12 @@ public class CustomBehaviourClass // Client auth public ClientCredentialsGrantPostCustomBehaviour? ClientCredentialsGrantPost { get; set; } + /// + /// Base ID token processing behaviour used when no endpoint-specific behaviour specified + /// (overridden on per-property basis). + /// + public IdTokenProcessingCustomBehaviour? BaseIdTokenProcessingCustomBehaviour { get; init; } + // Consent auth endpoints public JwksGetCustomBehaviour? JwksGet { get; set; } public ConsentAuthGetCustomBehaviour? AccountAccessConsentAuthGet { get; set; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs index 7f3b33e2f..c354b6653 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs @@ -3,11 +3,17 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.BankApiModels.Json; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; public class IdTokenProcessingCustomBehaviour { + /// + /// ID token "sub" claim type. + /// + public IdTokenSubClaimType? IdTokenSubClaimType { get; set; } + public bool? DoNotValidateIdToken { get; set; } public bool? IdTokenNonceClaimIsPreviousValue { get; set; } @@ -25,4 +31,58 @@ public class IdTokenProcessingCustomBehaviour public string? IssClaim { get; set; } public DateTimeOffsetUnixConverterEnum? IdTokenExpirationTimeClaimJsonConverter { get; set; } + + public static bool GetDoNotValidateIdToken( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.DoNotValidateIdToken ?? baseCustomBehaviour?.DoNotValidateIdToken ?? false; + + public static bool GetIdTokenNonceClaimIsPreviousValue( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenNonceClaimIsPreviousValue ?? + baseCustomBehaviour?.IdTokenNonceClaimIsPreviousValue ?? false; + + public static IdTokenSubClaimType GetIdTokenSubClaimType( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenSubClaimType ?? baseCustomBehaviour?.IdTokenSubClaimType + ?? Models.Public.Management.IdTokenSubClaimType.ConsentId; + + public static bool GetIdTokenMayNotHaveAcrClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenMayNotHaveAcrClaim ?? baseCustomBehaviour?.IdTokenMayNotHaveAcrClaim ?? false; + + public static bool GetDoNotValidateIdTokenAcrClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.DoNotValidateIdTokenAcrClaim ?? baseCustomBehaviour?.DoNotValidateIdTokenAcrClaim ?? false; + + public static bool GetIdTokenMayNotHaveAuthTimeClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenMayNotHaveAuthTimeClaim ?? baseCustomBehaviour?.IdTokenMayNotHaveAuthTimeClaim ?? false; + + public static bool GetIdTokenMayNotHaveConsentIdClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenMayNotHaveConsentIdClaim ?? + baseCustomBehaviour?.IdTokenMayNotHaveConsentIdClaim ?? false; + + public static bool GetIdTokenMayNotHaveNonceClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenMayNotHaveNonceClaim ?? baseCustomBehaviour?.IdTokenMayNotHaveNonceClaim ?? false; + + public static string? GetIssClaim( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IssClaim ?? baseCustomBehaviour?.IssClaim; + + public static DateTimeOffsetUnixConverterEnum? GetIdTokenExpirationTimeClaimJsonConverter( + IdTokenProcessingCustomBehaviour? customBehaviour, + IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => + customBehaviour?.IdTokenExpirationTimeClaimJsonConverter ?? + baseCustomBehaviour?.IdTokenExpirationTimeClaimJsonConverter; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs index 48ec4541b..d309d706c 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs @@ -77,8 +77,7 @@ public override BankProfile GetBankProfile( BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationEndpoint = false, - TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt, - IdTokenSubClaimType = IdTokenSubClaimType.EndUserId + TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { @@ -151,6 +150,8 @@ or BarclaysBank.BarclaycardCommercialPayments }, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId }, AccountAccessConsentPost = new ReadWritePostCustomBehaviour { PostResponseLinksMayOmitId = true }, AccountAccessConsentAuthGet = bank is BarclaysBank.Sandbox diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs index 1409096ba..fad727430 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ChaseGenerator.cs @@ -41,8 +41,7 @@ public override BankProfile GetBankProfile( BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationEndpoint = false, - TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt, - IdTokenSubClaimType = IdTokenSubClaimType.EndUserId + TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { @@ -78,6 +77,8 @@ public override BankProfile GetBankProfile( }, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId }, AccountAccessConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour { ResponseScopeMayIncludeExtraValues = true }, AccountAccessConsentRefreshTokenGrantPost = diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs index 57530a0c4..f44f484aa 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs @@ -135,6 +135,9 @@ or LloydsBank.BankOfScotlandBusiness OAuth2ResponseMode.Fragment } }, + BaseIdTokenProcessingCustomBehaviour = bank is LloydsBank.Sandbox + ? new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId } + : null, AccountAccessConsentAuthGet = bank is LloydsBank.Sandbox ? null : new ConsentAuthGetCustomBehaviour { AddRedundantOAuth2NonceRequestParameter = true }, @@ -226,9 +229,7 @@ or LloydsBank.BankOfScotlandBusiness { UseRegistrationDeleteEndpoint = true, UseRegistrationGetEndpoint = true, - UseRegistrationAccessToken = bank is LloydsBank.Sandbox, - IdTokenSubClaimType = - bank is LloydsBank.Sandbox ? IdTokenSubClaimType.EndUserId : IdTokenSubClaimType.ConsentId + UseRegistrationAccessToken = bank is LloydsBank.Sandbox }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs index 4b150a50e..8505dce59 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs @@ -122,9 +122,7 @@ bank is not (NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbo { NatWestBank.Mettle => TokenEndpointAuthMethodSupportedValues.TlsClientAuth, _ => TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt - }, - IdTokenSubClaimType = - bank is NatWestBank.Coutts ? IdTokenSubClaimType.EndUserId : IdTokenSubClaimType.ConsentId + } }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { @@ -160,6 +158,9 @@ bank is not (NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbo }, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = bank is NatWestBank.Coutts + ? new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId } + : null, BankRegistrationPost = new BankRegistrationPostCustomBehaviour { TransportCertificateSubjectDnOrgIdEncoding = bank switch diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs index 6a3df3440..a589c4880 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/SantanderGenerator.cs @@ -83,6 +83,8 @@ bank is not SantanderBank.Santander : DynamicClientRegistrationApiVersion.Version3p2, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId }, BankRegistrationPost = new BankRegistrationPostCustomBehaviour { UseApplicationJoseNotApplicationJwtContentTypeHeader = @@ -128,7 +130,6 @@ bank is not SantanderBank.Santander bank is not SantanderBank.Santander ? TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt : TokenEndpointAuthMethodSupportedValues.TlsClientAuth, - IdTokenSubClaimType = IdTokenSubClaimType.EndUserId, UseRegistrationGetEndpoint = bank is not SantanderBank.Santander, UseRegistrationDeleteEndpoint = bank is not SantanderBank.Santander }, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs index c86f627e0..834bf1d93 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/TsbGenerator.cs @@ -42,8 +42,7 @@ public override BankProfile GetBankProfile( { TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt, UseRegistrationGetEndpoint = true, - UseRegistrationDeleteEndpoint = true, - IdTokenSubClaimType = IdTokenSubClaimType.EndUserId + UseRegistrationDeleteEndpoint = true }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { @@ -71,6 +70,8 @@ public override BankProfile GetBankProfile( }, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId }, AccountAccessConsentRefreshTokenGrantPost = new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, DomesticPaymentConsent = diff --git a/src/OpenBanking.Library.Connector/Models/Public/Management/IdTokenSubClaimType.cs b/src/OpenBanking.Library.Connector/Models/Public/Management/IdTokenSubClaimType.cs index 0168cfb45..afdaeaec9 100644 --- a/src/OpenBanking.Library.Connector/Models/Public/Management/IdTokenSubClaimType.cs +++ b/src/OpenBanking.Library.Connector/Models/Public/Management/IdTokenSubClaimType.cs @@ -15,5 +15,8 @@ public enum IdTokenSubClaimType EndUserId, [EnumMember(Value = "ConsentId")] - ConsentId + ConsentId, + + [EnumMember(Value = "ClientId")] + ClientId } diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs index 7d38a6a5f..f5ba77ae8 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -75,7 +74,6 @@ public AccountGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -106,10 +104,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs index 694ea2b55..44c8721f4 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -75,7 +74,6 @@ public BalanceGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -106,10 +104,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs index 66af642b5..4129d7608 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs @@ -18,7 +18,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -78,7 +77,6 @@ public DirectDebitGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -109,10 +107,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs index ac97ab712..28533bf54 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs @@ -16,7 +16,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using FluentValidation.Results; @@ -182,7 +181,6 @@ public MonzoPotGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -213,10 +211,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs index f41434c4f..2522e433c 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -75,7 +74,6 @@ public Party2Get( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -106,10 +104,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs index bf401cf5f..1b4d079eb 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -75,7 +74,6 @@ public PartyGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -106,10 +104,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs index 20d38dde9..5a1a172d2 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs @@ -15,7 +15,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -75,7 +74,6 @@ public StandingOrderGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -106,10 +104,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = readParams.ExternalApiAccountId switch diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs index ec695d268..847991444 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using Newtonsoft.Json; @@ -76,7 +75,6 @@ public TransactionGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -107,10 +105,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, customBehaviour?.AccountAccessConsentRefreshTokenGrantPost, customBehaviour?.JwksGet, - readParams.ModifiedBy); + readParams.ModifiedBy, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Retrieve endpoint URL string urlStringWihoutQuery = (externalAccountId: readParams.ExternalApiAccountId, diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index 6d8be96e9..beeac9faa 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.PaymentInitiation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.VariableRecurringPayments; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations.AccountAndTransaction; @@ -339,7 +338,6 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( bankRegistration.DefaultResponseModeOverride ?? bankProfile.DefaultResponseMode; bool supportsSca = bankProfile.SupportsSca; string issuerUrl = bankProfile.IssuerUrl; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; CustomBehaviourClass? customBehaviour = bankProfile.CustomBehaviour; string redirectUrl = softwareStatement.GetRedirectUri( defaultResponseMode, @@ -403,7 +401,9 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( _ => throw new ArgumentOutOfRangeException() }; bool nonceClaimIsInitialValue = - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour?.IdTokenNonceClaimIsPreviousValue ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenNonceClaimIsPreviousValue( + consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); string nonce = nonceClaimIsInitialValue && consent.AuthContextNonce is not null ? consent.AuthContextNonce : authContextNonce; @@ -417,7 +417,9 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( if (idToken is not null) { bool doNotValidateIdToken = - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; + IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( + consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); if (doNotValidateIdToken is false) { string? newExternalApiUserId = await _grantPost.ValidateIdTokenAuthEndpoint( @@ -425,6 +427,7 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( code, state, consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour, jwksUri, customBehaviour?.JwksGet, bankTokenIssuerClaim, @@ -433,7 +436,6 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( nonce, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, consent.ExternalApiUserId); if (newExternalApiUserId != consent.ExternalApiUserId) { @@ -522,12 +524,12 @@ await _grantPost.PostAuthCodeGrantAsync( supportsSca, expectRefreshToken, bankProfile.BankProfileEnum, - idTokenSubClaimType, authContext.CodeVerifier, jsonSerializerSettings, consentAuthCodeGrantPostCustomBehaviour, customBehaviour?.JwksGet, - apiClient); + apiClient, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Cache new access token MemoryCacheEntryOptions cacheEntryOptions = diff --git a/src/OpenBanking.Library.Connector/Operations/ConsentAccessTokenGet.cs b/src/OpenBanking.Library.Connector/Operations/ConsentAccessTokenGet.cs index 4f2d130ac..0f4facd5d 100644 --- a/src/OpenBanking.Library.Connector/Operations/ConsentAccessTokenGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/ConsentAccessTokenGet.cs @@ -11,7 +11,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Operations.Cache; using FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; @@ -88,10 +87,10 @@ public async Task GetAccessTokenAndUpdateConsent( OBSealKey obSealKey, bool supportsSca, BankProfileEnum bankProfile, - IdTokenSubClaimType idTokenSubClaimType, RefreshTokenGrantPostCustomBehaviour? refreshTokenGrantPostCustomBehaviour, JwksGetCustomBehaviour? jwksGetCustomBehaviour, - string? modifiedBy) + string? modifiedBy, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour = null) where TConsentEntity : BaseConsent { string consentAssociatedData = consent.GetAssociatedData(bankRegistration); @@ -199,11 +198,11 @@ await _grantPost.PostRefreshTokenGrantAsync( tokenEndpoint, supportsSca, bankProfile, - idTokenSubClaimType, jsonSerializerSettings, refreshTokenGrantPostCustomBehaviour, jwksGetCustomBehaviour, - apiClient); + apiClient, + baseIdTokenProcessingCustomBehaviour); // Conditionally store new access token var newAccessToken = new AccessToken(tokenEndpointResponse.AccessToken, tokenEndpointResponse.ExpiresIn); diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs index 6c01698c2..6f566467f 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs @@ -51,6 +51,7 @@ public GrantPost( string code, string state, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, JwksGetCustomBehaviour? jwksGetCustomBehaviour, string bankIssuerUrl, @@ -59,7 +60,6 @@ public GrantPost( string expectedNonce, bool supportsSca, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, string? externalApiUserId) { // Check for empty token @@ -72,6 +72,7 @@ public GrantPost( var idTokenObject = await DeserialiseIdToken( idTokenEncoded, idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, bankProfileForTppReportingMetrics, jwksGetCustomBehaviour); @@ -83,9 +84,14 @@ public GrantPost( externalApiConsentId, expectedNonce, supportsSca, - idTokenProcessingCustomBehaviour); + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + IdTokenSubClaimType idTokenSubClaimType = + IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); string? outputExternalApiUserId = externalApiUserId; // unchanged by default switch (idTokenSubClaimType) { @@ -114,6 +120,13 @@ public GrantPost( throw new Exception("Subject from ID token does not match consent ID."); } + break; + case IdTokenSubClaimType.ClientId: + if (!string.Equals(idTokenObject.Subject, externalApiClientId)) + { + throw new Exception("Subject from ID token does not match client ID."); + } + break; default: throw new ArgumentOutOfRangeException(nameof(idTokenSubClaimType), idTokenSubClaimType, null); @@ -230,12 +243,12 @@ public async Task PostAuthCodeGrantAsync( bool supportsSca, bool expectRefreshToken, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, string? codeVerifier, JsonSerializerSettings? jsonSerializerSettings, AuthCodeGrantPostCustomBehaviour? authCodeGrantPostCustomBehaviour, JwksGetCustomBehaviour? jwksGetCustomBehaviour, - IApiClient mtlsApiClient) + IApiClient mtlsApiClient, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour) { bool useOpenIdConnect = requestScope.Split(' ').ToList().Contains("openid"); @@ -331,16 +344,17 @@ public async Task PostAuthCodeGrantAsync( } // Perform validation - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour = - authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour; bool doNotValidateIdToken = - idTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; + IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( + authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (doNotValidateIdToken is false) { await ValidateIdTokenTokenEndpoint( response.IdToken, response.AccessToken, - idTokenProcessingCustomBehaviour, + authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, jwksGetCustomBehaviour, bankIssuerUrl, @@ -349,7 +363,6 @@ await ValidateIdTokenTokenEndpoint( expectedNonce, bankProfileForTppReportingMetrics, supportsSca, - idTokenSubClaimType, externalApiUserId); } } @@ -372,11 +385,11 @@ public async Task PostRefreshTokenGrantAsync( string tokenEndpoint, bool supportsSca, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, JsonSerializerSettings? jsonSerializerSettings, RefreshTokenGrantPostCustomBehaviour? refreshTokenGrantPostCustomBehaviour, JwksGetCustomBehaviour? jwksGetCustomBehaviour, - IApiClient mtlsApiClient) + IApiClient mtlsApiClient, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour) { bool useOpenIdConnect = refreshTokenScope.Split(' ').ToList().Contains("openid"); @@ -445,16 +458,17 @@ public async Task PostRefreshTokenGrantAsync( } // Perform validation - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour = - refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour; bool doNotValidateIdToken = - idTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; + IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( + refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (doNotValidateIdToken is false) { await ValidateIdTokenTokenEndpoint( response.IdToken, response.AccessToken, - idTokenProcessingCustomBehaviour, + refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, jwksGetCustomBehaviour, bankIssuerUrl, @@ -463,7 +477,6 @@ await ValidateIdTokenTokenEndpoint( expectedNonce, bankProfileForTppReportingMetrics, supportsSca, - idTokenSubClaimType, externalApiUserId); } } @@ -536,7 +549,8 @@ private void ValidateIdTokenCommon( string externalApiConsentId, string expectedNonce, bool supportsSca, - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour) + IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour) { if (idToken.Exp < DateTimeOffset.UtcNow) { @@ -544,7 +558,9 @@ private void ValidateIdTokenCommon( } bool idTokenMayNotHaveConsentIdClaim = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveConsentIdClaim ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveConsentIdClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (!idTokenMayNotHaveConsentIdClaim && idToken.ConsentId is null) { @@ -557,7 +573,9 @@ private void ValidateIdTokenCommon( } bool idTokenMayNotHaveNonceClaim = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveNonceClaim ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveNonceClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (!idTokenMayNotHaveNonceClaim && idToken.Nonce is null) { @@ -570,7 +588,10 @@ private void ValidateIdTokenCommon( throw new Exception("Nonce from ID token does not match expected nonce."); } - bool idTokenMayNotHaveAuthTimeClaim = idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveAuthTimeClaim ?? false; + bool idTokenMayNotHaveAuthTimeClaim = + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveAuthTimeClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (!idTokenMayNotHaveAuthTimeClaim && idToken.AuthTime is null) { @@ -578,8 +599,9 @@ private void ValidateIdTokenCommon( } bool idTokenMayNotHaveAcrClaim = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveAcrClaim - ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveAcrClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (!idTokenMayNotHaveAcrClaim && idToken.Acr is null) { @@ -587,8 +609,9 @@ private void ValidateIdTokenCommon( } bool doNotValidateIdTokenAcrClaim = - idTokenProcessingCustomBehaviour?.DoNotValidateIdTokenAcrClaim - ?? false; + IdTokenProcessingCustomBehaviour.GetDoNotValidateIdTokenAcrClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (idToken.Acr is not null) { if (supportsSca && @@ -606,7 +629,9 @@ private void ValidateIdTokenCommon( } } - string issClaim = idTokenProcessingCustomBehaviour?.IssClaim ?? bankIssuerUrl; + string issClaim = IdTokenProcessingCustomBehaviour.GetIssClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour) ?? bankIssuerUrl; if (!string.Equals(idToken.Issuer, issClaim)) { throw new Exception("Issuer from ID token does not match expected issuer."); @@ -653,6 +678,7 @@ private async Task ValidateIdTokenTokenEndpoint( string idTokenEncoded, string accessToken, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, JwksGetCustomBehaviour? jwksGetCustomBehaviour, string bankIssuerUrl, @@ -661,7 +687,6 @@ private async Task ValidateIdTokenTokenEndpoint( string expectedNonce, BankProfileEnum? bankProfileForTppReportingMetrics, bool supportsSca, - IdTokenSubClaimType idTokenSubClaimType, string? externalApiUserId) { // Check for empty token @@ -674,6 +699,7 @@ private async Task ValidateIdTokenTokenEndpoint( var idToken = await DeserialiseIdToken( idTokenEncoded, idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, bankProfileForTppReportingMetrics, jwksGetCustomBehaviour); @@ -685,9 +711,14 @@ private async Task ValidateIdTokenTokenEndpoint( externalApiConsentId, expectedNonce, supportsSca, - idTokenProcessingCustomBehaviour); + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + IdTokenSubClaimType idTokenSubClaimType = + IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); switch (idTokenSubClaimType) { case IdTokenSubClaimType.EndUserId: @@ -708,6 +739,13 @@ private async Task ValidateIdTokenTokenEndpoint( throw new Exception("Subject from ID token does not match consent ID."); } + break; + case IdTokenSubClaimType.ClientId: + if (!string.Equals(idToken.Subject, externalApiClientId)) + { + throw new Exception("Subject from ID token does not match client ID."); + } + break; default: throw new ArgumentOutOfRangeException(nameof(idTokenSubClaimType), idTokenSubClaimType, null); @@ -726,6 +764,7 @@ private async Task ValidateIdTokenTokenEndpoint( private async Task DeserialiseIdToken( string idTokenEncoded, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, BankProfileEnum? bankProfileForTppReportingMetrics, JwksGetCustomBehaviour? jwksGetCustomBehaviour) @@ -742,7 +781,9 @@ private async Task DeserialiseIdToken( // Deserialise IT token claims var optionsDict = new Dictionary(); DateTimeOffsetUnixConverterEnum? idTokenExpirationTimeClaimJsonConverter = - idTokenProcessingCustomBehaviour?.IdTokenExpirationTimeClaimJsonConverter; + IdTokenProcessingCustomBehaviour.GetIdTokenExpirationTimeClaimJsonConverter( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); if (idTokenExpirationTimeClaimJsonConverter is not null) { optionsDict.Add( diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/IGrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/IGrantPost.cs index 0ac239bde..fdd494c0c 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/IGrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/IGrantPost.cs @@ -8,7 +8,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using Jose; using Newtonsoft.Json; @@ -48,12 +47,12 @@ Task PostAuthCodeGrantAsync( bool supportsSca, bool expectRefreshToken, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, string? codeVerifier, JsonSerializerSettings? jsonSerializerSettings, AuthCodeGrantPostCustomBehaviour? authCodeGrantPostCustomBehaviour, JwksGetCustomBehaviour? jwksGetCustomBehaviour, - IApiClient mtlsApiClient); + IApiClient mtlsApiClient, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour); Task PostRefreshTokenGrantAsync( string refreshToken, @@ -70,17 +69,18 @@ Task PostRefreshTokenGrantAsync( string tokenEndpoint, bool supportsSca, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, JsonSerializerSettings? jsonSerializerSettings, RefreshTokenGrantPostCustomBehaviour? refreshTokenGrantPostCustomBehaviour, JwksGetCustomBehaviour? jwksGetCustomBehaviour, - IApiClient mtlsApiClient); + IApiClient mtlsApiClient, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour); Task ValidateIdTokenAuthEndpoint( string idToken, string code, string state, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, JwksGetCustomBehaviour? jwksGetCustomBehaviour, string bankIssuerUrl, @@ -89,7 +89,6 @@ Task PostRefreshTokenGrantAsync( string expectedNonce, bool supportsSca, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, string? externalApiUserId); TimeSpan GetTokenAdjustedDuration(int expiresInSeconds); diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs index b259cf8d6..28e416c58 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; @@ -271,7 +270,6 @@ Task CreateAsync( bool supportsSca = bankProfile.SupportsSca; string issuerUrl = bankProfile.IssuerUrl; string bankFinancialId = bankProfile.PaymentInitiationApiSettings.FinancialId ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; ConsentAuthGetCustomBehaviour? domesticPaymentConsentAuthGetCustomBehaviour = bankProfile.CustomBehaviour ?.DomesticPaymentConsentAuthGet; DomesticPaymentCustomBehaviour? readWritePostCustomBehaviour = @@ -308,10 +306,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, domesticPaymentConsentRefreshTokenGrantPostCustomBehaviour, jwksGetCustomBehaviour, - request.ModifiedBy); + request.ModifiedBy, + bankProfile.CustomBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Create new object at external API var externalApiUrl = new Uri(paymentInitiationApi.BaseUrl + RelativePathBeforeId); diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs index 87b212ab6..59a0bfef1 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; @@ -540,7 +539,6 @@ Task ReadFundsConfirmationAsync bool supportsSca = bankProfile.SupportsSca; string bankFinancialId = bankProfile.PaymentInitiationApiSettings.FinancialId ?? bankProfile.FinancialId; string issuerUrl = bankProfile.IssuerUrl; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; DomesticPaymentConsentCustomBehaviour? readWriteGetCustomBehaviour = bankProfile.CustomBehaviour?.DomesticPaymentConsent; RefreshTokenGrantPostCustomBehaviour? domesticPaymentConsentRefreshTokenGrantPostCustomBehaviour = @@ -577,10 +575,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, domesticPaymentConsentRefreshTokenGrantPostCustomBehaviour, jwksGetCustomBehaviour, - readParams.ModifiedBy); + readParams.ModifiedBy, + bankProfile.CustomBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Read object from external API var externalApiUrl = new Uri( diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs index 83961f29b..c64edb6e3 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs @@ -16,7 +16,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; @@ -122,7 +121,6 @@ public async Task CreateAsync( string issuerUrl = bankProfile.IssuerUrl; string bankFinancialId = bankProfile.VariableRecurringPaymentsApiSettings.FinancialId ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; ConsentAuthGetCustomBehaviour? domesticVrpConsentAuthGetCustomBehaviour = bankProfile.CustomBehaviour ?.DomesticVrpConsentAuthGet; DomesticVrpCustomBehaviour? domesticVrpPostCustomBehaviour = bankProfile.CustomBehaviour?.DomesticVrp; @@ -158,10 +156,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, domesticVrpConsentRefreshTokenGrantPostCustomBehaviour, jwksGetCustomBehaviour, - request.ModifiedBy); + request.ModifiedBy, + bankProfile.CustomBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Create new object at external API var externalApiUrl = new Uri(variableRecurringPaymentsApi.BaseUrl + RelativePathBeforeId); diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs index 760a0112d..29a73b298 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs @@ -14,7 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request; @@ -128,7 +127,6 @@ Task CreateFundsConfirmationAsync( string bankFinancialId = bankProfile.VariableRecurringPaymentsApiSettings.FinancialId ?? bankProfile.FinancialId; string issuerUrl = bankProfile.IssuerUrl; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; RefreshTokenGrantPostCustomBehaviour? domesticVrpConsentRefreshTokenGrantPostCustomBehaviour = bankProfile.CustomBehaviour?.DomesticVrpConsentRefreshTokenGrantPost; JwksGetCustomBehaviour? jwksGetCustomBehaviour = bankProfile.CustomBehaviour?.JwksGet; @@ -165,10 +163,10 @@ await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( obSealKey, supportsSca, bankProfile.BankProfileEnum, - idTokenSubClaimType, domesticVrpConsentRefreshTokenGrantPostCustomBehaviour, jwksGetCustomBehaviour, - createParams.Request.ModifiedBy); + createParams.Request.ModifiedBy, + bankProfile.CustomBehaviour?.BaseIdTokenProcessingCustomBehaviour); // Read object from external API var externalApiUrl = new Uri( From fbc8bca34c427f1e9be1390627af0570e271bd62 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Wed, 6 May 2026 10:55:05 +0100 Subject: [PATCH 13/49] [Test] Add delay setting --- .../BankProfiles/BankProfile.cs | 2 ++ .../AccountAccessConsent/AccountAccessConsentSubtest.cs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs index cfcb06b58..12688df8a 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs @@ -136,6 +136,8 @@ public AccountAccessConsentExternalApiRequestAdjustments AccountAccessConsentTemplateExternalApiRequestAdjustments { get; set; } = x => x; public bool UseBalancesNotAccountEndpointInSecondSession { get; init; } + + public int AccountAccessConsentPostCreateDelaySeconds { get; init; } = 0; } public class PaymentInitiationApiSettings diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs index 314b4f686..79368825c 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs @@ -68,6 +68,13 @@ await GetAccountAccessConsentRequest( AccountAccessConsentCreateResponse accountAccessConsentCreateResponseTmp = await accountAndTransactionApiClient.AccountAccessConsentCreate(accountAccessConsentRequest); + if (bankProfile.AccountAndTransactionApiSettings.AccountAccessConsentPostCreateDelaySeconds > 0) + { + await Task.Delay( + TimeSpan.FromSeconds( + bankProfile.AccountAndTransactionApiSettings.AccountAccessConsentPostCreateDelaySeconds)); + } + // Read fresh AccountAccessConsent AccountAccessConsentCreateResponse accountAccessConsentReadResponse = await accountAndTransactionApiClient.AccountAccessConsentRead( From 3a83b15011a5b4cb11f9b766c09d052d3191e518 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Thu, 7 May 2026 20:52:47 +0100 Subject: [PATCH 14/49] Generate JWT signature from unencoded payload when b64 header set to false --- .../PaymentInitiationPostRequestProcessor.cs | 14 ++++---------- .../Security/JwtFactory.cs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs index e449276b6..d69113f5a 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs @@ -69,7 +69,9 @@ public PaymentInitiationPostRequestProcessor( _useB64), payloadJson, _obSealKey.Key, - null); + null, + !_useB64, + true); StringBuilder requestTraceSb = new StringBuilder() .AppendLine($"#### JWT ({requestDescription})") .Append(jwt); @@ -81,7 +83,7 @@ public PaymentInitiationPostRequestProcessor( new("x-fapi-financial-id", _orgId), new("Authorization", "Bearer " + _accessToken), new("x-idempotency-key", Guid.NewGuid().ToString()), - CreateJwsSignatureHeader(jwt) + new("x-jws-signature", jwt) }; if (extraHeaders is not null) { @@ -105,14 +107,6 @@ public PaymentInitiationPostRequestProcessor( return (response, xFapiInteractionId); } - private static HttpHeader CreateJwsSignatureHeader(string jwt) - { - // Create headers - string[] jwsComponents = jwt.Split('.'); - var jwsSignature = $"{jwsComponents[0]}..{jwsComponents[2]}"; - return new HttpHeader("x-jws-signature", jwsSignature); - } - private static Dictionary GetJoseHeaders( string orgId, string softwareId, diff --git a/src/OpenBanking.Library.Connector/Security/JwtFactory.cs b/src/OpenBanking.Library.Connector/Security/JwtFactory.cs index f763dbe33..d35c35f02 100644 --- a/src/OpenBanking.Library.Connector/Security/JwtFactory.cs +++ b/src/OpenBanking.Library.Connector/Security/JwtFactory.cs @@ -19,7 +19,9 @@ public static string CreateJwt( Dictionary headers, string payloadJson, string signingKey, - JwsAlgorithm? jwsAlgorithm) + JwsAlgorithm? jwsAlgorithm, + bool encodePayload = true, + bool detachPayload = false) { signingKey.ArgNotNull(nameof(signingKey)); @@ -28,11 +30,18 @@ public static string CreateJwt( { CertificateFactories.ImportPrivateKey(signingKey, ref rsa); + var options = new JwtOptions + { + EncodePayload = encodePayload, + DetachPayload = detachPayload + }; + string result = JWT.Encode( payloadJson, rsa, jwsAlgorithm ?? JwsAlgorithm.PS256, - headers); + headers, + options: options); return result; } finally From 57fed8511f0667d1a97522755220989af1bdc632 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 1 May 2026 20:15:23 +0100 Subject: [PATCH 15/49] Add Virgin (Merged APIs) to Nationwide --- .../BankProfiles/BankGroups/Nationwide.cs | 9 +- .../BankProfiles/BankProfileEnum.cs | 3 + .../BankProfiles/BankProfileExtensions.cs | 1 + .../Generators/NationwideGenerator.cs | 256 +++++++++++++----- 4 files changed, 194 insertions(+), 75 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Nationwide.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Nationwide.cs index 42901e6d5..118bb2558 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Nationwide.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Nationwide.cs @@ -9,13 +9,18 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; public enum NationwideBank { - Nationwide + Nationwide, + VirginMerged } public class Nationwide() : BankGroupBase(BankGroup.Nationwide) { protected override ConcurrentDictionary BankProfileToBank { get; } = - new() { [BankProfileEnum.Nationwide_Nationwide] = NationwideBank.Nationwide }; + new() + { + [BankProfileEnum.Nationwide_Nationwide] = NationwideBank.Nationwide, + [BankProfileEnum.Nationwide_VirginMerged] = NationwideBank.VirginMerged + }; public override NationwideRegistrationGroup GetRegistrationGroup( NationwideBank bank, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index 717fa0dba..c9f80a003 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs @@ -105,6 +105,9 @@ public enum BankProfileEnum [EnumMember(Value = "Nationwide_Nationwide")] Nationwide_Nationwide, + [EnumMember(Value = "Nationwide_VirginMerged")] + Nationwide_VirginMerged, + [EnumMember(Value = "NatWest_NatWestSandbox")] NatWest_NatWestSandbox, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs index 811eea873..ce7dcb78c 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs @@ -41,6 +41,7 @@ public static BankGroup GetBankGroup(this BankProfileEnum bankProfileEnum) => BankProfileEnum.Monzo_Monzo => BankGroup.Monzo, BankProfileEnum.Monzo_Sandbox => BankGroup.Monzo, BankProfileEnum.Nationwide_Nationwide => BankGroup.Nationwide, + BankProfileEnum.Nationwide_VirginMerged => BankGroup.Nationwide, BankProfileEnum.NatWest_NatWestSandbox => BankGroup.NatWest, BankProfileEnum.NatWest_NatWest => BankGroup.NatWest, BankProfileEnum.NatWest_NatWestBankline => BankGroup.NatWest, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs index 2c4b079a7..e5508c3ca 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NationwideGenerator.cs @@ -12,6 +12,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Cache.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Configuration; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; @@ -31,108 +32,184 @@ public override BankProfile GetBankProfile( IInstrumentationClient instrumentationClient) => new( _bankGroupData.GetBankProfile(bank), - "https://obonline.nationwide.co.uk/open-banking/", // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide - "0015800000jf8aKAAQ", // from https://developer.nationwide.co.uk/open-banking/how-to?page=1 - new AccountAndTransactionApi { BaseUrl = GetAccountAndTransactionBaseUrl("v3.1") }, - new AccountAndTransactionApi - { - BaseUrl = GetAccountAndTransactionBaseUrl("v4.0"), - ApiVersion = AccountAndTransactionApiVersion.Version4p0 - }, - new PaymentInitiationApi - { - BaseUrl = - GetPaymentsBaseUrl("v3.1") - }, - new PaymentInitiationApi - { - BaseUrl = - GetPaymentsBaseUrl("v4.0"), - ApiVersion = PaymentInitiationApiVersion.Version4p0 - }, - new VariableRecurringPaymentsApi - { - BaseUrl = - GetPaymentsBaseUrl("v3.1") - }, - new VariableRecurringPaymentsApi + bank switch { - BaseUrl = - GetPaymentsBaseUrl("v4.0"), - ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 + NationwideBank.Nationwide => + "https://obonline.nationwide.co.uk/open-banking/", // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide + NationwideBank + .VirginMerged => + "https://api.openbanking.virginmoney.com", // from https://developer.virginmoney.com/merged/dynamic-registration/ + _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) }, + "0015800000jf8aKAAQ", // from https://developer.nationwide.co.uk/open-banking/how-to?page=1 + new AccountAndTransactionApi { BaseUrl = GetApiBaseUrl(bank, "v3.1/aisp") }, + bank is NationwideBank.Nationwide + ? new AccountAndTransactionApi + { + BaseUrl = GetApiBaseUrl(bank, "v4.0/aisp"), + ApiVersion = AccountAndTransactionApiVersion.Version4p0 + } + : null, + new PaymentInitiationApi { BaseUrl = GetApiBaseUrl(bank, "v3.1/pisp") }, + bank is NationwideBank.Nationwide + ? new PaymentInitiationApi + { + BaseUrl = GetApiBaseUrl(bank, "v4.0/pisp"), + ApiVersion = PaymentInitiationApiVersion.Version4p0 + } + : null, + bank is NationwideBank.Nationwide + ? new VariableRecurringPaymentsApi { BaseUrl = GetApiBaseUrl(bank, "v3.1/pisp") } + : null, + bank is NationwideBank.Nationwide + ? new VariableRecurringPaymentsApi + { + BaseUrl = GetApiBaseUrl(bank, "v4.0/pisp"), + ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 + } + : null, true, instrumentationClient) { - DynamicClientRegistrationApiVersion = DynamicClientRegistrationApiVersion.Version3p3, + DynamicClientRegistrationApiVersion = bank switch + { + NationwideBank.Nationwide => DynamicClientRegistrationApiVersion.Version3p3, + NationwideBank.VirginMerged => DynamicClientRegistrationApiVersion.Version3p2, + _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) + }, CustomBehaviour = new CustomBehaviourClass { BankRegistrationPost = new BankRegistrationPostCustomBehaviour { TransportCertificateSubjectDnOrgIdEncoding = - SubjectDnOrgIdEncoding.DottedDecimalAttributeTypeWithStringValue, + bank is NationwideBank.Nationwide + ? SubjectDnOrgIdEncoding.DottedDecimalAttributeTypeWithStringValue + : null, UseApplicationJoseNotApplicationJwtContentTypeHeader = true, + IssuedAtClaimResponseJsonConverter = + bank is NationwideBank.VirginMerged + ? DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat + : null, + ExpirationTimeClaimResponseJsonConverter = + bank is NationwideBank.VirginMerged + ? DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat + : null, ClientIdIssuedAtClaimResponseJsonConverter = - DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat + bank is NationwideBank.Nationwide + ? DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat + : null }, OpenIdConfigurationGet = new OpenIdConfigurationGetCustomBehaviour { - Url = - "https://obonline.nationwide.co.uk/open-banking/.well-known/openid-configuration" // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide - }, - AccountAccessConsentAuthGet = new ConsentAuthGetCustomBehaviour - { - IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } - }, - AccountAccessConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour - { - IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + Url = bank switch + { + NationwideBank.Nationwide => + "https://obonline.nationwide.co.uk/open-banking/.well-known/openid-configuration", // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide + NationwideBank.VirginMerged => + "https://api.openbanking.virginmoney.com/open-banking/v3.0/.well-known/openid-configuration", // from https://developer.virginmoney.com/merged/dynamic-registration/ + _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) + } }, + AccountAccessConsentAuthGet = + new ConsentAuthGetCustomBehaviour + { + IdTokenProcessingCustomBehaviour = new IdTokenProcessingCustomBehaviour + { + IdTokenSubClaimType = + bank is NationwideBank.VirginMerged ? IdTokenSubClaimType.ClientId : null, + IdTokenMayNotHaveAuthTimeClaim = bank is NationwideBank.VirginMerged, + DoNotValidateIdTokenAcrClaim = bank is NationwideBank.Nationwide, + IdTokenMayNotHaveAcrClaim = bank is NationwideBank.VirginMerged + } + }, + AccountAccessConsentAuthCodeGrantPost = + new AuthCodeGrantPostCustomBehaviour + { + UseScopeInRequest = bank is NationwideBank.VirginMerged, + Scope = bank is NationwideBank.VirginMerged ? "accounts" : null, + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour + { + IdTokenMayNotHaveNonceClaim = bank is NationwideBank.VirginMerged, + IdTokenMayNotHaveAuthTimeClaim = bank is NationwideBank.VirginMerged, + DoNotValidateIdTokenAcrClaim = bank is NationwideBank.Nationwide + } + }, AccountAccessConsentRefreshTokenGrantPost = new RefreshTokenGrantPostCustomBehaviour { + UseScopeInRequest = bank is NationwideBank.VirginMerged, + Scope = bank is NationwideBank.VirginMerged ? "accounts" : null, IdTokenProcessingCustomBehaviour = new IdTokenProcessingCustomBehaviour { + IdTokenMayNotHaveNonceClaim = bank is NationwideBank.VirginMerged, IdTokenMayNotHaveAuthTimeClaim = true, - DoNotValidateIdTokenAcrClaim = true + DoNotValidateIdTokenAcrClaim = bank is NationwideBank.Nationwide } }, AccountAccessConsentPost = new ReadWritePostCustomBehaviour { PostResponseLinksMayOmitId = true }, + BankRegistrationPut = bank is NationwideBank.VirginMerged + ? new BankRegistrationPutCustomBehaviour + { + GetCustomTokenScope = registrationScope => + { + if ((registrationScope & RegistrationScopeEnum.AccountAndTransaction) == + RegistrationScopeEnum.AccountAndTransaction) + { + return "accounts"; + } + if ((registrationScope & RegistrationScopeEnum.PaymentInitiation) == + RegistrationScopeEnum.PaymentInitiation) + { + return "payments"; + } + if ((registrationScope & RegistrationScopeEnum.FundsConfirmation) == + RegistrationScopeEnum.FundsConfirmation) + { + return "fundsconfirmations"; + } + throw new Exception("Cannot determine custom token scope."); + } + } + : null, DomesticPaymentConsentAuthGet = new ConsentAuthGetCustomBehaviour { IdTokenProcessingCustomBehaviour = new IdTokenProcessingCustomBehaviour { + IdTokenSubClaimType = + bank is NationwideBank.VirginMerged ? IdTokenSubClaimType.ClientId : null, + IdTokenMayNotHaveAuthTimeClaim = bank is NationwideBank.VirginMerged, DoNotValidateIdTokenAcrClaim = true } }, - DomesticPaymentConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour - { - ExpectedResponseRefreshTokenMayBeAbsent = true, - IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } - }, - DomesticPaymentConsentRefreshTokenGrantPost = - new RefreshTokenGrantPostCustomBehaviour + DomesticPaymentConsentAuthCodeGrantPost = + new AuthCodeGrantPostCustomBehaviour { - IdTokenProcessingCustomBehaviour = new IdTokenProcessingCustomBehaviour - { - IdTokenMayNotHaveAuthTimeClaim = true, - DoNotValidateIdTokenAcrClaim = true - } + ExpectedResponseRefreshTokenMayBeAbsent = true, + UseScopeInRequest = bank is NationwideBank.VirginMerged, + Scope = bank is NationwideBank.VirginMerged ? "payments" : null, + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour + { + IdTokenMayNotHaveNonceClaim = bank is NationwideBank.VirginMerged, + IdTokenMayNotHaveAuthTimeClaim = bank is NationwideBank.VirginMerged, + DoNotValidateIdTokenAcrClaim = true + } }, DomesticPaymentConsent = new DomesticPaymentConsentCustomBehaviour { PostResponseLinksMayOmitId = true, - PreferMisspeltContractPresentIndicator = true + PreferMisspeltContractPresentIndicator = true, + UseB64JoseHeader = bank is NationwideBank.VirginMerged }, DomesticPayment = new DomesticPaymentCustomBehaviour { PostResponseLinksMayOmitId = true, PreferMisspeltContractPresentIndicator = true, - ResponseDataDebtorMayBeMissingOrWrong = true + ResponseDataDebtorMayBeMissingOrWrong = true, + ResponseDataRefundMayBeMissingOrWrong = bank is NationwideBank.VirginMerged, + UseB64JoseHeader = bank is NationwideBank.VirginMerged }, DomesticVrpConsentAuthGet = new ConsentAuthGetCustomBehaviour @@ -169,14 +246,33 @@ public override BankProfile GetBankProfile( ResponseLinksMayHaveIncorrectUrlBeforeQuery = true } }, + BankConfigurationApiSettings = new BankConfigurationApiSettings + { + UseRegistrationDeleteEndpoint = bank is NationwideBank.VirginMerged, + UseRegistrationGetEndpoint = bank is NationwideBank.VirginMerged, + TokenEndpointAuthMethod = bank switch + { + NationwideBank.Nationwide => TokenEndpointAuthMethodSupportedValues.TlsClientAuth, + NationwideBank.VirginMerged => TokenEndpointAuthMethodSupportedValues.ClientSecretBasic, + _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) + } + }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { AccountAccessConsentTemplateExternalApiRequestAdjustments = externalApiRequest => { - var elementsToRemove = new List - { - AccountAndTransactionModelsPublic.Permissions.ReadPartyPSU - }; + List elementsToRemove = + bank is NationwideBank.VirginMerged + ? new List + { + AccountAndTransactionModelsPublic.Permissions.ReadOffers, + AccountAndTransactionModelsPublic.Permissions.ReadProducts + } + : new List + { + AccountAndTransactionModelsPublic.Permissions.ReadPartyPSU + } + ; foreach (AccountAndTransactionModelsPublic.Permissions element in elementsToRemove) { @@ -185,17 +281,31 @@ public override BankProfile GetBankProfile( return externalApiRequest; }, - UseGetPartyEndpoint = false + AccountAccessConsentPostCreateDelaySeconds = bank is NationwideBank.VirginMerged ? 30 : 0, + UseGetParty2Endpoint = bank is NationwideBank.Nationwide, + UseGetPartyEndpoint = bank is NationwideBank.VirginMerged + }, + PaymentInitiationApiSettings = new PaymentInitiationApiSettings + { + PreferPartyToPartyPaymentContextCode = bank is NationwideBank.VirginMerged, + UseContractPresentIndicator = bank is NationwideBank.Nationwide, + UseReadRefundAccount = bank is NationwideBank.Nationwide }, - AispUseV4ByDefault = true, - PispUseV4ByDefault = true, - VrpUseV4ByDefault = true + DefaultResponseMode = + bank is NationwideBank.VirginMerged ? OAuth2ResponseMode.Query : OAuth2ResponseMode.Fragment, + AispUseV4ByDefault = bank is NationwideBank.Nationwide, + PispUseV4ByDefault = bank is NationwideBank.Nationwide, + VrpUseV4ByDefault = bank is NationwideBank.Nationwide }; - private static string GetAccountAndTransactionBaseUrl(string version) => - $"https://api.nationwide.co.uk/open-banking/{version}/aisp"; // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide - - - private static string GetPaymentsBaseUrl(string version) => - $"https://api.nationwide.co.uk/open-banking/{version}/pisp"; // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide + private static string GetApiBaseUrl(NationwideBank bank, string suffix) => + bank switch + { + NationwideBank.Nationwide => + $"https://api.nationwide.co.uk/open-banking/{suffix}", // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/110101211/Implementation+Guide+Nationwide + NationwideBank.VirginMerged => + $"https://api.openbanking.virginmoney.com/open-banking/{suffix}", // from https://developer.virginmoney.com/merged/account-access-consents/, + // https://developer.virginmoney.com/merged/dip/ + _ => throw new ArgumentOutOfRangeException(nameof(bank), bank, null) + }; } From 5b09decd39378f5934918f1cef33c159b5bba084 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 8 May 2026 00:10:42 +0100 Subject: [PATCH 16/49] [Docs] Update docs --- docs/apis/account-and-transaction/openapi-aisp.json | 2 +- docs/apis/auth-contexts/openapi-auth.json | 2 +- docs/apis/management/openapi-manage.json | 3 ++- docs/apis/payment-initiation/openapi-pisp.json | 2 +- docs/apis/variable-recurring-payments/openapi-vrp.json | 2 +- docs/bank-integrations.md | 6 +++--- .../OpenBanking.WebApp.Connector.csproj | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index 7f5249960..73999e5cc 100644 --- a/docs/apis/account-and-transaction/openapi-aisp.json +++ b/docs/apis/account-and-transaction/openapi-aisp.json @@ -3,7 +3,7 @@ "info": { "title": "Account and Transaction API", "description": "Account and Transaction API for Open Banking Connector Web App", - "version": "18.3.0" + "version": "18.4.0" }, "paths": { "/aisp/account-access-consent-auth-contexts": { diff --git a/docs/apis/auth-contexts/openapi-auth.json b/docs/apis/auth-contexts/openapi-auth.json index ffac0dad1..39d7a391f 100644 --- a/docs/apis/auth-contexts/openapi-auth.json +++ b/docs/apis/auth-contexts/openapi-auth.json @@ -3,7 +3,7 @@ "info": { "title": "Auth Contexts API", "description": "Auth Contexts API for Open Banking Connector Web App", - "version": "18.3.0" + "version": "18.4.0" }, "paths": { "/auth/redirect-delegate": { diff --git a/docs/apis/management/openapi-manage.json b/docs/apis/management/openapi-manage.json index 7e6f5cce6..78ace0ae0 100644 --- a/docs/apis/management/openapi-manage.json +++ b/docs/apis/management/openapi-manage.json @@ -3,7 +3,7 @@ "info": { "title": "Management API", "description": "Management API for Open Banking Connector Web App", - "version": "18.3.0" + "version": "18.4.0" }, "paths": { "/manage/bank-registrations": { @@ -767,6 +767,7 @@ "Monzo_Monzo", "Monzo_Sandbox", "Nationwide_Nationwide", + "Nationwide_VirginMerged", "NatWest_NatWestSandbox", "NatWest_NatWest", "NatWest_NatWestBankline", diff --git a/docs/apis/payment-initiation/openapi-pisp.json b/docs/apis/payment-initiation/openapi-pisp.json index 22e7b1c60..9195418ba 100644 --- a/docs/apis/payment-initiation/openapi-pisp.json +++ b/docs/apis/payment-initiation/openapi-pisp.json @@ -3,7 +3,7 @@ "info": { "title": "Payment Initiation API", "description": "Payment Initiation API for Open Banking Connector Web App", - "version": "18.3.0" + "version": "18.4.0" }, "paths": { "/pisp/domestic-payment-consent-auth-contexts": { diff --git a/docs/apis/variable-recurring-payments/openapi-vrp.json b/docs/apis/variable-recurring-payments/openapi-vrp.json index 3ecc07909..ceac5bdc7 100644 --- a/docs/apis/variable-recurring-payments/openapi-vrp.json +++ b/docs/apis/variable-recurring-payments/openapi-vrp.json @@ -3,7 +3,7 @@ "info": { "title": "Variable Recurring Payments API", "description": "Variable Recurring Payments API for Open Banking Connector Web App", - "version": "18.3.0" + "version": "18.4.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/docs/bank-integrations.md b/docs/bank-integrations.md index f9ebfeaee..4dcb21e79 100644 --- a/docs/bank-integrations.md +++ b/docs/bank-integrations.md @@ -10,14 +10,14 @@ UK Open Banking uses the [Financial-grade API](https://openid.net/specs/openid-f | Chase | All brands | :white_large_square: | Chase_Chase | AISP,
PISP (domestic payments) | | Co-operative | Sandbox
Co-operative
Smile | :white_check_mark:
:white_large_square:
:white_large_square: | Cooperative_CooperativeSandbox
Cooperative_Cooperative
Cooperative_Smile | AISP | | HSBC | FirstDirect
UK Personal
UK Business
UK Kinetic
HSBCnet UK | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Hsbc_FirstDirect
Hsbc_UkPersonal
Hsbc_UkBusiness
Hsbc_UkKinetic
Hsbc_HsbcNetUk | AISP,
PISP (domestic payments),
VRP | -| Lloyds | Lloyds Personal
Lloyds Business
Lloyds Commercial
Halifax Personal
Bank of Scotland Personal
Bank of Scotland Business
Bank of Scotland Commercial
MBNA Personal | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Lloyds_LloydsPersonal
Lloyds_LloydsBusiness
Lloyds_LloydsCommerical
Lloyds_HalifaxPersonal
Lloyds_BankOfScotlandPersonal
Lloyds_BankOfScotlandBusiness
Lloyds_BankOfScotlandCommerical
Lloyds_MbnaPersonal | AISP,
PISP (domestic payments),
VRP | +| Lloyds | Lloyds Personal
Lloyds Business
Lloyds Commercial
Halifax Personal
Bank of Scotland Personal
Bank of Scotland Business
Bank of Scotland Commercial
MBNA Personal | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Lloyds_LloydsPersonal
Lloyds_LloydsBusiness
Lloyds_LloydsCommerical
Lloyds_HalifaxPersonal
Lloyds_BankOfScotlandPersonal
Lloyds_BankOfScotlandBusiness
Lloyds_BankOfScotlandCommerical
Lloyds_MbnaPersonal *(no VRPs)* | AISP,
PISP (domestic payments),
VRP | | Monzo | All brands | :white_large_square: | Monzo_Monzo | AISP,
PISP (domestic payments),
VRP | -| Nationwide | All brands | :white_large_square: | Nationwide_Nationwide | AISP,
PISP (domestic payments),
VRP | +| Nationwide | Nationwide
Virgin (Merged APIs) | :white_large_square:
:white_large_square: | Nationwide_Nationwide
Nationwide_VirginMerged *(no VRPs)* | AISP,
PISP (domestic payments),
VRP | | NatWest | NatWest Sandbox
RBS Sandbox
NatWest
NatWest Bankline
NatWest ClearSpend
RBS
RBS Bankline
RBS ClearSpend
The One
NatWest One
Virgin One
Ulster Bank NI
Ulster Bank NI Bankline
Ulster Bank NI ClearSpend
Mettle *(under test)*
Coutts *(under test)* | :white_check_mark:
:white_check_mark:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | NatWest_NatWestSandbox
NatWest_RoyalBankOfScotlandSandbox
NatWest_NatWest
NatWest_NatWestBankline
NatWest_NatWestClearSpend
NatWest_RoyalBankOfScotland
NatWest_RoyalBankOfScotlandBankline
NatWest_RoyalBankOfScotlandClearSpend
NatWest_TheOne
NatWest_NatWestOne
NatWest_VirginOne
NatWest_UlsterBankNi
NatWest_UlsterBankNiBankline
NatWest_UlsterBankNiClearSpend
NatWest_Mettle
NatWest_Coutts | AISP,
PISP (domestic payments),
VRP | | OBIE | Model Bank (2023) | :white_check_mark: | Obie_Model2023 | AISP,
PISP (domestic payments),
VRP | | Revolut | All brands | :white_large_square: | Revolut_Revolut | AISP,
PISP (domestic payments) | | Santander | Personal
Business
Corporate | :white_large_square: | Santander_Personal
Santander_Business
Santander_Corporate | AISP,
PISP (domestic payments),
VRP | -| Starling | All brands | :white_large_square: | Starling_Starling | AISP | +| Starling | All brands | :white_large_square: | Starling_Starling | AISP,
PISP (domestic payments) | | Tide | All brands | :white_large_square: | Tide_Tide | AISP,
PISP (domestic payments) | | TSB | All brands | :white_large_square: | Tsb_Tsb | AISP,
PISP (domestic payments) | | Wise | All brands | :white_large_square: | Wise_Wise | AISP,
PISP (domestic payments) | diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 9acbfe087..b4d4edea8 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 18.3.0 + 18.4.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false From 470eeb723d3e6209b4f8b637ad5ce237db4b7af7 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 9 May 2026 15:35:34 +0100 Subject: [PATCH 17/49] Update .NET dependencies --- .../OpenBanking.Library.Connector.GenericHost.csproj | 6 +++--- .../OpenBanking.Library.Connector.BankTests.csproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index 3eada0f87..8e5378e8c 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,9 +24,9 @@ - - - + + + diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 5043afaca..2d9898257 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -1,4 +1,4 @@ - + net9.0 From 447655ad1e490059d51ce5e3162eb374ebde0fc4 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 10 May 2026 13:27:27 +0100 Subject: [PATCH 18/49] Update logging providers --- src/OpenBanking.WebApp.Connector/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OpenBanking.WebApp.Connector/Program.cs b/src/OpenBanking.WebApp.Connector/Program.cs index 62083e980..432f1d6db 100644 --- a/src/OpenBanking.WebApp.Connector/Program.cs +++ b/src/OpenBanking.WebApp.Connector/Program.cs @@ -130,6 +130,8 @@ builder .Logging + .ClearProviders() + .AddConsole() .AddWebHostLogging(builder.Configuration, serviceVersion); // Build app From b629935cfc3f417b0a9107154ceb9fe75012942e Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Mon, 11 May 2026 23:06:46 +0100 Subject: [PATCH 19/49] [Test] Use HTML emails --- .../BrowserInteraction/ConsentAuth.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/ConsentAuth.cs b/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/ConsentAuth.cs index 6f5e0bc26..ba1b6c333 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/ConsentAuth.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/ConsentAuth.cs @@ -60,9 +60,14 @@ public async Task PerformAuth( { // Perform email auth waitForRedirect = redirectObserver.WaitForRedirect(TimeSpan.FromMinutes(5)); - SendEmail( - "Open Banking Connector Test", - "This is the auth URL: " + authUrl); + var htmlBody = // language=html + $""" +

Please click or tap to authorise:

+

+ Authorise +

+ """; + SendEmail("Open Banking Connector Test", htmlBody); } else { @@ -153,13 +158,14 @@ await page.WaitForSelectorAsync( } } - private void SendEmail(string subject, string body) + private void SendEmail(string subject, string htmlBody) { using var mailMessage = new MailMessage( new MailAddress(_emailOptions.FromEmailAddress, _emailOptions.FromEmailName), new MailAddress(_emailOptions.ToEmailAddress, _emailOptions.ToEmailName)); mailMessage.Subject = subject; - mailMessage.Body = body; + mailMessage.Body = htmlBody; + mailMessage.IsBodyHtml = true; _smtpClient.Send(mailMessage); } } From 38cd085e4dd6b6b45dd4daa50057690c8067f0e2 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 9 May 2026 16:36:56 +0100 Subject: [PATCH 20/49] [Test] Update xUnit from v2 to v3 --- .../OpenBanking.Library.Connector.IntegrationTests.csproj | 7 ++++--- .../Persistence/BankClientProfileDbRepoTests.cs | 2 +- .../Persistence/DomesticConsentRepoTests.cs | 2 +- .../Http/ApiClientTests.cs | 2 +- .../Http/HttpRequestInfoPropertyAttribute.cs | 7 ++++++- .../OpenBanking.Library.Connector.UnitTests.csproj | 5 +++-- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj index d0d630afc..81efb6824 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj +++ b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj @@ -2,6 +2,7 @@ net9.0 + Exe enable enable FinnovationLabs.OpenBanking.Library.Connector.IntegrationTests @@ -13,7 +14,7 @@ - + @@ -21,8 +22,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/BankClientProfileDbRepoTests.cs b/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/BankClientProfileDbRepoTests.cs index 5953869b6..e8fdce292 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/BankClientProfileDbRepoTests.cs +++ b/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/BankClientProfileDbRepoTests.cs @@ -7,7 +7,7 @@ using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; -using Xunit.Abstractions; +using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.IntegrationTests.Persistence; diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/DomesticConsentRepoTests.cs b/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/DomesticConsentRepoTests.cs index 2b74d9d6f..6831b7dd8 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/DomesticConsentRepoTests.cs +++ b/test/OpenBanking.Library.Connector.IntegrationTests/Persistence/DomesticConsentRepoTests.cs @@ -4,7 +4,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.PaymentInitiation; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; -using Xunit.Abstractions; +using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.IntegrationTests.Persistence; diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs index f8c9138ac..33b931a53 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs @@ -39,7 +39,7 @@ public async Task SendAsync_ResponseReturned(string url, string content) HttpResponseMessage response = await apiClient.LowLevelSendAsync(req); response.StatusCode.Should().Be(HttpStatusCode.OK); - string responseContent = await response.Content.ReadAsStringAsync(); + string responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); responseContent.Should().Be(content); response.Content.Headers.ContentType?.ToString().Should().Be(contentType + "; charset=utf-8"); } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoPropertyAttribute.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoPropertyAttribute.cs index 38046bafb..264490c12 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoPropertyAttribute.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoPropertyAttribute.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Net; +using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Artbitraries; @@ -12,7 +13,11 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Http; public class HttpRequestInfoPropertyAttribute : PropertyAttribute { - public HttpRequestInfoPropertyAttribute() + public HttpRequestInfoPropertyAttribute( + [CallerFilePath] + string? sourceFile = null, + [CallerLineNumber] + int sourceLine = 0) { Verbose = PropertyTests.VerboseTests; Arbitrary = new[] diff --git a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj index d2bb352bb..df6092c46 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj +++ b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj @@ -2,6 +2,7 @@ net9.0 + Exe enable enable FinnovationLabs.OpenBanking.Library.Connector.UnitTests @@ -13,7 +14,7 @@ - + @@ -22,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 4a2e77cbd758abc95366aed746652c1c9087e6a5 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Mon, 11 May 2026 22:48:11 +0100 Subject: [PATCH 21/49] [Test] Change output type to support remote testing with Visual Studio --- .../OpenBanking.Library.Connector.IntegrationTests.csproj | 1 - .../OpenBanking.Library.Connector.UnitTests.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj index 81efb6824..b9c9c4444 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj +++ b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj @@ -2,7 +2,6 @@ net9.0 - Exe enable enable FinnovationLabs.OpenBanking.Library.Connector.IntegrationTests diff --git a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj index df6092c46..e9f7e58cf 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj +++ b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj @@ -2,7 +2,6 @@ net9.0 - Exe enable enable FinnovationLabs.OpenBanking.Library.Connector.UnitTests From 7b1f662cdcd03cddba3690deb6a413da26aafa41 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 9 May 2026 18:57:05 +0100 Subject: [PATCH 22/49] [Test] Use test framework assertions rather than FluentAssertions --- .../AccountAndTransactionApiClient.cs | 53 ++++----- .../BankTests/AppTests.cs | 9 +- .../BankTests/AuthContextsApiClient.cs | 3 +- .../BankTests/ManagementApiClient.cs | 31 +++-- .../BankTests/PaymentInitiationApiClient.cs | 112 +++++++++--------- .../VariableRecurringPaymentsApiClient.cs | 97 ++++++++------- .../BankTests/WebAppClient.cs | 9 +- ...Banking.Library.Connector.BankTests.csproj | 1 - .../LocalMockTests/Http/ApiClientTests.cs | 11 +- ....Library.Connector.IntegrationTests.csproj | 1 - .../Extensions/StringExtensionTests.cs | 3 +- .../ExtensionsTests.cs | 13 +- .../Http/ApiClientTests.cs | 19 ++- .../Http/HttpRequestInfoExtensionsTests.cs | 3 +- .../ConsoleInstrumentationClientTests.cs | 19 ++- .../Json/OBTypesTests.cs | 9 +- .../Mapping/EntityMapperTests.cs | 10 +- .../Mapping/EntityTypeFinderTests.cs | 3 +- .../GeneratedModelImplementationTests.cs | 3 +- .../GenereatedModelImplementationTests.cs | 3 +- ...AuthorisationCallbackDataValidatorTests.cs | 7 +- ...Banking.Library.Connector.UnitTests.csproj | 1 - .../Security/CertificateExtensionsTests.cs | 5 +- .../Security/KeySecretTests.cs | 3 +- 24 files changed, 211 insertions(+), 217 deletions(-) diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AccountAndTransactionApiClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AccountAndTransactionApiClient.cs index 8066ae2a5..a34ff9787 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AccountAndTransactionApiClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AccountAndTransactionApiClient.cs @@ -6,7 +6,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; -using FluentAssertions; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -24,14 +23,14 @@ await client.GetAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); if (readParams.ExcludeExternalApiOperation) { - response.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(response.ExternalApiResponse); } else { - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse); } return response; @@ -46,14 +45,14 @@ public async Task AccountAccessConsentCreate await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); if (request.ExternalApiObject is not null) { - response.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(response.ExternalApiResponse); } else { - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse); } return response; @@ -71,7 +70,7 @@ await client.DeleteAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -87,8 +86,8 @@ await client.CreateAsync AccountAccessCons await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -126,8 +125,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -167,8 +166,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -193,8 +192,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -219,8 +218,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -245,8 +244,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -275,8 +274,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -301,8 +300,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -327,8 +326,8 @@ await client.GetAsync( GetExtraHeaders(readParams)); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index 5b17b03ce..6a2ca5c84 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs @@ -23,7 +23,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; using FinnovationLabs.OpenBanking.Library.Connector.Utility; -using FluentAssertions; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.DependencyInjection; @@ -811,11 +810,11 @@ private async Task BankRegistrationCreate( // Checks if (bankRegistrationRequest.ExternalApiId is not null) { - bankRegistrationCreateResponse.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(bankRegistrationCreateResponse.ExternalApiResponse); } else { - bankRegistrationCreateResponse.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(bankRegistrationCreateResponse.ExternalApiResponse); } // Read BankRegistration @@ -848,11 +847,11 @@ private async Task BankRegistrationRead( !bankProfileUseRegistrationGetEndpoint; if (noExternalApiOperation) { - bankRegistrationReadResponse.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(bankRegistrationReadResponse.ExternalApiResponse); } else { - bankRegistrationReadResponse.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(bankRegistrationReadResponse.ExternalApiResponse); } return bankRegistrationReadResponse; diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AuthContextsApiClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AuthContextsApiClient.cs index 80f12b033..087f47462 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AuthContextsApiClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AuthContextsApiClient.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; -using FluentAssertions; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -20,7 +19,7 @@ await client.CreateFromFormAsync( formCollection); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/ManagementApiClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/ManagementApiClient.cs index 1612a31a8..5d6036c02 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/ManagementApiClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/ManagementApiClient.cs @@ -6,7 +6,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; -using FluentAssertions; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -24,7 +23,7 @@ await client.GetAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -37,7 +36,7 @@ public async Task BankRegistrationCreate(BankRegistrat await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -57,7 +56,7 @@ await client.DeleteAsync( ]); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -70,7 +69,7 @@ public async Task EncryptionKeyDescriptionCrea await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -83,7 +82,7 @@ public async Task EncryptionKeyDescriptionRead await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -96,7 +95,7 @@ public async Task EncryptionKeyDescriptionDelete(Guid id) await client.DeleteAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -109,7 +108,7 @@ public async Task ObWacCertificateCreate(ObWacCertific await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -122,7 +121,7 @@ public async Task ObWacCertificateRead(Guid id) await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -135,7 +134,7 @@ public async Task ObWacCertificateDelete(Guid id) await client.DeleteAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -148,7 +147,7 @@ public async Task ObSealCertificateDelete(Guid id) await client.DeleteAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -161,7 +160,7 @@ public async Task SoftwareStatementDelete(Guid id) await client.DeleteAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -175,7 +174,7 @@ public async Task ObSealCertificateCreate(ObSealCerti await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -188,7 +187,7 @@ public async Task ObSealCertificateRead(Guid id) await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -201,7 +200,7 @@ public async Task SoftwareStatementCreate(SoftwareSta await client.CreateAsync(uriPath, request); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -214,7 +213,7 @@ public async Task SoftwareStatementRead(Guid id) await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/PaymentInitiationApiClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/PaymentInitiationApiClient.cs index db8ac30e0..c4cbcad64 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/PaymentInitiationApiClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/PaymentInitiationApiClient.cs @@ -8,7 +8,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; -using FluentAssertions; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -26,14 +25,14 @@ await client.GetAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); if (readParams.ExcludeExternalApiOperation) { - response.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(response.ExternalApiResponse); } else { - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse); } return response; @@ -51,17 +50,16 @@ await client.GetAsync( []); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); - response.ExternalApiResponse.Data.FundsAvailableResult.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); + Assert.IsNotNull(response.ExternalApiResponse.Data.FundsAvailableResult); // Check funds available bool responseDataFundsAvailableResultFundsAvailableMayBeWrong = customBehaviour?.ResponseDataFundsAvailableResultFundsAvailableMayBeWrong ?? false; if (!responseDataFundsAvailableResultFundsAvailableMayBeWrong) { - response.ExternalApiResponse.Data.FundsAvailableResult!.FundsAvailable.Should() - .Be(true); + Assert.IsTrue(response.ExternalApiResponse.Data.FundsAvailableResult!.FundsAvailable); } return response; @@ -79,24 +77,27 @@ await client.CreateAsync( []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -130,8 +131,8 @@ await client request); // Checks - response.Warnings.Should().BeNull(); - response.AuthUrl.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.AuthUrl); return response; } @@ -145,7 +146,7 @@ public async Task DomesticPayment await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -166,45 +167,45 @@ await client.GetAsync( ]); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); // Check status bool responseDataStatusMayBeWrong = customBehaviour?.ResponseDataStatusMayBeWrong ?? false; if (!responseDataStatusMayBeWrong) { - response.ExternalApiResponse.Data.Status.Should() - .BeOneOf( - PaymentInitiationModelsPublic.Data4Status.ACCC, - PaymentInitiationModelsPublic.Data4Status.ACCP, - PaymentInitiationModelsPublic.Data4Status.ACFC, - PaymentInitiationModelsPublic.Data4Status.ACSC, - PaymentInitiationModelsPublic.Data4Status.ACSP, - PaymentInitiationModelsPublic.Data4Status.ACTC, - PaymentInitiationModelsPublic.Data4Status.ACWC, - PaymentInitiationModelsPublic.Data4Status.ACWP, - PaymentInitiationModelsPublic.Data4Status.PDNG); + CollectionAssert.Contains( + new[] + { + PaymentInitiationModelsPublic.Data4Status.ACCC, PaymentInitiationModelsPublic.Data4Status.ACCP, + PaymentInitiationModelsPublic.Data4Status.ACFC, PaymentInitiationModelsPublic.Data4Status.ACSC, + PaymentInitiationModelsPublic.Data4Status.ACSP, PaymentInitiationModelsPublic.Data4Status.ACTC, + PaymentInitiationModelsPublic.Data4Status.ACWC, PaymentInitiationModelsPublic.Data4Status.ACWP, + PaymentInitiationModelsPublic.Data4Status.PDNG + }, + response.ExternalApiResponse.Data.Status); } // Check refund account bool responseDataRefundMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundMayBeMissingOrWrong ?? false; if (!responseDataRefundMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Refund); bool responseDataRefundAccountSchemeNameMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundAccountSchemeNameMayBeMissingOrWrong ?? false; if (!responseDataRefundAccountSchemeNameMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund!.Account.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Refund!.Account.SchemeName); } bool responseDataRefundAccountIdentificationMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundAccountIdentificationMayBeMissingOrWrong ?? false; if (!responseDataRefundAccountIdentificationMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund!.Account.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Refund!.Account.Identification.Length); } } @@ -212,20 +213,22 @@ await client.GetAsync( bool responseDataDebtorMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorMayBeMissingOrWrong ?? false; if (!responseDataDebtorMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Debtor); bool responseDataDebtorSchemeNameMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorSchemeNameMayBeMissingOrWrong ?? false; if (!responseDataDebtorSchemeNameMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor!.SchemeName.Should().Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Debtor!.SchemeName); } bool responseDataDebtorIdentificationMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorIdentificationMayBeMissingOrWrong ?? false; if (!responseDataDebtorIdentificationMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Debtor!.Identification!.Length); } } @@ -247,8 +250,8 @@ await client.GetAsync( ]); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -266,28 +269,29 @@ await client.CreateAsync( request); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); // Check refund account bool responseDataRefundMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundMayBeMissingOrWrong ?? false; if (!responseDataRefundMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Refund); bool responseDataRefundAccountSchemeNameMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundAccountSchemeNameMayBeMissingOrWrong ?? false; if (!responseDataRefundAccountSchemeNameMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund!.Account.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Refund!.Account.SchemeName); } bool responseDataRefundAccountIdentificationMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundAccountIdentificationMayBeMissingOrWrong ?? false; if (!responseDataRefundAccountIdentificationMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund!.Account.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Refund!.Account.Identification.Length); } } @@ -295,20 +299,22 @@ await client.CreateAsync( bool responseDataDebtorMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorMayBeMissingOrWrong ?? false; if (!responseDataDebtorMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Debtor); bool responseDataDebtorSchemeNameMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorSchemeNameMayBeMissingOrWrong ?? false; if (!responseDataDebtorSchemeNameMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor!.SchemeName.Should().Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Debtor!.SchemeName); } bool responseDataDebtorIdentificationMayBeMissingOrWrong = customBehaviour?.ResponseDataDebtorIdentificationMayBeMissingOrWrong ?? false; if (!responseDataDebtorIdentificationMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Debtor!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Debtor!.Identification!.Length); } } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/VariableRecurringPaymentsApiClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/VariableRecurringPaymentsApiClient.cs index 860394bb6..8b93c9ebc 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/VariableRecurringPaymentsApiClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/VariableRecurringPaymentsApiClient.cs @@ -8,7 +8,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; -using FluentAssertions; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -26,14 +25,14 @@ await client.GetAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); if (readParams.ExcludeExternalApiOperation) { - response.ExternalApiResponse.Should().BeNull(); + Assert.IsNull(response.ExternalApiResponse); } else { - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse); } return response; @@ -51,23 +50,25 @@ await client.CreateAsync( : []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -130,8 +132,8 @@ await client request); // Checks - response.Warnings.Should().BeNull(); - response.AuthUrl.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.AuthUrl); return response; } @@ -145,7 +147,7 @@ public async Task DomesticVrpConsentA await client.GetAsync(uriPath, []); // Checks - response.Warnings.Should().BeNull(); + Assert.IsNull(response.Warnings); return response; } @@ -166,15 +168,16 @@ await client.GetAsync( ]); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); // Check status bool responseDataStatusMayBeWrong = customBehaviour?.ResponseDataStatusMayBeMissingOrWrong ?? false; if (!responseDataStatusMayBeWrong) { - response.ExternalApiResponse.Data.Status.Should() - .BeOneOf( + CollectionAssert.Contains( + new[] + { VariableRecurringPaymentsModelsPublic.Data4Status.ACCC, VariableRecurringPaymentsModelsPublic.Data4Status.ACCP, VariableRecurringPaymentsModelsPublic.Data4Status.ACFC, @@ -183,28 +186,31 @@ await client.GetAsync( VariableRecurringPaymentsModelsPublic.Data4Status.ACTC, VariableRecurringPaymentsModelsPublic.Data4Status.ACWC, VariableRecurringPaymentsModelsPublic.Data4Status.ACWP, - VariableRecurringPaymentsModelsPublic.Data4Status.PDNG); + VariableRecurringPaymentsModelsPublic.Data4Status.PDNG + }, + response.ExternalApiResponse.Data.Status); } // Check refund account bool responseDataRefundMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundMayBeMissingOrWrong ?? false; if (!responseDataRefundMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Refund); bool responseDataRefundSchemeNameMayBeWrong = customBehaviour?.ResponseDataRefundSchemeNameMayBeWrong ?? false; if (!responseDataRefundSchemeNameMayBeWrong) { - response.ExternalApiResponse.Data.Refund!.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Refund!.SchemeName); } bool responseDataRefundIdentificationMayBeWrong = customBehaviour?.ResponseDataRefundIdentificationMayBeWrong ?? false; if (!responseDataRefundIdentificationMayBeWrong) { - response.ExternalApiResponse.Data.Refund!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Refund!.Identification.Length); } } @@ -213,21 +219,22 @@ await client.GetAsync( customBehaviour?.ResponseDataDebtorAccountMayBeMissingOrWrong ?? false; if (!responseDataDebtorAccountMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.DebtorAccount.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.DebtorAccount); bool responseDataDebtorAccountSchemeNameMayBeWrong = customBehaviour?.ResponseDataDebtorAccountSchemeNameMayBeWrong ?? false; if (!responseDataDebtorAccountSchemeNameMayBeWrong) { - response.ExternalApiResponse.Data.DebtorAccount!.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.DebtorAccount!.SchemeName); } bool responseDataDebtorAccountIdentificationMayBeWrong = customBehaviour?.ResponseDataDebtorAccountIdentificationMayBeWrong ?? false; if (!responseDataDebtorAccountIdentificationMayBeWrong) { - response.ExternalApiResponse.Data.DebtorAccount!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.DebtorAccount!.Identification.Length); } } @@ -249,8 +256,8 @@ await client.GetAsync( ]); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); return response; } @@ -268,28 +275,29 @@ await client.CreateAsync( request); // Checks - response.Warnings.Should().BeNull(); - response.ExternalApiResponse.Should().NotBeNull(); + Assert.IsNull(response.Warnings); + Assert.IsNotNull(response.ExternalApiResponse); // Check refund account bool responseDataRefundMayBeMissingOrWrong = customBehaviour?.ResponseDataRefundMayBeMissingOrWrong ?? false; if (!responseDataRefundMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.Refund.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.Refund); bool responseDataRefundSchemeNameMayBeWrong = customBehaviour?.ResponseDataRefundSchemeNameMayBeWrong ?? false; if (!responseDataRefundSchemeNameMayBeWrong) { - response.ExternalApiResponse.Data.Refund!.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.Refund!.SchemeName); } bool responseDataRefundIdentificationMayBeWrong = customBehaviour?.ResponseDataRefundIdentificationMayBeWrong ?? false; if (!responseDataRefundIdentificationMayBeWrong) { - response.ExternalApiResponse.Data.Refund!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.Refund!.Identification.Length); } } @@ -298,21 +306,22 @@ await client.CreateAsync( customBehaviour?.ResponseDataDebtorAccountMayBeMissingOrWrong ?? false; if (!responseDataDebtorAccountMayBeMissingOrWrong) { - response.ExternalApiResponse.Data.DebtorAccount.Should().NotBeNull(); + Assert.IsNotNull(response.ExternalApiResponse.Data.DebtorAccount); bool responseDataDebtorAccountSchemeNameMayBeWrong = customBehaviour?.ResponseDataDebtorAccountSchemeNameMayBeWrong ?? false; if (!responseDataDebtorAccountSchemeNameMayBeWrong) { - response.ExternalApiResponse.Data.DebtorAccount!.SchemeName.Should() - .Be("UK.OBIE.SortCodeAccountNumber"); + Assert.AreEqual( + "UK.OBIE.SortCodeAccountNumber", + response.ExternalApiResponse.Data.DebtorAccount!.SchemeName); } bool responseDataDebtorAccountIdentificationMayBeWrong = customBehaviour?.ResponseDataDebtorAccountIdentificationMayBeWrong ?? false; if (!responseDataDebtorAccountIdentificationMayBeWrong) { - response.ExternalApiResponse.Data.DebtorAccount!.Identification.Should().HaveLength(14); + Assert.AreEqual(14, response.ExternalApiResponse.Data.DebtorAccount!.Identification.Length); } } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/WebAppClient.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/WebAppClient.cs index 5cbc2ecdf..60ace5630 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/WebAppClient.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/WebAppClient.cs @@ -5,7 +5,6 @@ using System.Net; using System.Text; using FinnovationLabs.OpenBanking.Library.Connector.Http; -using FluentAssertions; using Newtonsoft.Json; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; @@ -27,7 +26,7 @@ public async Task GetAsync( using HttpResponseMessage httpResponse = await client.SendAsync(httpRequestMessage); // Check status code - httpResponse.StatusCode.Should().Be(HttpStatusCode.OK); + Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode); // De-serialise content string responseContentString = await httpResponse.Content.ReadAsStringAsync(); @@ -55,7 +54,7 @@ public async Task CreateAsync( requestContent); // Check status code - httpResponse.StatusCode.Should().Be(expectedResponseStatusCode); + Assert.AreEqual(expectedResponseStatusCode, httpResponse.StatusCode); // De-serialise content string responseContentString = await httpResponse.Content.ReadAsStringAsync(); @@ -77,7 +76,7 @@ public async Task CreateFromFormAsync( requestContent); // Check status code - httpResponse.StatusCode.Should().Be(HttpStatusCode.Created); + Assert.AreEqual(HttpStatusCode.Created, httpResponse.StatusCode); // De-serialise content string responseContentString = await httpResponse.Content.ReadAsStringAsync(); @@ -103,7 +102,7 @@ public async Task DeleteAsync( using HttpResponseMessage httpResponse = await client.SendAsync(httpRequestMessage); // Check status code - httpResponse.StatusCode.Should().Be(HttpStatusCode.OK); + Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode); // De-serialise content string responseContentString = await httpResponse.Content.ReadAsStringAsync(); diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 2d9898257..4c9193a4c 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -13,7 +13,6 @@ - diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/LocalMockTests/Http/ApiClientTests.cs b/test/OpenBanking.Library.Connector.IntegrationTests/LocalMockTests/Http/ApiClientTests.cs index 0fc4049a8..630865490 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/LocalMockTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.IntegrationTests/LocalMockTests/Http/ApiClientTests.cs @@ -5,7 +5,6 @@ using System.Net; using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; -using FluentAssertions; using NSubstitute; using Xunit; @@ -31,7 +30,7 @@ public async Task ApiClient_SendAsync_Success(string url) HttpResponseMessage r = await api.LowLevelSendAsync(req); - r.StatusCode.Should().Be(HttpStatusCode.OK); + Assert.Equal(HttpStatusCode.OK, r.StatusCode); } } @@ -53,28 +52,28 @@ public async Task ApiClient_SendAsync_NotFound(string url) HttpResponseMessage r = await api.LowLevelSendAsync(req); - r.StatusCode.Should().Be(HttpStatusCode.NotFound); + Assert.Equal(HttpStatusCode.NotFound, r.StatusCode); } } [Theory] [InlineData("https://a5b2a8a9-1220-4aa4-aa83-0036a7bd1e69.com/stuff")] - public void ApiClient_SendAsync_Failure(string url) + public async Task ApiClient_SendAsync_Failure(string url) { HttpRequestMessage req = new HttpRequestBuilder() .SetMethod(HttpMethod.Get) .SetUri(url) .CreateHttpRequestMessage(); - using var http = new HttpClient(); Func a = async () => { + using var http = new HttpClient(); var api = new ApiClient( Substitute.For(), http); HttpResponseMessage r = await api.LowLevelSendAsync(req); }; - a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } } diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj index b9c9c4444..83acd4a8c 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj +++ b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj @@ -12,7 +12,6 @@ - diff --git a/test/OpenBanking.Library.Connector.UnitTests/Extensions/StringExtensionTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Extensions/StringExtensionTests.cs index b0e006d70..2ecf1eec2 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Extensions/StringExtensionTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Extensions/StringExtensionTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Extensions; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Extensions; @@ -26,6 +25,6 @@ public void PascalOrCamelToKebabCase_String_ReturnsString(string input, string e string output = input.PascalOrCamelToKebabCase(); // Assert - output.Should().Be(expectedOutput); + Assert.Equal(expectedOutput, output); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/ExtensionsTests.cs b/test/OpenBanking.Library.Connector.UnitTests/ExtensionsTests.cs index 2ff352a5a..066db26e6 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/ExtensionsTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/ExtensionsTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Extensions; -using FluentAssertions; using Xunit; using ClientRegistrationModelsPublic = FinnovationLabs.OpenBanking.Library.BankApiModels.UKObDcr.V3p3.Models; @@ -26,7 +25,7 @@ public void DelimiterCount(string value, int expectedCount) { int result = value.DelimiterCount('.'); - result.Should().Be(expectedCount); + Assert.Equal(expectedCount, result); } [Fact] @@ -42,10 +41,10 @@ public void ToObjectDictionary_ScratchTest() Dictionary result = value.ToObjectDictionary(); - result.Should().NotBeNull(); - result["aud"].Should().Be(value.Aud); - result["application_type"].Should().Be(value.ApplicationType); - result["client_id"].Should().Be(value.ClientId); - result["token_endpoint_auth_signing_alg"].Should().BeNull(); + Assert.NotNull(result); + Assert.Equal(value.Aud, result["aud"]); + Assert.Equal(value.ApplicationType, result["application_type"]); + Assert.Equal(value.ClientId, result["client_id"]); + Assert.Null(result["token_endpoint_auth_signing_alg"]); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs index 33b931a53..ff81d5fad 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs @@ -6,7 +6,6 @@ using System.Text; using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; -using FluentAssertions; using Newtonsoft.Json; using NSubstitute; using RichardSzalay.MockHttp; @@ -38,10 +37,10 @@ public async Task SendAsync_ResponseReturned(string url, string content) HttpResponseMessage response = await apiClient.LowLevelSendAsync(req); - response.StatusCode.Should().Be(HttpStatusCode.OK); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); string responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - responseContent.Should().Be(content); - response.Content.Headers.ContentType?.ToString().Should().Be(contentType + "; charset=utf-8"); + Assert.Equal(content, responseContent); + Assert.Equal(contentType + "; charset=utf-8", response.Content.Headers.ContentType?.ToString()); } [Theory] @@ -69,7 +68,7 @@ public async Task SendAsync_TraceStarted(string url, string content) [Theory] [InlineData("https://yadayada.com")] - public void SendAsync_ExceptionLogged(string url) + public async Task SendAsync_ExceptionLogged(string url) { var mockHttp = new MockHttpMessageHandler(); mockHttp.When(HttpMethod.Get, url).Respond(x => throw new HttpRequestException()); @@ -87,7 +86,7 @@ public void SendAsync_ExceptionLogged(string url) Func a = async () => await apiClient.LowLevelSendAsync(req); - a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } @@ -118,7 +117,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - result.Message.Should().Be(entity.Message); + Assert.Equal(entity.Message, result.Message); } } @@ -149,7 +148,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - await a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } [Theory] @@ -186,7 +185,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - await a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } @@ -223,7 +222,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - await a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); instrumentationClient.Received(1).Trace(Arg.Any()); } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoExtensionsTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoExtensionsTests.cs index 3ffc78dca..6112834a8 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoExtensionsTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/HttpRequestInfoExtensionsTests.cs @@ -4,7 +4,6 @@ using System.Net.Http.Headers; using FinnovationLabs.OpenBanking.Library.Connector.Http; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Http; @@ -24,6 +23,6 @@ public void Create_ContentTypesPreserved(string contentType) MediaTypeHeaderValue? resultContentTypes = result.Content?.Headers.ContentType; - resultContentTypes?.MediaType.Should().Be(contentType); + Assert.Equal(contentType, resultContentTypes?.MediaType); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Instrumentation/ConsoleInstrumentationClientTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Instrumentation/ConsoleInstrumentationClientTests.cs index b7702b063..56596def5 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Instrumentation/ConsoleInstrumentationClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Instrumentation/ConsoleInstrumentationClientTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Instrumentation; @@ -27,7 +26,7 @@ public void Logging_Info_Recorded(string message) string result = new StreamReader(ms).ReadToEnd(); - result.Should().Contain(message); + Assert.Contains(message, result); } } @@ -48,7 +47,7 @@ public void Logging_Warning_Recorded(string message) string result = new StreamReader(ms).ReadToEnd(); - result.Should().Contain(message); + Assert.Contains(message, result); } } @@ -70,7 +69,7 @@ public void Logging_Error_Recorded(string message) string result = new StreamReader(ms).ReadToEnd(); - result.Should().Contain(message); + Assert.Contains(message, result); } } @@ -95,8 +94,8 @@ public void Logging_Exception_Recorded(string message, string exceptionMessage) string result = new StreamReader(ms).ReadToEnd(); - result.Should().Contain(exceptionMessage); - result.Should().Contain(message); + Assert.Contains(exceptionMessage, result); + Assert.Contains(message, result); } } @@ -122,8 +121,8 @@ public void Logging_StartTrace_EndTrace_Recorded(string startMsg, string endMsg) result = result.Trim(Environment.NewLine.ToCharArray()); - result.Should().StartWith(startMsg); - result.Should().EndWith(endMsg); + Assert.StartsWith(startMsg, result); + Assert.EndsWith(endMsg, result); } } @@ -154,10 +153,10 @@ public void Logging_StartTrace_TraceRecorded(string msg, params string[] msgs) result = result.Trim(Environment.NewLine.ToCharArray()); - result.Should().StartWith(msg); + Assert.StartsWith(msg, result); foreach (string m in msgs) { - result.Should().Contain(m); + Assert.Contains(m, result); } } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Json/OBTypesTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Json/OBTypesTests.cs index 0fc41824a..4d76adf57 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Json/OBTypesTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Json/OBTypesTests.cs @@ -2,7 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FluentAssertions; using Newtonsoft.Json; using Xunit; @@ -26,7 +25,7 @@ public void ConvertOBCurrencyExchange5() var result = JsonConvert.DeserializeObject(jsonString); - result.Should().BeEquivalentTo(obCurrencyExchange5); + Assert.Equivalent(obCurrencyExchange5, result); } [Fact] @@ -47,7 +46,7 @@ public void ConvertOBTransactionCashBalance() var result = JsonConvert.DeserializeObject(jsonString); - result.Should().BeEquivalentTo(obTransactionCashBalance); + Assert.Equivalent(obTransactionCashBalance, result); } [Fact] @@ -68,7 +67,7 @@ public void ConverOBBankTransactionCodeStructure1() JsonConvert .DeserializeObject(jsonString); - result.Should().BeEquivalentTo(bankTransactionCode); + Assert.Equivalent(bankTransactionCode, result); } [Fact] @@ -88,6 +87,6 @@ public void ConvertOBAccount6() var result = JsonConvert.DeserializeObject(jsonString); - result.Should().BeEquivalentTo(initialValue); + Assert.Equivalent(initialValue, result); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityMapperTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityMapperTests.cs index 48b0e455a..6eff4a573 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityMapperTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityMapperTests.cs @@ -5,7 +5,6 @@ using AutoMapper; using FinnovationLabs.OpenBanking.Library.BankApiModels.UKObDcr.V3p3.Models; using FinnovationLabs.OpenBanking.Library.Connector.Mapping; -using FluentAssertions; using Xunit; using OBClientRegistration1 = FinnovationLabs.OpenBanking.Library.BankApiModels.UKObDcr.V3p1.Models.OBClientRegistration1; @@ -67,7 +66,7 @@ public void OpenBankingDcr_V3p1Scope_MapToV3p3() var output = mapper.Map(input); - output.Scope.Should().Be("Value1 Value2"); + Assert.Equal("Value1 Value2", output.Scope); } @@ -97,13 +96,14 @@ public void OpenBankingDcr_V3p3Scope_MapToV3p1() var output = mapper.Map(input); - output.Scope.Should().NotBeNull(); + Assert.NotNull(output.Scope); - output.Scope.Should().Equal( + Assert.Equal( new List { "Value1", "Value2" - }); + }, + output.Scope); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityTypeFinderTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityTypeFinderTests.cs index a0f274bb4..4a9063c10 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityTypeFinderTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Mapping/EntityTypeFinderTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Mapping; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Mapping; @@ -19,6 +18,6 @@ public void GetOpenBankingEquivalentTypes_GetsEquivalentTypes() List typePairs = finder.GetTypesWithSourceApiEquivalent(publicType).ToList(); - typePairs.Should().HaveCount(1); + Assert.Single(typePairs); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Model/Payments/GeneratedModelImplementationTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Model/Payments/GeneratedModelImplementationTests.cs index 698b1d45e..ca5ea576f 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Model/Payments/GeneratedModelImplementationTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Model/Payments/GeneratedModelImplementationTests.cs @@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations; using System.Reflection; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Model.Payments; @@ -42,7 +41,7 @@ public void AllObjectsHaveToString() { var r = instance.ToString(); - r.Should().NotBeNullOrWhiteSpace(); + Assert.False(string.IsNullOrWhiteSpace(r)); } } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Model/Public/GenereatedModelImplementationTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Model/Public/GenereatedModelImplementationTests.cs index a0f607de6..26afd468c 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Model/Public/GenereatedModelImplementationTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Model/Public/GenereatedModelImplementationTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Reflection; -using FluentAssertions; using Newtonsoft.Json; using Xunit; @@ -44,7 +43,7 @@ public void AllObjectsHaveToString() { var r = instance.ToString(); - r.Should().NotBeNullOrWhiteSpace(); + Assert.False(string.IsNullOrWhiteSpace(r)); } } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Model/Validation/AuthorisationCallbackDataValidatorTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Model/Validation/AuthorisationCallbackDataValidatorTests.cs index a4189ab7d..2309d06ef 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Model/Validation/AuthorisationCallbackDataValidatorTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Model/Validation/AuthorisationCallbackDataValidatorTests.cs @@ -5,7 +5,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Validators; -using FluentAssertions; using FluentValidation.Results; using Xunit; @@ -27,7 +26,7 @@ public void Validate_BodyIsNull() IList? results = validator.Validate(data).Errors; - results.Should().HaveCount(1); + Assert.Single(results); } @@ -49,7 +48,7 @@ public void Validate_BodyIsDefault() IList? results = validator.Validate(data).Errors; - results.Should().HaveCount(1); + Assert.Single(results); } @@ -71,6 +70,6 @@ public void Validate_BodyIsValid() IList? results = validator.Validate(data).Errors; - results.Should().HaveCount(0); + Assert.Empty(results); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj index e9f7e58cf..ba9999dd7 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj +++ b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj @@ -12,7 +12,6 @@ - diff --git a/test/OpenBanking.Library.Connector.UnitTests/Security/CertificateExtensionsTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Security/CertificateExtensionsTests.cs index 3b1342dac..1bb69927f 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Security/CertificateExtensionsTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Security/CertificateExtensionsTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.Security; -using FluentAssertions; using Xunit; namespace FinnovationLabs.OpenBanking.Library.Connector.UnitTests.Security; @@ -23,7 +22,7 @@ public void IsPemThumbprint_False(string? tp) { bool result = tp.IsPemThumbprint(); - result.Should().BeFalse(); + Assert.False(result); } [Theory] @@ -33,6 +32,6 @@ public void IsPemThumbprint_True(string tp) { bool result = tp.IsPemThumbprint(); - result.Should().BeTrue(); + Assert.True(result); } } diff --git a/test/OpenBanking.Library.Connector.UnitTests/Security/KeySecretTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Security/KeySecretTests.cs index 302da3dff..4d49fc66f 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Security/KeySecretTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Security/KeySecretTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.KeySecrets; -using FluentAssertions; using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; @@ -22,7 +21,7 @@ public void Ctor_NullParameters_ExceptionThrown(string? vault, string? key, stri { Func a = () => new KeySecret(vault!, key!, value!); - a.Should().Throw(); + Assert.Throws(() => a()); } [Property(Verbose = PropertyTests.VerboseTests)] From aae9f03f89ed7535bfb6242166a944cd7b36e30c Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 9 May 2026 19:30:11 +0100 Subject: [PATCH 23/49] [Test] Update BankTests project --- .../OpenBanking.Library.Connector.BankTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 4c9193a4c..210836034 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -9,6 +9,7 @@ FinnovationLabs.OpenBanking.Library.Connector.BankTests Finnovation Labs Limited Finnovation Labs Limited + false From 7d97a113826cc420543118ab88d403b6f59c2d04 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 10 May 2026 21:03:09 +0100 Subject: [PATCH 24/49] [Test] Use TestOutputLoggerProvider for BankTestingFixture in bank tests --- .../BankTests/AppContextFixture.cs | 4 +- .../BankTests/BankTestingFixture.cs | 16 +++++ .../TestOutputLoggerProvider.cs | 61 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/OpenBanking.Library.Connector.BankTests/Instrumentation/TestOutputLoggerProvider.cs diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs index 367df10dd..0ea2c5c5e 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using ServiceCollectionExtensions = @@ -59,7 +60,8 @@ public AppContextFixture() builder .Logging - .AddWebHostLogging(builder.Configuration, null); + .ClearProviders() + .AddConsole(); // Build app WebApplication app = builder.Build(); diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/BankTestingFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/BankTestingFixture.cs index 51d3761e7..2e49b2436 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/BankTestingFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/BankTestingFixture.cs @@ -3,18 +3,34 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Instrumentation; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.Logging; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; public class BankTestingFixture : WebApplicationFactory { + public BankTestingFixture() + { + Server.PreserveExecutionContext = true; // ensure correct propagation of AsyncLocal + } + protected override void ConfigureWebHost(IWebHostBuilder builder) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { builder.UseContentRoot(""); } + + // Use TestOutputLoggerProvider which routes logs to MSTest test context ensuring + // logs end up in correct test's output during parallel testing. + builder.ConfigureLogging( + logging => + { + logging.ClearProviders(); + logging.AddProvider(new TestOutputLoggerProvider()); + }); } } diff --git a/test/OpenBanking.Library.Connector.BankTests/Instrumentation/TestOutputLoggerProvider.cs b/test/OpenBanking.Library.Connector.BankTests/Instrumentation/TestOutputLoggerProvider.cs new file mode 100644 index 000000000..ff2aa12b1 --- /dev/null +++ b/test/OpenBanking.Library.Connector.BankTests/Instrumentation/TestOutputLoggerProvider.cs @@ -0,0 +1,61 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma warning disable MSTESTEXP // allows use of TestContext.Current +using Microsoft.Extensions.Logging; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Instrumentation; + +/// +/// ILoggerProvider that routes all ILogger output to the current test's context +/// via TestContext.Current. Used because console provider does not correctly route logs +/// from BankTestingFixture (DUT) to correct test's output when tests are run in parallel. +/// +public sealed class TestOutputLoggerProvider : ILoggerProvider +{ + public ILogger CreateLogger(string categoryName) => new TestOutputLogger(categoryName); + + public void Dispose() { } +} + +file sealed class TestOutputLogger(string categoryName) : ILogger +{ + public IDisposable? BeginScope(TState state) + where TState : notnull => null; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log( + LogLevel logLevel, + EventId eventId, + TState state, + Exception? exception, + Func formatter) + { + // Try to produce same log output as console provider using "simple" formatter + + string level = logLevel switch + { + LogLevel.Trace => "trce", + LogLevel.Debug => "dbug", + LogLevel.Information => "info", + LogLevel.Warning => "warn", + LogLevel.Error => "fail", + LogLevel.Critical => "crit", + _ => "none" + }; + + const string indent = " "; + var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + string body = formatter(state, exception).ReplaceLineEndings($"\n{indent}").TrimEnd(); + var message = $"{timestamp} {level}: {categoryName}[{eventId.Id}]\n{indent}{body}"; + if (exception is not null) + { + message += $"\n{indent}{exception.ToString().ReplaceLineEndings($"\n{indent}")}"; + } + + Action write = TestContext.Current is { } ctx ? ctx.WriteLine : Console.WriteLine; + write(message); + } +} From e64663e6506ebea256fdf469c4ca79a471bebd91 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 10 May 2026 21:25:26 +0100 Subject: [PATCH 25/49] Support consent type-specific auth endpoint --- .../CustomBehaviour/ConsentAuthGetCustomBehaviour.cs | 5 +++++ .../Operations/CreateAuthUrl.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/ConsentAuthGetCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/ConsentAuthGetCustomBehaviour.cs index b2ed8308e..c859c24f2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/ConsentAuthGetCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/ConsentAuthGetCustomBehaviour.cs @@ -34,6 +34,11 @@ public class ConsentAuthGetCustomBehaviour public string? SingleBase64EncodedParameterName { get; init; } + /// + /// Overrides authorization endpoint URL for this consent type. + /// + public string? AuthorizationEndpoint { get; init; } + public bool? AddRedundantOAuth2StateRequestParameter { get; set; } public bool? AddRedundantOAuth2NonceRequestParameter { get; set; } diff --git a/src/OpenBanking.Library.Connector/Operations/CreateAuthUrl.cs b/src/OpenBanking.Library.Connector/Operations/CreateAuthUrl.cs index 1cd53567d..7908408da 100644 --- a/src/OpenBanking.Library.Connector/Operations/CreateAuthUrl.cs +++ b/src/OpenBanking.Library.Connector/Operations/CreateAuthUrl.cs @@ -69,6 +69,11 @@ internal static (string authUrl, string state, string nonce, string? codeVerifie string appSessionId = GenerateRandomString(32); string responseTypeString = JsonConvert.SerializeObject(responseType).Replace("\"", ""); + if (customBehaviourConsentAuthGet?.AuthorizationEndpoint is not null) + { + authorisationEndpoint = customBehaviourConsentAuthGet.AuthorizationEndpoint; + } + // Add common parameters var keyValuePairs = new Dictionary { From b5cf473904edf9fad7771fe509cd4b79205bc2db Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 10 May 2026 21:03:18 +0100 Subject: [PATCH 26/49] Add Zopa bank with PISP (domestic payments) API --- .../BankProfiles/BankGroups/BankGroup.cs | 5 +- .../BankGroups/BankGroupExtensions.cs | 3 +- .../BankProfiles/BankGroups/Zopa.cs | 23 ++++++ .../BankProfiles/BankProfileEnum.cs | 5 +- .../BankProfiles/BankProfileExtensions.cs | 1 + .../BankProfiles/BankProfileService.cs | 4 +- .../BankProfiles/Generators/ZopaGenerator.cs | 70 +++++++++++++++++++ .../GlobalUsings.cs | 4 ++ .../Management/BankRegistrationOperations.cs | 1 + .../BankTests/AppTests.cs | 4 ++ .../GlobalUsings.cs | 4 ++ 11 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Zopa.cs create mode 100644 src/OpenBanking.Library.Connector/BankProfiles/Generators/ZopaGenerator.cs diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs index fb8a0a9ad..bb29824c0 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs @@ -57,5 +57,8 @@ public enum BankGroup Tsb, [EnumMember(Value = "Wise")] - Wise + Wise, + + [EnumMember(Value = "Zopa")] + Zopa } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs index 4b5bb46f1..cefd67fc2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs @@ -25,7 +25,8 @@ public static class BankGroupExtensions [BankGroup.Starling] = new Starling(), [BankGroup.Tide] = new Tide(), [BankGroup.Tsb] = new Tsb(), - [BankGroup.Wise] = new Wise() + [BankGroup.Wise] = new Wise(), + [BankGroup.Zopa] = new Zopa() }; public static IBankGroupData GetBankGroupData(this BankGroup bankGroup) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Zopa.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Zopa.cs new file mode 100644 index 000000000..07161aa3d --- /dev/null +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Zopa.cs @@ -0,0 +1,23 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; + +public enum ZopaBank +{ + Zopa +} + +public class Zopa() : BankGroupBase(BankGroup.Zopa) +{ + protected override ConcurrentDictionary BankProfileToBank { get; } = + new() { [BankProfileEnum.Zopa_Zopa] = ZopaBank.Zopa }; + + public override ZopaRegistrationGroup GetRegistrationGroup( + ZopaBank bank, + RegistrationScopeEnum registrationScopeEnum) => bank; +} diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index c9f80a003..2a534721b 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs @@ -184,5 +184,8 @@ public enum BankProfileEnum Tsb_Tsb, [EnumMember(Value = "Wise_Wise")] - Wise_Wise + Wise_Wise, + + [EnumMember(Value = "Zopa_Zopa")] + Zopa_Zopa } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs index ce7dcb78c..36993cbb7 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs @@ -68,6 +68,7 @@ public static BankGroup GetBankGroup(this BankProfileEnum bankProfileEnum) => BankProfileEnum.Tide_Tide => BankGroup.Tide, BankProfileEnum.Tsb_Tsb => BankGroup.Tsb, BankProfileEnum.Wise_Wise => BankGroup.Wise, + BankProfileEnum.Zopa_Zopa => BankGroup.Zopa, _ => throw new ArgumentOutOfRangeException(nameof(bankProfileEnum), bankProfileEnum, null) }; } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs index 90d3028fd..cf05bfda2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs @@ -56,7 +56,8 @@ public BankProfileService( [BankGroup.Starling] = new StarlingGenerator(bankProfilesSettingsProvider), [BankGroup.Tide] = new TideGenerator(bankProfilesSettingsProvider), [BankGroup.Tsb] = new TsbGenerator(bankProfilesSettingsProvider), - [BankGroup.Wise] = new WiseGenerator(bankProfilesSettingsProvider) + [BankGroup.Wise] = new WiseGenerator(bankProfilesSettingsProvider), + [BankGroup.Zopa] = new ZopaGenerator(bankProfilesSettingsProvider) }; } @@ -84,6 +85,7 @@ public BankProfile GetBankProfile(BankProfileEnum bankProfileEnum) => BankGroup.Tide => GetBankProfile(profileEnum, _instrumentationClient), BankGroup.Tsb => GetBankProfile(profileEnum, _instrumentationClient), BankGroup.Wise => GetBankProfile(profileEnum, _instrumentationClient), + BankGroup.Zopa => GetBankProfile(profileEnum, _instrumentationClient), _ => throw new ArgumentOutOfRangeException() }, LazyThreadSafetyMode.ExecutionAndPublication)) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/ZopaGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ZopaGenerator.cs new file mode 100644 index 000000000..674b38754 --- /dev/null +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/ZopaGenerator.cs @@ -0,0 +1,70 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.BankApiModels.Json; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour.Management; +using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour.PaymentInitiation; +using FinnovationLabs.OpenBanking.Library.Connector.Configuration; +using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Configuration; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.Generators; + +public class ZopaGenerator : BankProfileGeneratorBase +{ + public ZopaGenerator( + ISettingsProvider bankProfilesSettingsProvider) : base( + bankProfilesSettingsProvider, + BankGroup.Zopa) { } + + public override BankProfile GetBankProfile( + ZopaBank bank, + IInstrumentationClient instrumentationClient) => + new( + _bankGroupData.GetBankProfile(bank), + "https://auth1.openbanking.zopa.com", // from https://developer.openbanking-sandbox.zopa.com/perry/developer/documentation?resource=euhub-zopa-portal&document=docs/30-production.md + GetFinancialId(bank), + null, + null, + null, + new PaymentInitiationApi + { + ApiVersion = PaymentInitiationApiVersion.Version4p0, + BaseUrl = GetApiBaseUrl("v4.0/pisp") + }, + null, + null, + true, + instrumentationClient) + { + CustomBehaviour = + new CustomBehaviourClass + { + BankRegistrationPost = new BankRegistrationPostCustomBehaviour + { + ClientIdIssuedAtClaimResponseJsonConverter = + DateTimeOffsetUnixConverterEnum.UnixMilliSecondsJsonFormat + }, + DomesticPaymentConsentAuthGet = new ConsentAuthGetCustomBehaviour + { + AuthorizationEndpoint = + "zopa://open-banking/pis-single-payment-consent" // from https://developer.openbanking-sandbox.zopa.com/perry/developer/documentation?resource=euhub-zopa-portal&document=docs/API%20Overview/pis.md + }, + DomesticPayment = + new DomesticPaymentCustomBehaviour + { + ResponseDataRefundMayBeMissingOrWrong = true, + ResponseDataDebtorMayBeMissingOrWrong = true + } + }, + BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationGetEndpoint = true }, + PispUseV4ByDefault = true + }; + + private static string GetApiBaseUrl(string suffix) => + $"https://rs1.openbanking.zopa.com/open-banking/{suffix}"; // from https://developer.openbanking-sandbox.zopa.com/perry/developer/documentation?resource=euhub-zopa-portal&document=docs/30-production.md +} diff --git a/src/OpenBanking.Library.Connector/GlobalUsings.cs b/src/OpenBanking.Library.Connector/GlobalUsings.cs index 2aa7accae..b0a475d53 100644 --- a/src/OpenBanking.Library.Connector/GlobalUsings.cs +++ b/src/OpenBanking.Library.Connector/GlobalUsings.cs @@ -40,6 +40,10 @@ FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; global using TsbRegistrationGroup = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; +global using ZopaBank = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.ZopaBank; +global using ZopaRegistrationGroup = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.ZopaBank; // UK Open Banking API models global using AccountAndTransactionModelsV3p1p11 = diff --git a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs index aa7ce1ab6..184af3891 100644 --- a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs @@ -286,6 +286,7 @@ await _configurationRead.GetOpenIdConfigurationAsync( BankGroup.Tide => GetExistingRegistration, BankGroup.Tsb => GetExistingRegistration, BankGroup.Wise => GetExistingRegistration, + BankGroup.Zopa => GetExistingRegistration, _ => throw new ArgumentOutOfRangeException() }; (BankRegistrationEntity? existingGroupRegistration, diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index 6a2ca5c84..4a6b0895a 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs @@ -242,6 +242,10 @@ public static IEnumerable GetTestCases(TestType testType) bankProfileFromEnv, registrationScope, bankGroup), + BankGroup.Zopa => GetAllInRegistrationGroup( + bankProfileFromEnv, + registrationScope, + bankGroup), _ => throw new ArgumentOutOfRangeException() }; } diff --git a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs index 2aa7accae..b0a475d53 100644 --- a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs +++ b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs @@ -40,6 +40,10 @@ FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.WiseBank; global using TsbRegistrationGroup = FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.TsbBank; +global using ZopaBank = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.ZopaBank; +global using ZopaRegistrationGroup = + FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups.ZopaBank; // UK Open Banking API models global using AccountAndTransactionModelsV3p1p11 = From 51a4c8fd641f448c2d6546a6224b04524bf24e20 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Mon, 11 May 2026 23:45:33 +0100 Subject: [PATCH 27/49] [Docs] Update docs --- docs/apis/account-and-transaction/openapi-aisp.json | 2 +- docs/apis/auth-contexts/openapi-auth.json | 2 +- docs/apis/management/openapi-manage.json | 8 +++++--- docs/apis/payment-initiation/openapi-pisp.json | 2 +- docs/apis/variable-recurring-payments/openapi-vrp.json | 2 +- docs/bank-integrations.md | 1 + .../OpenBanking.WebApp.Connector.csproj | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index 73999e5cc..86a2cbc38 100644 --- a/docs/apis/account-and-transaction/openapi-aisp.json +++ b/docs/apis/account-and-transaction/openapi-aisp.json @@ -3,7 +3,7 @@ "info": { "title": "Account and Transaction API", "description": "Account and Transaction API for Open Banking Connector Web App", - "version": "18.4.0" + "version": "18.5.0" }, "paths": { "/aisp/account-access-consent-auth-contexts": { diff --git a/docs/apis/auth-contexts/openapi-auth.json b/docs/apis/auth-contexts/openapi-auth.json index 39d7a391f..9295b7266 100644 --- a/docs/apis/auth-contexts/openapi-auth.json +++ b/docs/apis/auth-contexts/openapi-auth.json @@ -3,7 +3,7 @@ "info": { "title": "Auth Contexts API", "description": "Auth Contexts API for Open Banking Connector Web App", - "version": "18.4.0" + "version": "18.5.0" }, "paths": { "/auth/redirect-delegate": { diff --git a/docs/apis/management/openapi-manage.json b/docs/apis/management/openapi-manage.json index 78ace0ae0..4c6277731 100644 --- a/docs/apis/management/openapi-manage.json +++ b/docs/apis/management/openapi-manage.json @@ -3,7 +3,7 @@ "info": { "title": "Management API", "description": "Management API for Open Banking Connector Web App", - "version": "18.4.0" + "version": "18.5.0" }, "paths": { "/manage/bank-registrations": { @@ -731,7 +731,8 @@ "Starling", "Tide", "Tsb", - "Wise" + "Wise", + "Zopa" ], "type": "string" }, @@ -793,7 +794,8 @@ "Starling_Starling", "Tide_Tide", "Tsb_Tsb", - "Wise_Wise" + "Wise_Wise", + "Zopa_Zopa" ], "type": "string", "description": "Banks for which bank profiles have been created. Called BankProfileEnum to avoid confusion with the associated\r\nclass BankProfile." diff --git a/docs/apis/payment-initiation/openapi-pisp.json b/docs/apis/payment-initiation/openapi-pisp.json index 9195418ba..346d12e28 100644 --- a/docs/apis/payment-initiation/openapi-pisp.json +++ b/docs/apis/payment-initiation/openapi-pisp.json @@ -3,7 +3,7 @@ "info": { "title": "Payment Initiation API", "description": "Payment Initiation API for Open Banking Connector Web App", - "version": "18.4.0" + "version": "18.5.0" }, "paths": { "/pisp/domestic-payment-consent-auth-contexts": { diff --git a/docs/apis/variable-recurring-payments/openapi-vrp.json b/docs/apis/variable-recurring-payments/openapi-vrp.json index ceac5bdc7..c85d22ea3 100644 --- a/docs/apis/variable-recurring-payments/openapi-vrp.json +++ b/docs/apis/variable-recurring-payments/openapi-vrp.json @@ -3,7 +3,7 @@ "info": { "title": "Variable Recurring Payments API", "description": "Variable Recurring Payments API for Open Banking Connector Web App", - "version": "18.4.0" + "version": "18.5.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/docs/bank-integrations.md b/docs/bank-integrations.md index 4dcb21e79..1e3ed4987 100644 --- a/docs/bank-integrations.md +++ b/docs/bank-integrations.md @@ -21,3 +21,4 @@ UK Open Banking uses the [Financial-grade API](https://openid.net/specs/openid-f | Tide | All brands | :white_large_square: | Tide_Tide | AISP,
PISP (domestic payments) | | TSB | All brands | :white_large_square: | Tsb_Tsb | AISP,
PISP (domestic payments) | | Wise | All brands | :white_large_square: | Wise_Wise | AISP,
PISP (domestic payments) | +| Zopa | All brands | :white_large_square: | Zopa_Zopa | PISP (domestic payments) | diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index b4d4edea8..d78bb1e3e 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 18.4.0 + 18.5.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false From 390a723fe1200884f2672c5841e203a7b7039957 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 14:59:52 +0100 Subject: [PATCH 28/49] Update LinksUrlOperations --- .../Operations/LinksUrlOperations.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/OpenBanking.Library.Connector/Operations/LinksUrlOperations.cs b/src/OpenBanking.Library.Connector/Operations/LinksUrlOperations.cs index 1a09bc841..8749272db 100644 --- a/src/OpenBanking.Library.Connector/Operations/LinksUrlOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/LinksUrlOperations.cs @@ -119,15 +119,19 @@ public static IList GetMethodExpectedLinkUrls( bool responseLinksMayAddSlash = readWriteGetCustomBehaviour?.ResponseLinksMayAddSlash ?? false; if (responseLinksMayAddSlash) { - expectedLinkUrls.Add(new Uri(expectedLinkUrl + "/")); + int count = expectedLinkUrls.Count; + for (var idx = 0; idx < count; idx++) + { + expectedLinkUrls.Add(new Uri($"{expectedLinkUrls[idx]}/")); + } } (string oldValue, string newValue)? responseLinksAllowReplace = readWriteGetCustomBehaviour?.GetResponseLinksAllowReplace?.Invoke(useV4NotV3); if (responseLinksAllowReplace is not null) { (string oldValue, string newValue) = responseLinksAllowReplace.Value; - int noUrls = expectedLinkUrls.Count; - for (var idx = 0; idx < noUrls; idx++) + int count = expectedLinkUrls.Count; + for (var idx = 0; idx < count; idx++) { expectedLinkUrls.Add(new Uri($"{expectedLinkUrls[idx]}".Replace(oldValue, newValue))); } From 45fb2e2a11d1a2ad227d17f508fdab50b7ad7a6c Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 13:59:45 +0100 Subject: [PATCH 29/49] Update Lloyds --- .../BankProfiles/Generators/LloydsGenerator.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs index f44f484aa..83a578e2d 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs @@ -85,11 +85,13 @@ or LloydsBank.BankOfScotlandBusiness BaseUrl = GetPaymentsApiBaseUrl(bank, true) }, new VariableRecurringPaymentsApi { BaseUrl = GetPaymentsApiBaseUrl(bank, false) }, - new VariableRecurringPaymentsApi - { - ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0, - BaseUrl = GetPaymentsApiBaseUrl(bank, true) - }, + bank is not LloydsBank.MbnaPersonal + ? new VariableRecurringPaymentsApi + { + ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0, + BaseUrl = GetPaymentsApiBaseUrl(bank, true) + } + : null, bank is not LloydsBank.Sandbox, instrumentationClient) { From 99afe3339a181b1bc077e5f38ff639fea858bdff Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 14:01:22 +0100 Subject: [PATCH 30/49] Update NatWest --- .../Generators/NatWestGenerator.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs index 8505dce59..44eb96c7f 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs @@ -106,13 +106,11 @@ or NatWestBank.UlsterBankNiBankline bank is NatWestBank.Coutts ? null : new VariableRecurringPaymentsApi { BaseUrl = GetPaymentsBaseUrl(bank, "v3.1") }, - bank is NatWestBank.Coutts - ? null - : new VariableRecurringPaymentsApi - { - BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), - ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 - }, + new VariableRecurringPaymentsApi + { + BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), + ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 + }, bank is not (NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox), instrumentationClient) { @@ -248,7 +246,7 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox PreferMisspeltContractPresentIndicator = true, ResponseDataFundsAvailableResultFundsAvailableMayBeWrong = bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, - ResponseLinksMayAddSlash = bank is NatWestBank.Coutts, + ResponseLinksMayHaveIncorrectUrlBeforeQuery = bank is NatWestBank.Coutts, ResponseRiskContractPresentIndicatorMayBeMissingOrWrong = bank is NatWestBank.Coutts }, DomesticPayment = @@ -261,7 +259,7 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, ResponseDataDebtorIdentificationMayBeMissingOrWrong = bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, - ResponseLinksMayAddSlash = bank is NatWestBank.Coutts + ResponseLinksMayHaveIncorrectUrlBeforeQuery = bank is NatWestBank.Coutts }, DomesticVrpConsent = new DomesticVrpConsentCustomBehaviour @@ -270,13 +268,15 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox ResponseRiskContractPresentIndicatorMayBeMissingOrWrong = bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, ResponseDataFundsAvailableResultFundsAvailableMayBeWrong = - bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox + bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, + ResponseLinksMayHaveIncorrectUrlBeforeQuery = bank is NatWestBank.Coutts }, DomesticVrp = new DomesticVrpCustomBehaviour { PreferMisspeltContractPresentIndicator = true, ResponseDataStatusMayBeMissingOrWrong = - bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox + bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox, + ResponseLinksMayHaveIncorrectUrlBeforeQuery = bank is NatWestBank.Coutts } }, AispUseV4ByDefault = true, From d0641045a87b21f44511ca4283abbde64e1459f0 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 22 May 2026 16:20:24 +0100 Subject: [PATCH 31/49] Add ServerError records and use to populate ProblemDetails responses --- .../GlobalExceptionFilter.cs | 28 +++++++--------- .../Extensions/StringExtensions.cs | 10 ++++++ .../Fluent/HttpResponseException.cs | 31 +++++++++++------- .../Operations/AuthContextServerErrors.cs | 32 +++++++++++++++++++ .../Operations/AuthContextUpdate.cs | 19 ++--------- 5 files changed, 76 insertions(+), 44 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/Operations/AuthContextServerErrors.cs diff --git a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs b/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs index 58b172dcf..846a3a8d5 100644 --- a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs +++ b/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs @@ -2,6 +2,7 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using FinnovationLabs.OpenBanking.Library.Connector.Extensions; using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; @@ -15,30 +16,23 @@ public void OnException(ExceptionContext context) { if (context.Exception is HttpResponseException exception) { + ServerError serverError = exception.ServerError; ProblemDetails problemDetails = problemDetailsFactory.CreateProblemDetails( context.HttpContext, - exception.StatusCode, - GetTitleString(exception.Title), + serverError.StatusCode, + serverError.Title, null, - exception.Message); - if (exception.Extensions is not null) + serverError.Detail); + + foreach ((string key, object? value) in serverError.Extensions) { - problemDetails.Extensions = exception.Extensions; + problemDetails.Extensions[key.ToCamelCase()] = value; } - context.Result = new ObjectResult(problemDetails) { StatusCode = exception.StatusCode }; - context.ExceptionHandled = true; - } - } + problemDetails.Extensions["serverErrorType"] = serverError.ServerErrorType.ToString().ToCamelCase(); - private static string GetTitleString(ProblemDetailsTitle title) - { - var titleString = title.ToString(); - if (string.IsNullOrEmpty(titleString) || - char.IsLower(titleString[0])) - { - return titleString; + context.Result = new ObjectResult(problemDetails) { StatusCode = serverError.StatusCode }; + context.ExceptionHandled = true; } - return char.ToLower(titleString[0]) + titleString[1..]; } } diff --git a/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs b/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs index 507bc22cc..ac6553497 100644 --- a/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs +++ b/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs @@ -23,4 +23,14 @@ public static string PascalOrCamelToKebabCase(this string value) string returnString = string.Join("-", stringMatches).ToLower(); return returnString; } + + public static string ToCamelCase(this string value) + { + if (string.IsNullOrEmpty(value) || + char.IsLower(value[0])) + { + return value; + } + return char.ToLower(value[0]) + value[1..]; + } } diff --git a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs index b8405c4c5..e2cb27466 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -2,26 +2,35 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; + namespace FinnovationLabs.OpenBanking.Library.Connector.Fluent; -public enum ProblemDetailsTitle +public enum ServerErrorType { AuthContextNotFound, AuthContextStale } +public abstract record ServerError +{ + public abstract ServerErrorType ServerErrorType { get; } + + public abstract string Title { get; } + + public abstract string Detail { get; } + + public abstract int StatusCode { get; } + + public virtual IReadOnlyDictionary Extensions => + ImmutableDictionary.Empty; +} + /// /// Exception exposed via HTTP response /// -public class HttpResponseException( - ProblemDetailsTitle title, - string detail, - int statusCode, - IDictionary? extensions = null) : Exception(detail) +public class HttpResponseException(ServerError serverError) + : Exception(serverError.Detail) { - public ProblemDetailsTitle Title { get; } = title; - - public int StatusCode { get; } = statusCode; - - public IDictionary? Extensions { get; } = extensions; + public ServerError ServerError { get; } = serverError; } diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextServerErrors.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextServerErrors.cs new file mode 100644 index 000000000..3c74a9017 --- /dev/null +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextServerErrors.cs @@ -0,0 +1,32 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; + +namespace FinnovationLabs.OpenBanking.Library.Connector.Operations; + +internal record AuthContextNotFoundServerError(string State) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.AuthContextNotFound; + public override string Title => "authContextNotFound"; + public override string Detail => "No record found for Auth Context with specified state."; + public override int StatusCode => 400; + + public override IReadOnlyDictionary Extensions { get; } = + new Dictionary { ["state"] = State }; +} + +internal record AuthContextStaleServerError(string State) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.AuthContextStale; + public override string Title => "authContextStale"; + + public override string Detail => + "Auth context exists but now stale (more than ten minutes old) so will not process redirect. Please create a new auth context and authenticate again."; + + public override int StatusCode => 400; + + public override IReadOnlyDictionary Extensions { get; } = + new Dictionary { ["state"] = State }; +} diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index beeac9faa..67c0330ab 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -148,11 +148,7 @@ public async .DomesticVrpConsentRefreshTokensNavigation) .AsSplitQuery() // Load collections in separate SQL queries .SingleOrDefault(x => x.State == state) ?? - throw new HttpResponseException( - ProblemDetailsTitle.AuthContextNotFound, - "No record found for Auth Context with specified state.", - 400, - new Dictionary { ["state"] = $"{state}" }); + throw new HttpResponseException(new AuthContextNotFoundServerError(state)); } else { @@ -160,11 +156,7 @@ public async _authContextMethods .DbSet .SingleOrDefault(x => x.State == state) ?? - throw new HttpResponseException( - ProblemDetailsTitle.AuthContextNotFound, - "No record found for Auth Context with specified state.", - 400, - new Dictionary { ["state"] = $"{state}" }); + throw new HttpResponseException(new AuthContextNotFoundServerError(state)); } // Only accept redirects within 10 mins of auth context (session) creation @@ -173,12 +165,7 @@ public async .AddSeconds(authContextExpiryIntervalInSeconds); if (_timeProvider.GetUtcNow() > authContextExpiryTime) { - throw new HttpResponseException( - ProblemDetailsTitle.AuthContextStale, - "Auth context exists but now stale (more than ten minutes old) so will not process redirect. " + - "Please create a new auth context and authenticate again.", - 400, - new Dictionary { ["state"] = $"{state}" }); + throw new HttpResponseException(new AuthContextStaleServerError(state)); } // Validate error parameter From da1334cb53203f497841c550838c635266c9e8a7 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 22 May 2026 17:05:19 +0100 Subject: [PATCH 32/49] Add ProblemDetails responses for consent not found errors --- .../Extensions/StringExtensions.cs | 21 +++++ .../Fluent/HttpResponseException.cs | 4 +- ...countAccessConsentAuthContextOperations.cs | 5 +- .../AccountAccessConsentCommon.cs | 14 ++- .../AccountAccessConsentDelete.cs | 2 +- .../AccountAccessConsentOperations.cs | 2 +- .../AccountAndTransaction/AccountGet.cs | 5 +- .../AccountAndTransaction/BalanceGet.cs | 5 +- .../AccountAndTransaction/DirectDebitGet.cs | 5 +- .../AccountAndTransaction/MonzoPotGet.cs | 5 +- .../AccountAndTransaction/Party2Get.cs | 5 +- .../AccountAndTransaction/PartyGet.cs | 5 +- .../AccountAndTransaction/StandingOrderGet.cs | 5 +- .../AccountAndTransaction/TransactionGet.cs | 5 +- .../Operations/AuthContextUpdate.cs | 9 +- .../Operations/ConsentServerErrors.cs | 85 +++++++++++++++++++ .../PaymentInitiation/DomesticPayment.cs | 5 +- ...sticPaymentConsentAuthContextOperations.cs | 5 +- .../DomesticPaymentConsentCommon.cs | 14 ++- .../DomesticPaymentConsentOperations.cs | 7 +- .../VariableRecurringPayments/DomesticVrp.cs | 5 +- ...DomesticVrpConsentAuthContextOperations.cs | 5 +- .../DomesticVrpConsentCommon.cs | 13 ++- .../DomesticVrpConsentDelete.cs | 2 +- .../DomesticVrpConsentOperations.cs | 9 +- 25 files changed, 213 insertions(+), 34 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/Operations/ConsentServerErrors.cs diff --git a/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs b/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs index ac6553497..8a40e9655 100644 --- a/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs +++ b/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs @@ -2,6 +2,7 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text; using System.Text.RegularExpressions; namespace FinnovationLabs.OpenBanking.Library.Connector.Extensions; @@ -33,4 +34,24 @@ public static string ToCamelCase(this string value) } return char.ToLower(value[0]) + value[1..]; } + + public static string FromPascalCaseToLowerWords(this string value) + { + if (string.IsNullOrEmpty(value)) + { + return value; + } + + var sb = new StringBuilder(); + foreach (char c in value) + { + if (char.IsUpper(c) && + sb.Length > 0) + { + sb.Append(' '); + } + sb.Append(char.ToLower(c)); + } + return sb.ToString(); + } } diff --git a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs index e2cb27466..c0c350641 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -9,7 +9,9 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.Fluent; public enum ServerErrorType { AuthContextNotFound, - AuthContextStale + AuthContextStale, + ConsentNotFound, + ConsentFromRequestNotFound } public abstract record ServerError diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentAuthContextOperations.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentAuthContextOperations.cs index b0f94a025..97fca4b08 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentAuthContextOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentAuthContextOperations.cs @@ -84,7 +84,10 @@ public AccountAccessConsentAuthContextOperations( // Load AccountAccessConsent and related (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(request.AccountAccessConsentId, false); + await _accountAccessConsentCommon.GetAccountAccessConsent( + request.AccountAccessConsentId, + false, + ConsentIdSource.RequestBody); string authorizationEndpoint = bankRegistration.AuthorizationEndpoint; string externalApiConsentId = persistedConsent.ExternalApiId; diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs index 724f7c48b..6df50d2b9 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs @@ -7,6 +7,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; using Microsoft.EntityFrameworkCore; using AccountAccessConsentPersisted = @@ -50,7 +51,8 @@ public async SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret)> GetAccountAccessConsent( Guid consentId, - bool dbTracking) + bool dbTracking, + ConsentIdSource consentIdSource) { IQueryable db = dbTracking ? _entityMethods.DbSet @@ -68,7 +70,10 @@ await db .Include(o => o.BankRegistrationNavigation.ExternalApiSecretsNavigation) .AsSplitQuery() // Load collections in separate SQL queries .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Account Access Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.AccountAccessConsent, + consentIdSource, + consentId); bankRegistration = persistedConsent.BankRegistrationNavigation; softwareStatement = bankRegistration.SoftwareStatementNavigation; @@ -81,7 +86,10 @@ await db persistedConsent = await db .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Account Access Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.AccountAccessConsent, + consentIdSource, + consentId); bankRegistration = await _bankRegistrationMethods .DbSetNoTracking .SingleAsync(x => x.Id == persistedConsent.BankRegistrationId); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentDelete.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentDelete.cs index 00a046564..221c02b72 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentDelete.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentDelete.cs @@ -64,7 +64,7 @@ protected override async // Load object (AccountAccessConsent persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(deleteParams.Id, true); + await _accountAccessConsentCommon.GetAccountAccessConsent(deleteParams.Id, true, ConsentIdSource.UrlPath); if (!deleteParams.ExcludeExternalApiOperation) { string bankApiId = persistedConsent.ExternalApiId; diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentOperations.cs index b4d516ae3..6c8d8d9b3 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentOperations.cs @@ -336,7 +336,7 @@ public async // Load AccountAccessConsent and related (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.Id, false); + await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.Id, false, ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; bool excludeExternalApiOperation = diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs index f5ba77ae8..f8f2b964f 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs @@ -60,7 +60,10 @@ public AccountGet( // Get consent and associated data (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs index 44c8721f4..7b5054450 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs @@ -60,7 +60,10 @@ public BalanceGet( // Get consent and associated data (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs index 4129d7608..335e20d13 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs @@ -63,7 +63,10 @@ public DirectDebitGet( // Get consent and associated data (AccountAccessConsent persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs index 28533bf54..00160cc42 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs @@ -167,7 +167,10 @@ public MonzoPotGet( // Get consent and associated data (AccountAccessConsent persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs index 2522e433c..29f176cc6 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs @@ -60,7 +60,10 @@ public Party2Get( // Get consent and associated data (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs index 1b4d079eb..e235e8243 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs @@ -60,7 +60,10 @@ public PartyGet( // Get consent and associated data (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs index 5a1a172d2..650cad5d0 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs @@ -60,7 +60,10 @@ public StandingOrderGet( // Get consent and associated data (AccountAccessConsent persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs index 847991444..51c26a96d 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs @@ -61,7 +61,10 @@ public TransactionGet( // Get consent and associated data (AccountAccessConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _accountAccessConsentCommon.GetAccountAccessConsent(readParams.ConsentId, true); + await _accountAccessConsentCommon.GetAccountAccessConsent( + readParams.ConsentId, + true, + ConsentIdSource.RequestHeader); // Get bank profile BankProfile bankProfile = _bankProfileService.GetBankProfile(bankRegistration.BankProfile); diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index 67c0330ab..b6c1b15a8 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -209,7 +209,8 @@ public async SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret) = await _accountAccessConsentCommon.GetAccountAccessConsent( ac.AccountAccessConsentId, - true); + true, + ConsentIdSource.DatabaseForeignKey); AccessTokenEntity? accessToken = await _accountAccessConsentCommon.GetAccessToken(persistedConsent.Id, true); @@ -229,7 +230,8 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret) = await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( ac.DomesticPaymentConsentId, - true); + true, + ConsentIdSource.DatabaseForeignKey); AccessTokenEntity? accessToken = await _domesticPaymentConsentCommon.GetAccessToken(persistedConsent.Id, true); RefreshTokenEntity? refreshToken = @@ -249,7 +251,8 @@ await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret) = await _domesticVrpConsentCommon.GetDomesticVrpConsent( ac.DomesticVrpConsentId, - true); + true, + ConsentIdSource.DatabaseForeignKey); AccessTokenEntity? accessToken = await _domesticVrpConsentCommon.GetAccessToken(persistedConsent.Id, true); RefreshTokenEntity? refreshToken = diff --git a/src/OpenBanking.Library.Connector/Operations/ConsentServerErrors.cs b/src/OpenBanking.Library.Connector/Operations/ConsentServerErrors.cs new file mode 100644 index 000000000..88861afdc --- /dev/null +++ b/src/OpenBanking.Library.Connector/Operations/ConsentServerErrors.cs @@ -0,0 +1,85 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.Connector.Extensions; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; + +namespace FinnovationLabs.OpenBanking.Library.Connector.Operations; + +internal enum ConsentIdSource +{ + UrlPath, + RequestHeader, + RequestBody, + DatabaseForeignKey +} + +internal enum ConsentIdRequestSource +{ + RequestHeader, + RequestBody +} + +internal record ConsentNotFoundServerError(ConsentType ConsentType, Guid ConsentId) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.ConsentNotFound; + public override string Title => "Consent not found"; + + public override string Detail => + $"No record found for {ConsentType.ToString().FromPascalCaseToLowerWords()} with ID {ConsentId}."; + + public override int StatusCode => 404; + + public override IReadOnlyDictionary Extensions { get; } = + new Dictionary + { + ["consentType"] = ConsentType.ToString().ToCamelCase(), + ["consentId"] = ConsentId + }; +} + +internal record ConsentFromRequestNotFoundServerError( + ConsentType ConsentType, + Guid ConsentId, + ConsentIdRequestSource Source) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.ConsentFromRequestNotFound; + public override string Title => "Consent from request not found"; + + public override string Detail => + $"No record found for {ConsentType.ToString().FromPascalCaseToLowerWords()} with ID {ConsentId}."; + + public override int StatusCode => 400; + + public override IReadOnlyDictionary Extensions { get; } = + new Dictionary + { + ["consentType"] = ConsentType.ToString().ToCamelCase(), + ["consentId"] = ConsentId, + ["consentIdSource"] = Source.ToString().ToCamelCase() + }; +} + +internal static class ConsentServerErrors +{ + internal static Exception ConsentNotFoundException( + ConsentType consentType, + ConsentIdSource source, + Guid id) => + source switch + { + ConsentIdSource.UrlPath => + new HttpResponseException(new ConsentNotFoundServerError(consentType, id)), + ConsentIdSource.RequestHeader => + new HttpResponseException( + new ConsentFromRequestNotFoundServerError(consentType, id, ConsentIdRequestSource.RequestHeader)), + ConsentIdSource.RequestBody => + new HttpResponseException( + new ConsentFromRequestNotFoundServerError(consentType, id, ConsentIdRequestSource.RequestBody)), + ConsentIdSource.DatabaseForeignKey => + new InvalidOperationException("Invalid foreign key"), + _ => throw new ArgumentOutOfRangeException(nameof(source)) + }; +} diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs index 28e416c58..20d21673a 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs @@ -248,7 +248,10 @@ Task CreateAsync( // Load DomesticPaymentConsent and related (DomesticPaymentConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticPaymentConsentCommon.GetDomesticPaymentConsent(request.DomesticPaymentConsentId, true); + await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( + request.DomesticPaymentConsentId, + true, + ConsentIdSource.RequestBody); string externalApiConsentId = persistedConsent.ExternalApiId; bool pispUseV4 = persistedConsent.CreatedWithV4; diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentAuthContextOperations.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentAuthContextOperations.cs index f94c893a2..86c154256 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentAuthContextOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentAuthContextOperations.cs @@ -68,7 +68,10 @@ public DomesticPaymentConsentAuthContextOperations( // Load DomesticPaymentConsent and related (DomesticPaymentConsentPersisted domesticPaymentConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? _) = - await _domesticPaymentConsentCommon.GetDomesticPaymentConsent(request.DomesticPaymentConsentId, false); + await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( + request.DomesticPaymentConsentId, + false, + ConsentIdSource.RequestBody); string authorizationEndpoint = bankRegistration.AuthorizationEndpoint; diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentCommon.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentCommon.cs index 593e62af4..6abc102cb 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentCommon.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentCommon.cs @@ -6,6 +6,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.PaymentInitiation; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; using Microsoft.EntityFrameworkCore; using DomesticPaymentConsentPersisted = @@ -49,7 +50,8 @@ public async SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret)> GetDomesticPaymentConsent( Guid consentId, - bool dbTracking) + bool dbTracking, + ConsentIdSource consentIdSource) { IQueryable db = dbTracking ? _entityMethods.DbSet @@ -68,7 +70,10 @@ await db .Include(o => o.BankRegistrationNavigation.ExternalApiSecretsNavigation) .AsSplitQuery() // Load collections in separate SQL queries .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Domestic Payment Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.DomesticPaymentConsent, + consentIdSource, + consentId); bankRegistration = persistedConsent.BankRegistrationNavigation; softwareStatement = persistedConsent.BankRegistrationNavigation.SoftwareStatementNavigation; externalApiSecret = bankRegistration.ExternalApiSecretsNavigation @@ -79,7 +84,10 @@ await db persistedConsent = await db .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Domestic Payment Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.DomesticPaymentConsent, + consentIdSource, + consentId); bankRegistration = await _bankRegistrationMethods .DbSetNoTracking .SingleAsync(x => x.Id == persistedConsent.BankRegistrationId); diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs index 59a0bfef1..8fc2a0db9 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs @@ -356,7 +356,10 @@ public async // Load DomesticPaymentConsent and related (DomesticPaymentConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticPaymentConsentCommon.GetDomesticPaymentConsent(readParams.Id, false); + await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( + readParams.Id, + false, + ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; bool pispUseV4 = persistedConsent.CreatedWithV4; @@ -529,7 +532,7 @@ Task ReadFundsConfirmationAsync // Load DomesticPaymentConsent and related (DomesticPaymentConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticPaymentConsentCommon.GetDomesticPaymentConsent(readParams.Id, true); + await _domesticPaymentConsentCommon.GetDomesticPaymentConsent(readParams.Id, true, ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; bool pispUseV4 = persistedConsent.CreatedWithV4; diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs index c64edb6e3..51e41c5d6 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs @@ -97,7 +97,10 @@ public async Task CreateAsync( // Load DomesticVrpConsent and related (DomesticVrpConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(request.DomesticVrpConsentId, true); + await _domesticVrpConsentCommon.GetDomesticVrpConsent( + request.DomesticVrpConsentId, + true, + ConsentIdSource.RequestBody); string externalApiConsentId = persistedConsent.ExternalApiId; bool vrpUseV4 = persistedConsent.CreatedWithV4 || persistedConsent.MigratedToV4; diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentAuthContextOperations.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentAuthContextOperations.cs index 4a5be77f5..d2b148338 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentAuthContextOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentAuthContextOperations.cs @@ -66,7 +66,10 @@ public DomesticVrpConsentAuthContextOperations( // Load DomesticVrpConsent and related (DomesticVrpConsent domesticVrpConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? _) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(request.DomesticVrpConsentId, false); + await _domesticVrpConsentCommon.GetDomesticVrpConsent( + request.DomesticVrpConsentId, + false, + ConsentIdSource.RequestBody); string authorizationEndpoint = bankRegistration.AuthorizationEndpoint; diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentCommon.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentCommon.cs index 8dea13e53..4962782a3 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentCommon.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentCommon.cs @@ -6,6 +6,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.VariableRecurringPayments; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; using Microsoft.EntityFrameworkCore; using DomesticVrpConsentPersisted = @@ -47,7 +48,7 @@ public DomesticVrpConsentCommon( public async Task<(DomesticVrpConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret)> - GetDomesticVrpConsent(Guid consentId, bool dbTracking) + GetDomesticVrpConsent(Guid consentId, bool dbTracking, ConsentIdSource consentIdSource) { IQueryable db = dbTracking ? _entityMethods.DbSet @@ -66,7 +67,10 @@ await db .Include(o => o.BankRegistrationNavigation.ExternalApiSecretsNavigation) .AsSplitQuery() // Load collections in separate SQL queries .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Domestic VRP Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.DomesticVrpConsent, + consentIdSource, + consentId); bankRegistration = persistedConsent.BankRegistrationNavigation; softwareStatement = persistedConsent.BankRegistrationNavigation.SoftwareStatementNavigation; externalApiSecret = bankRegistration.ExternalApiSecretsNavigation @@ -77,7 +81,10 @@ await db persistedConsent = await db .SingleOrDefaultAsync(x => x.Id == consentId) ?? - throw new KeyNotFoundException($"No record found for Domestic VRP Consent with ID {consentId}."); + throw ConsentServerErrors.ConsentNotFoundException( + ConsentType.DomesticVrpConsent, + consentIdSource, + consentId); bankRegistration = await _bankRegistrationMethods .DbSetNoTracking .SingleAsync(x => x.Id == persistedConsent.BankRegistrationId); diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentDelete.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentDelete.cs index 9f25ba118..e3611c397 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentDelete.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentDelete.cs @@ -64,7 +64,7 @@ protected override async // Load object (DomesticVrpConsent persistedObject, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(deleteParams.Id, true); + await _domesticVrpConsentCommon.GetDomesticVrpConsent(deleteParams.Id, true, ConsentIdSource.UrlPath); if (!deleteParams.ExcludeExternalApiOperation) { diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs index 29a73b298..de9fbebe0 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs @@ -101,7 +101,10 @@ Task CreateFundsConfirmationAsync( // Load DomesticVrpConsent and related (DomesticVrpConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(createParams.ConsentId, true); + await _domesticVrpConsentCommon.GetDomesticVrpConsent( + createParams.ConsentId, + true, + ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; bool vrpUseV4 = persistedConsent.CreatedWithV4 || persistedConsent.MigratedToV4; @@ -541,7 +544,7 @@ public async // Load DomesticVrpConsent and related (DomesticVrpConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(readParams.Id, false); + await _domesticVrpConsentCommon.GetDomesticVrpConsent(readParams.Id, false, ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; bool vrpUseV4 = persistedConsent.CreatedWithV4 || persistedConsent.MigratedToV4; @@ -730,7 +733,7 @@ public async // Load DomesticVrpConsent and related (DomesticVrpConsentPersisted persistedConsent, BankRegistrationEntity bankRegistration, SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret) = - await _domesticVrpConsentCommon.GetDomesticVrpConsent(updateParams.Id, true); + await _domesticVrpConsentCommon.GetDomesticVrpConsent(updateParams.Id, true, ConsentIdSource.UrlPath); string externalApiConsentId = persistedConsent.ExternalApiId; var vrpUseV4 = true; // only v4 APIs support consent migration From 3316dd8040b8f69ae82cab6c300e05330cf17ef4 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 22 May 2026 19:44:41 +0100 Subject: [PATCH 33/49] Make unused Barclays sandbox obsolete --- .../BankProfiles/BankGroups/Barclays.cs | 6 ++ .../BankProfiles/BankProfileEnum.cs | 1 + .../BankProfiles/BankProfileExtensions.cs | 2 + .../IdTokenProcessingCustomBehaviour.cs | 7 -- .../Generators/BarclaysGenerator.cs | 31 +------- .../Operations/AuthContextUpdate.cs | 47 ++++++------- .../Operations/ExternalApi/GrantPost.cs | 70 ++++++++----------- .../BankUiMethods/BarclaysUiMethods.cs | 28 +------- 8 files changed, 61 insertions(+), 131 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Barclays.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Barclays.cs index 2ec8f9b17..3cad87f2c 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Barclays.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Barclays.cs @@ -9,6 +9,7 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; public enum BarclaysBank { + [Obsolete("Not supported.")] Sandbox, Personal, Wealth, @@ -20,6 +21,7 @@ public enum BarclaysBank public enum BarclaysRegistrationGroup { + [Obsolete("Not supported.")] Sandbox, Production } @@ -29,7 +31,9 @@ public class Barclays() : BankGroupBase protected override ConcurrentDictionary BankProfileToBank { get; } = new() { +#pragma warning disable CS0618 [BankProfileEnum.Barclays_Sandbox] = BarclaysBank.Sandbox, +#pragma warning restore CS0618 [BankProfileEnum.Barclays_Personal] = BarclaysBank.Personal, [BankProfileEnum.Barclays_Wealth] = BarclaysBank.Wealth, [BankProfileEnum.Barclays_Barclaycard] = BarclaysBank.Barclaycard, @@ -41,7 +45,9 @@ public class Barclays() : BankGroupBase public override BarclaysRegistrationGroup GetRegistrationGroup( BarclaysBank bank, RegistrationScopeEnum registrationScopeEnum) => +#pragma warning disable CS0618 bank is BarclaysBank.Sandbox ? BarclaysRegistrationGroup.Sandbox : BarclaysRegistrationGroup.Production; +#pragma warning restore CS0618 } diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index 2a534721b..ddbf3c238 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs @@ -15,6 +15,7 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankProfiles; [JsonConverter(typeof(StringEnumConverter))] public enum BankProfileEnum { + [Obsolete("Not supported.")] [EnumMember(Value = "Barclays_Sandbox")] Barclays_Sandbox, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs index 36993cbb7..72cb56c48 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs @@ -11,7 +11,9 @@ public static class BankProfileExtensions public static BankGroup GetBankGroup(this BankProfileEnum bankProfileEnum) => bankProfileEnum switch { +#pragma warning disable CS0618 BankProfileEnum.Barclays_Sandbox => BankGroup.Barclays, +#pragma warning restore CS0618 BankProfileEnum.Barclays_Personal => BankGroup.Barclays, BankProfileEnum.Barclays_Wealth => BankGroup.Barclays, BankProfileEnum.Barclays_Barclaycard => BankGroup.Barclays, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs index c354b6653..6bd6509bf 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs @@ -14,8 +14,6 @@ public class IdTokenProcessingCustomBehaviour /// public IdTokenSubClaimType? IdTokenSubClaimType { get; set; } - public bool? DoNotValidateIdToken { get; set; } - public bool? IdTokenNonceClaimIsPreviousValue { get; set; } public bool? IdTokenMayNotHaveAcrClaim { get; set; } @@ -32,11 +30,6 @@ public class IdTokenProcessingCustomBehaviour public DateTimeOffsetUnixConverterEnum? IdTokenExpirationTimeClaimJsonConverter { get; set; } - public static bool GetDoNotValidateIdToken( - IdTokenProcessingCustomBehaviour? customBehaviour, - IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => - customBehaviour?.DoNotValidateIdToken ?? baseCustomBehaviour?.DoNotValidateIdToken ?? false; - public static bool GetIdTokenNonceClaimIsPreviousValue( IdTokenProcessingCustomBehaviour? customBehaviour, IdTokenProcessingCustomBehaviour? baseCustomBehaviour) => diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs index d309d706c..625827ec5 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs @@ -34,9 +34,6 @@ public override BankProfile GetBankProfile( _bankGroupData.GetBankProfile(bank), bank switch { - BarclaysBank - .Sandbox => - "https://token.sandbox.barclays.com", // from https://developer.barclays.com/apis/account-and-transactions/20e74071-13fb-44eb-b98f-2c89d6251ad8.bdn/documentation#barclays-identity-provider-(idp)-authentication-types BarclaysBank.Personal => "https://oauth.tiaa.barclays.com/BarclaysPersonal", // from https://openbanking.atlassian.net/wiki/spaces/AD/pages/998342986/Barclays+Bank+UK+Plc BarclaysBank.Wealth => @@ -71,7 +68,7 @@ public override BankProfile GetBankProfile( ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0, BaseUrl = GetApiBaseUrl("v4.0/pisp") }, - bank is not BarclaysBank.Sandbox, + true, instrumentationClient) { BankConfigurationApiSettings = new BankConfigurationApiSettings @@ -126,18 +123,6 @@ or BarclaysBank.BarclaycardCommercialPayments elementsToRemove.Add(AccountAndTransactionModelsPublic.Permissions.ReadDirectDebits); } - if (bank is BarclaysBank.Sandbox) - { - elementsToRemove.Add(AccountAndTransactionModelsPublic.Permissions.ReadParty); - elementsToRemove.Add(AccountAndTransactionModelsPublic.Permissions.ReadDirectDebits); - elementsToRemove.Add( - AccountAndTransactionModelsPublic.Permissions - .ReadStandingOrdersBasic); - elementsToRemove.Add( - AccountAndTransactionModelsPublic.Permissions - .ReadStandingOrdersDetail); - } - foreach (AccountAndTransactionModelsPublic.Permissions element in elementsToRemove) { @@ -154,20 +139,6 @@ or BarclaysBank.BarclaycardCommercialPayments new IdTokenProcessingCustomBehaviour { IdTokenSubClaimType = IdTokenSubClaimType.EndUserId }, AccountAccessConsentPost = new ReadWritePostCustomBehaviour { PostResponseLinksMayOmitId = true }, - AccountAccessConsentAuthGet = bank is BarclaysBank.Sandbox - ? new ConsentAuthGetCustomBehaviour - { - IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdToken = true } - } - : null, - AccountAccessConsentAuthCodeGrantPost = bank is BarclaysBank.Sandbox - ? new AuthCodeGrantPostCustomBehaviour - { - IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdToken = true } - } - : null, AccountAccessConsentRefreshTokenGrantPost = new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, BankRegistrationPost = diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index b6c1b15a8..4b10a8dc6 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -406,34 +406,27 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( DateTimeOffset modified = _timeProvider.GetUtcNow(); if (idToken is not null) { - bool doNotValidateIdToken = - IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, - customBehaviour?.BaseIdTokenProcessingCustomBehaviour); - if (doNotValidateIdToken is false) + string? newExternalApiUserId = await _grantPost.ValidateIdTokenAuthEndpoint( + idToken, + code, + state, + consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour, + jwksUri, + customBehaviour?.JwksGet, + bankTokenIssuerClaim, + externalApiClientId, + externalApiConsentId, + nonce, + supportsSca, + bankProfile.BankProfileEnum, + consent.ExternalApiUserId); + if (newExternalApiUserId != consent.ExternalApiUserId) { - string? newExternalApiUserId = await _grantPost.ValidateIdTokenAuthEndpoint( - idToken, - code, - state, - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, - customBehaviour?.BaseIdTokenProcessingCustomBehaviour, - jwksUri, - customBehaviour?.JwksGet, - bankTokenIssuerClaim, - externalApiClientId, - externalApiConsentId, - nonce, - supportsSca, - bankProfile.BankProfileEnum, - consent.ExternalApiUserId); - if (newExternalApiUserId != consent.ExternalApiUserId) - { - consent.UpdateExternalApiUserId( - newExternalApiUserId, - modified, - modifiedBy); - } + consent.UpdateExternalApiUserId( + newExternalApiUserId, + modified, + modifiedBy); } } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs index 6f566467f..9d2fea6b1 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs @@ -344,27 +344,20 @@ public async Task PostAuthCodeGrantAsync( } // Perform validation - bool doNotValidateIdToken = - IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( - authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, - baseIdTokenProcessingCustomBehaviour); - if (doNotValidateIdToken is false) - { - await ValidateIdTokenTokenEndpoint( - response.IdToken, - response.AccessToken, - authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, - baseIdTokenProcessingCustomBehaviour, - jwksUri, - jwksGetCustomBehaviour, - bankIssuerUrl, - externalApiClientId, - externalApiConsentId, - expectedNonce, - bankProfileForTppReportingMetrics, - supportsSca, - externalApiUserId); - } + await ValidateIdTokenTokenEndpoint( + response.IdToken, + response.AccessToken, + authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, + jwksUri, + jwksGetCustomBehaviour, + bankIssuerUrl, + externalApiClientId, + externalApiConsentId, + expectedNonce, + bankProfileForTppReportingMetrics, + supportsSca, + externalApiUserId); } return response; @@ -458,27 +451,20 @@ public async Task PostRefreshTokenGrantAsync( } // Perform validation - bool doNotValidateIdToken = - IdTokenProcessingCustomBehaviour.GetDoNotValidateIdToken( - refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, - baseIdTokenProcessingCustomBehaviour); - if (doNotValidateIdToken is false) - { - await ValidateIdTokenTokenEndpoint( - response.IdToken, - response.AccessToken, - refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, - baseIdTokenProcessingCustomBehaviour, - jwksUri, - jwksGetCustomBehaviour, - bankIssuerUrl, - externalApiClientId, - externalApiConsentId, - expectedNonce, - bankProfileForTppReportingMetrics, - supportsSca, - externalApiUserId); - } + await ValidateIdTokenTokenEndpoint( + response.IdToken, + response.AccessToken, + refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, + jwksUri, + jwksGetCustomBehaviour, + bankIssuerUrl, + externalApiClientId, + externalApiConsentId, + expectedNonce, + bankProfileForTppReportingMetrics, + supportsSca, + externalApiUserId); } return response; diff --git a/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/BankUiMethods/BarclaysUiMethods.cs b/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/BankUiMethods/BarclaysUiMethods.cs index 705354a7f..63dd8df31 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/BankUiMethods/BarclaysUiMethods.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BrowserInteraction/BankUiMethods/BarclaysUiMethods.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles; -using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.BankGroups; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Models.Repository; using Microsoft.Playwright; @@ -11,31 +10,10 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.BrowserInterac public class BarclaysUiMethods : IBankUiMethods { - private readonly BarclaysBank _barclaysBank; + public BarclaysUiMethods(BankProfileEnum bankProfileEnum) { } - public BarclaysUiMethods(BankProfileEnum bankProfileEnum) - { - _barclaysBank = BankGroup.Barclays.GetBankGroupData() - .GetBank(bankProfileEnum); - } - - public async Task PerformConsentAuthUiInteractions( + public Task PerformConsentAuthUiInteractions( ConsentVariety consentVariety, IPage page, - BankUser bankUser) - { - if (_barclaysBank is BarclaysBank.Sandbox) - { - await page.Locator("#api-type").SelectOptionAsync(new[] { "Account And Transactions" }); - - await page.Locator("#business-unit").SelectOptionAsync(new[] { "Barclays Personal" }); - - await page.Locator("#test-case").SelectOptionAsync(new[] { "ACC200" }); - - await page.GetByRole( - AriaRole.Button, - new PageGetByRoleOptions { NameString = "Run Test Case" }) - .ClickAsync(); - } - } + BankUser bankUser) => Task.CompletedTask; } From a16b474befe0f49e7e20f1778b7d7233ce3d2492 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Fri, 22 May 2026 20:19:39 +0100 Subject: [PATCH 34/49] Use IssClaim for custom behaviour (re-factor not behaviour change) --- .../Generators/NatWestGenerator.cs | 57 ++++++++++++++++--- .../AccountAccessConsentCommon.cs | 6 -- .../AccountAndTransaction/AccountGet.cs | 5 +- .../AccountAndTransaction/BalanceGet.cs | 5 +- .../AccountAndTransaction/DirectDebitGet.cs | 5 +- .../AccountAndTransaction/MonzoPotGet.cs | 5 +- .../AccountAndTransaction/Party2Get.cs | 5 +- .../AccountAndTransaction/PartyGet.cs | 5 +- .../AccountAndTransaction/StandingOrderGet.cs | 5 +- .../AccountAndTransaction/TransactionGet.cs | 5 +- .../Operations/AuthContextUpdate.cs | 8 +-- .../PaymentInitiation/DomesticPayment.cs | 4 +- .../DomesticPaymentConsentOperations.cs | 4 +- .../VariableRecurringPayments/DomesticVrp.cs | 4 +- .../DomesticVrpConsentOperations.cs | 4 +- 15 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs index 44eb96c7f..56618c14e 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/NatWestGenerator.cs @@ -175,15 +175,28 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox { AudClaim = GetAudClaim(bank), IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, AccountAccessConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour { IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, AccountAccessConsentRefreshTokenGrantPost = - new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, + new RefreshTokenGrantPostCustomBehaviour + { + IdTokenMayBeAbsent = true, + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IssClaim = GetAudClaim(bank) } + }, AccountAccessConsentPost = bank is NatWestBank.Coutts ? new ReadWritePostCustomBehaviour { ResponseLinksMayAddSlash = true } : null, @@ -218,28 +231,54 @@ bank is NatWestBank.NatWestSandbox or NatWestBank.RoyalBankOfScotlandSandbox { AudClaim = GetAudClaim(bank), IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, DomesticPaymentConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour { IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, DomesticPaymentConsentRefreshTokenGrantPost = - new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, + new RefreshTokenGrantPostCustomBehaviour + { + IdTokenMayBeAbsent = true, + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IssClaim = GetAudClaim(bank) } + }, DomesticVrpConsentAuthGet = new ConsentAuthGetCustomBehaviour { AudClaim = GetAudClaim(bank), IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, DomesticVrpConsentAuthCodeGrantPost = new AuthCodeGrantPostCustomBehaviour { IdTokenProcessingCustomBehaviour = - new IdTokenProcessingCustomBehaviour { DoNotValidateIdTokenAcrClaim = true } + new IdTokenProcessingCustomBehaviour + { + DoNotValidateIdTokenAcrClaim = true, + IssClaim = GetAudClaim(bank) + } }, DomesticVrpConsentRefreshTokenGrantPost = - new RefreshTokenGrantPostCustomBehaviour { IdTokenMayBeAbsent = true }, + new RefreshTokenGrantPostCustomBehaviour + { + IdTokenMayBeAbsent = true, + IdTokenProcessingCustomBehaviour = + new IdTokenProcessingCustomBehaviour { IssClaim = GetAudClaim(bank) } + }, DomesticPaymentConsent = new DomesticPaymentConsentCustomBehaviour { diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs index 6df50d2b9..9255ebb2b 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs @@ -2,7 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.AccountAndTransaction; @@ -207,9 +206,4 @@ public async Task AddNewRefreshToken( return refreshTokens.FirstOrDefault(); } - - public static string GetBankTokenIssuerClaim(CustomBehaviourClass? customBehaviour, string issuerUrl) => - customBehaviour - ?.AccountAccessConsentAuthGet - ?.AudClaim ?? issuerUrl; } diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs index f8f2b964f..4b44bbfb2 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountGet.cs @@ -88,13 +88,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs index 7b5054450..1af5ea818 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/BalanceGet.cs @@ -88,13 +88,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs index 335e20d13..e88b87598 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/DirectDebitGet.cs @@ -91,13 +91,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs index 00160cc42..711cd246d 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/MonzoPotGet.cs @@ -195,13 +195,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs index 29f176cc6..03e1712d0 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/Party2Get.cs @@ -88,13 +88,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs index e235e8243..5280d0ce7 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/PartyGet.cs @@ -88,13 +88,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs index 650cad5d0..36a50e04f 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/StandingOrderGet.cs @@ -88,13 +88,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs index 51c26a96d..811ddbb56 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/TransactionGet.cs @@ -89,13 +89,10 @@ await _accountAccessConsentCommon.GetAccountAccessConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = AccountAccessConsentCommon.GetBankTokenIssuerClaim( - customBehaviour, - issuerUrl); // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "accounts", bankRegistration, _accountAccessConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index 4b10a8dc6..7f5857c94 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -399,10 +399,6 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( : authContextNonce; // Validate ID token including nonce - string? requestObjectAudClaim = consentAuthGetCustomBehaviour?.AudClaim; - string bankTokenIssuerClaim = - requestObjectAudClaim ?? - issuerUrl; DateTimeOffset modified = _timeProvider.GetUtcNow(); if (idToken is not null) { @@ -414,7 +410,7 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( customBehaviour?.BaseIdTokenProcessingCustomBehaviour, jwksUri, customBehaviour?.JwksGet, - bankTokenIssuerClaim, + issuerUrl, externalApiClientId, externalApiConsentId, nonce, @@ -493,7 +489,7 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( await _grantPost.PostAuthCodeGrantAsync( code, redirectUrl, - bankTokenIssuerClaim, + issuerUrl, externalApiClientId, clientSecret, externalApiConsentId, diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs index 20d21673a..b50d8ed0b 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs @@ -291,12 +291,10 @@ await _domesticPaymentConsentCommon.GetDomesticPaymentConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = domesticPaymentConsentAuthGetCustomBehaviour - ?.AudClaim ?? issuerUrl; // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "payments", bankRegistration, _domesticPaymentConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs index 8fc2a0db9..98ddadb5c 100644 --- a/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPaymentConsentOperations.cs @@ -560,12 +560,10 @@ Task ReadFundsConfirmationAsync (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = domesticPaymentConsentAuthGetCustomBehaviour - ?.AudClaim ?? issuerUrl; // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "payments", bankRegistration, _domesticPaymentConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs index 51e41c5d6..4f81f248d 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrp.cs @@ -141,12 +141,10 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = domesticVrpConsentAuthGetCustomBehaviour - ?.AudClaim ?? issuerUrl; // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "payments", bankRegistration, _domesticVrpConsentCommon.GetAccessToken, diff --git a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs index de9fbebe0..697e9a54e 100644 --- a/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/VariableRecurringPayments/DomesticVrpConsentOperations.cs @@ -148,12 +148,10 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( (await _obSealCertificateMethods.GetValue(softwareStatement.DefaultObSealCertificateId)).ObSealKey; // Get access token - string bankTokenIssuerClaim = domesticVrpConsentAuthGetCustomBehaviour - ?.AudClaim ?? issuerUrl; // Get bank token issuer ("iss") claim string accessToken = await _consentAccessTokenGet.GetAccessTokenAndUpdateConsent( persistedConsent, - bankTokenIssuerClaim, + issuerUrl, "payments", bankRegistration, _domesticVrpConsentCommon.GetAccessToken, From ba21c3129faca9d0afded1a7d3528ff484d3987f Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 23 May 2026 15:16:56 +0100 Subject: [PATCH 35/49] Add ProblemDetails responses for ID token validation errors --- .../Fluent/HttpResponseException.cs | 3 +- .../Operations/AuthContextUpdate.cs | 3 +- .../Operations/ExternalApi/GrantPost.cs | 91 ++++++++++++------- .../ExternalApi/IdTokenValidationError.cs | 79 ++++++++++++++++ 4 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/Operations/ExternalApi/IdTokenValidationError.cs diff --git a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs index c0c350641..57120b773 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -11,7 +11,8 @@ public enum ServerErrorType AuthContextNotFound, AuthContextStale, ConsentNotFound, - ConsentFromRequestNotFound + ConsentFromRequestNotFound, + IdTokenValidationError } public abstract record ServerError diff --git a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs index 7f5857c94..c203e18a6 100644 --- a/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs +++ b/src/OpenBanking.Library.Connector/Operations/AuthContextUpdate.cs @@ -171,8 +171,7 @@ public async // Validate error parameter if (request.OAuth2RedirectOptionalParameters.Error is not null) { - throw new InvalidOperationException( - $"OAuth2 error parameter received: {request.OAuth2RedirectOptionalParameters.Error}"); + throw new InvalidOperationException("OAuth2 error parameter received."); } // Validate code parameter diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs index 9d2fea6b1..16a19c918 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs @@ -9,6 +9,7 @@ using FinnovationLabs.OpenBanking.Library.BankApiModels.Json; using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles; using FinnovationLabs.OpenBanking.Library.Connector.BankProfiles.CustomBehaviour; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Metrics; @@ -88,6 +89,11 @@ public GrantPost( baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + if (string.IsNullOrEmpty(idTokenObject.Subject)) + { + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.SubjectEmpty)); + } IdTokenSubClaimType idTokenSubClaimType = IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( idTokenProcessingCustomBehaviour, @@ -98,18 +104,16 @@ public GrantPost( case IdTokenSubClaimType.EndUserId: if (externalApiUserId is null) { - if (string.IsNullOrEmpty(idTokenObject.Subject)) - { - throw new Exception("Subject from ID token is null or empty."); - } - outputExternalApiUserId = idTokenObject.Subject; } else { if (!string.Equals(idTokenObject.Subject, externalApiUserId)) { - throw new Exception("Subject from ID token does not match user ID."); + throw new HttpResponseException( + new IdTokenValidationError( + idTokenObject, + IdTokenValidationErrorType.SubjectEndUserIdMismatch)); } } @@ -117,14 +121,16 @@ public GrantPost( case IdTokenSubClaimType.ConsentId: if (!string.Equals(idTokenObject.Subject, externalApiConsentId)) { - throw new Exception("Subject from ID token does not match consent ID."); + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.SubjectConsentIdMismatch)); } break; case IdTokenSubClaimType.ClientId: if (!string.Equals(idTokenObject.Subject, externalApiClientId)) { - throw new Exception("Subject from ID token does not match client ID."); + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.SubjectClientIdMismatch)); } break; @@ -132,16 +138,20 @@ public GrantPost( throw new ArgumentOutOfRangeException(nameof(idTokenSubClaimType), idTokenSubClaimType, null); } + // Validate ID token code hash claim string codeHash = ComputeHash(code); if (!string.Equals(idTokenObject.CodeHash, codeHash)) { - throw new Exception("Code hash from ID token does not match code hash."); + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.CodeHashMismatch)); } + // Validate ID token state hash claim string stateHash = ComputeHash(state); if (!string.Equals(idTokenObject.StateHash, stateHash)) { - throw new Exception("State hash from ID token does not match state hash."); + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.StateHashMismatch)); } return outputExternalApiUserId; @@ -540,7 +550,8 @@ private void ValidateIdTokenCommon( { if (idToken.Exp < DateTimeOffset.UtcNow) { - throw new Exception("ID token has expired."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.IdTokenExpired)); } bool idTokenMayNotHaveConsentIdClaim = @@ -550,12 +561,14 @@ private void ValidateIdTokenCommon( if (!idTokenMayNotHaveConsentIdClaim && idToken.ConsentId is null) { - throw new Exception("Consent ID not provided in ID token."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.ConsentIdMissing)); } if (idToken.ConsentId is not null && !string.Equals(idToken.ConsentId, externalApiConsentId)) { - throw new Exception("Consent ID from ID token does not match expected consent ID."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.ConsentIdMismatch)); } bool idTokenMayNotHaveNonceClaim = @@ -565,13 +578,15 @@ private void ValidateIdTokenCommon( if (!idTokenMayNotHaveNonceClaim && idToken.Nonce is null) { - throw new Exception("Nonce not provided in ID token."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.NonceMissing)); } if (idToken.Nonce is not null && !string.Equals(idToken.Nonce, expectedNonce)) { - throw new Exception("Nonce from ID token does not match expected nonce."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.NonceMismatch)); } bool idTokenMayNotHaveAuthTimeClaim = @@ -581,7 +596,8 @@ private void ValidateIdTokenCommon( if (!idTokenMayNotHaveAuthTimeClaim && idToken.AuthTime is null) { - throw new Exception("Auth time not provided in ID token."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.AuthTimeMissing)); } bool idTokenMayNotHaveAcrClaim = @@ -591,7 +607,7 @@ private void ValidateIdTokenCommon( if (!idTokenMayNotHaveAcrClaim && idToken.Acr is null) { - throw new Exception("Acr not provided in ID token."); + throw new HttpResponseException(new IdTokenValidationError(idToken, IdTokenValidationErrorType.AcrMissing)); } bool doNotValidateIdTokenAcrClaim = @@ -601,17 +617,11 @@ private void ValidateIdTokenCommon( if (idToken.Acr is not null) { if (supportsSca && - idToken.Acr is not Acr.Sca) + idToken.Acr is not Acr.Sca && + !doNotValidateIdTokenAcrClaim) { - var message = $"Acr from ID token is {idToken.Acr} rather than expected {Acr.Sca}."; - if (doNotValidateIdTokenAcrClaim) - { - _instrumentationClient.Info(message); - } - else - { - throw new Exception(message); - } + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.AcrMismatch)); } } @@ -620,12 +630,14 @@ private void ValidateIdTokenCommon( baseIdTokenProcessingCustomBehaviour) ?? bankIssuerUrl; if (!string.Equals(idToken.Issuer, issClaim)) { - throw new Exception("Issuer from ID token does not match expected issuer."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.IssuerMismatch)); } if (!string.Equals(idToken.Audience, externalApiClientId)) { - throw new Exception("Audience from ID token does not match expected audience."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.AudienceMismatch)); } } @@ -701,6 +713,11 @@ private async Task ValidateIdTokenTokenEndpoint( baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + if (string.IsNullOrEmpty(idToken.Subject)) + { + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectEmpty)); + } IdTokenSubClaimType idTokenSubClaimType = IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( idTokenProcessingCustomBehaviour, @@ -710,26 +727,30 @@ private async Task ValidateIdTokenTokenEndpoint( case IdTokenSubClaimType.EndUserId: if (externalApiUserId is null) { - throw new Exception("No user ID available to use in ID token validation."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectEndUserIdNotFound)); } if (!string.Equals(idToken.Subject, externalApiUserId)) { - throw new Exception("Subject from ID token does not match user ID."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectEndUserIdMismatch)); } break; case IdTokenSubClaimType.ConsentId: if (!string.Equals(idToken.Subject, externalApiConsentId)) { - throw new Exception("Subject from ID token does not match consent ID."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectConsentIdMismatch)); } break; case IdTokenSubClaimType.ClientId: if (!string.Equals(idToken.Subject, externalApiClientId)) { - throw new Exception("Subject from ID token does not match client ID."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectClientIdMismatch)); } break; @@ -737,12 +758,14 @@ private async Task ValidateIdTokenTokenEndpoint( throw new ArgumentOutOfRangeException(nameof(idTokenSubClaimType), idTokenSubClaimType, null); } + // Validate ID token access token hash claim if (idToken.AccessTokenHash is not null) { string accessTokenHash = ComputeHash(accessToken); if (!string.Equals(idToken.AccessTokenHash, accessTokenHash)) { - throw new Exception("Access token hash from ID token does not match access token."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.AccessTokenHashMismatch)); } } } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/IdTokenValidationError.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/IdTokenValidationError.cs new file mode 100644 index 000000000..3b65e15ec --- /dev/null +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/IdTokenValidationError.cs @@ -0,0 +1,79 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.Connector.Extensions; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; +using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; + +namespace FinnovationLabs.OpenBanking.Library.Connector.Operations.ExternalApi; + +internal enum IdTokenValidationErrorType +{ + IdTokenExpired, + ConsentIdMissing, + ConsentIdMismatch, + NonceMissing, + NonceMismatch, + AuthTimeMissing, + AcrMissing, + AcrMismatch, + IssuerMismatch, + AudienceMismatch, + SubjectEmpty, + SubjectEndUserIdNotFound, + SubjectEndUserIdMismatch, + SubjectConsentIdMismatch, + SubjectClientIdMismatch, + CodeHashMismatch, + StateHashMismatch, + AccessTokenHashMismatch +} + +internal record IdTokenValidationError( + IdTokenBase IdToken, + IdTokenValidationErrorType IdTokenValidationErrorType) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.IdTokenValidationError; + public override string Title => "ID token validation error"; + + public override string Detail => IdTokenValidationErrorType switch + { + IdTokenValidationErrorType.IdTokenExpired => $"ID token has expired (exp: {IdToken.Exp:u}).", + IdTokenValidationErrorType.ConsentIdMissing => "Consent ID not provided in ID token.", + IdTokenValidationErrorType.ConsentIdMismatch => "Consent ID from ID token does not match expected value.", + IdTokenValidationErrorType.NonceMissing => "Nonce not provided in ID token.", + IdTokenValidationErrorType.NonceMismatch => "Nonce from ID token does not match expected value.", + IdTokenValidationErrorType.AuthTimeMissing => "Auth time not provided in ID token.", + IdTokenValidationErrorType.AcrMissing => "ACR not provided in ID token.", + IdTokenValidationErrorType.AcrMismatch => "ACR from ID token does not match expected value.", + IdTokenValidationErrorType.IssuerMismatch => "Issuer from ID token does not match expected value.", + IdTokenValidationErrorType.AudienceMismatch => "Audience from ID token does not match expected value.", + IdTokenValidationErrorType.SubjectEmpty => "Subject from ID token empty.", + IdTokenValidationErrorType.SubjectEndUserIdNotFound => "No end user ID found for use in ID token validation.", + IdTokenValidationErrorType.SubjectEndUserIdMismatch => + "Subject from ID token does not match expected end user ID.", + IdTokenValidationErrorType.SubjectConsentIdMismatch => + "Subject from ID token does not match expected consent ID.", + IdTokenValidationErrorType.SubjectClientIdMismatch => + "Subject from ID token does not match expected client ID.", + IdTokenValidationErrorType.CodeHashMismatch => "Code hash from ID token does not match code.", + IdTokenValidationErrorType.StateHashMismatch => "State hash from ID token does not match state.", + IdTokenValidationErrorType.AccessTokenHashMismatch => + "Access token hash from ID token does not match access token.", + _ => throw new ArgumentOutOfRangeException() + }; + + public override int StatusCode => 502; + + public override IReadOnlyDictionary Extensions { get; } = + new Dictionary + { + ["iss"] = IdToken.Issuer, + ["iat"] = IdToken.Iat, + ["exp"] = IdToken.Exp, + ["acr"] = IdToken.Acr?.ToString().ToCamelCase(), + ["authTime"] = IdToken.AuthTime, + ["idTokenValidationErrorType"] = IdTokenValidationErrorType.ToString().ToCamelCase() + }; +} From a881737c76cd0fb6bf641ce88fc975d0354be111 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 23 May 2026 16:20:51 +0100 Subject: [PATCH 36/49] Add ProblemDetails responses for external API HTTP request errors --- .../Fluent/HttpResponseException.cs | 4 +- .../Http/ApiClient.cs | 177 +++++++++++------- .../Http/ExternalApiHttpRequestErrors.cs | 45 +++++ 3 files changed, 153 insertions(+), 73 deletions(-) create mode 100644 src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs diff --git a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs index 57120b773..4b96e3aba 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -12,7 +12,9 @@ public enum ServerErrorType AuthContextStale, ConsentNotFound, ConsentFromRequestNotFound, - IdTokenValidationError + IdTokenValidationError, + ExternalApiHttpRequestIoError, + ExternalApiHttpRequestTimeout } public abstract record ServerError diff --git a/src/OpenBanking.Library.Connector/Http/ApiClient.cs b/src/OpenBanking.Library.Connector/Http/ApiClient.cs index c50e4a22b..753dabcdc 100644 --- a/src/OpenBanking.Library.Connector/Http/ApiClient.cs +++ b/src/OpenBanking.Library.Connector/Http/ApiClient.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Metrics; using Microsoft.Extensions.Http.Logging; @@ -91,8 +92,8 @@ public ApiClient(IInstrumentationClient instrumentationClient, HttpClient unwrap { throw new ExternalApiResponseDeserialisationException( statusCode, - $"{request.Method}", - $"{request.RequestUri}", + request.Method.ToString(), + request.RequestUri!.ToString(), responseBody, xFapiInteractionId, ex.Message); @@ -113,7 +114,7 @@ public async Task SendExpectingNoResponseAsync( { ArgumentNullException.ThrowIfNull(request); - (int statusCode, string? responseBody, string? xFapiInteractionId) = + (_, string? responseBody, _) = await SendInnerAsync(request, requestContentForLog, tppReportingRequestInfo); // Check body null @@ -130,7 +131,7 @@ public async Task SendExpectingStringResponseAsync( { ArgumentNullException.ThrowIfNull(request); - (int statusCode, string? responseBody, string? xFapiInteractionId) = + (_, string? responseBody, _) = await SendInnerAsync(request, requestContentForLog, tppReportingRequestInfo); // Check body not null @@ -215,13 +216,12 @@ private static SocketsHttpHandler CreatePrimaryHandler( { ArgumentNullException.ThrowIfNull(request); - // Make request and retrieve response body. - // Since logging requires request and response body, done at this level rather than in wrapping DelegatingHandler as previously intended. + // Since logging requires request and response body, done at this level rather than in wrapping DelegatingHandler as previously intended. _httpRequestLogger.LogRequestStart(request); + var requestMethod = request.Method.ToString(); + var requestUri = request.RequestUri!.ToString(); HttpResponseMessage? response = null; - string? responseBody = null; - var stopWatch = new Stopwatch(); - stopWatch.Start(); + var stopWatch = Stopwatch.StartNew(); try { // Make request @@ -229,83 +229,87 @@ private static SocketsHttpHandler CreatePrimaryHandler( request, HttpCompletionOption.ResponseContentRead, CancellationToken.None); + } + catch (Exception ex) + { stopWatch.Stop(); - // Get request and response bodies - responseBody = await response.Content.ReadAsStringAsync(); - - // Log request - _httpRequestLogger.LogRequestStop( - new HttpRequestLoggerAdditionalData - { - RequestBody = requestContentForLog, - ResponseBody = responseBody - }, + LogAndUpdateMetricsForFailure( request, - response, + requestContentForLog, + tppReportingRequestInfo, + null, + ex, stopWatch.Elapsed); - } - catch (Exception ex) - { - stopWatch.Stop(); // no-op if not required - // Update TPP reporting metric - if (tppReportingRequestInfo is not null) // removed ex is TaskCanceledException { InnerException: TimeoutException }) + if (ex is HttpIOException httpIoException) { - if (response is null) - { - tppReportingMetrics?.RequestNoResponseCount.Add( - 1, - new KeyValuePair("bank_profile", tppReportingRequestInfo.BankProfile), - new KeyValuePair( - "external_api_endpoint", - tppReportingRequestInfo.EndpointDescription)); - } - else - { - UpdateTppReportingMetrics(tppReportingRequestInfo, (int) response.StatusCode); - } + throw new HttpResponseException( + new ExternalApiHttpRequestIoError(requestMethod, requestUri, httpIoException.HttpRequestError)); } - // Log request - _httpRequestLogger.LogRequestFailed( - new HttpRequestLoggerAdditionalData - { - RequestBody = requestContentForLog, - ResponseBody = responseBody - }, + if (ex is TaskCanceledException { InnerException: TimeoutException }) + { + throw new HttpResponseException(new ExternalApiHttpRequestTimeout(requestMethod, requestUri)); + } + + throw; + } + + stopWatch.Stop(); + + // Process response + string? xFapiInteractionId = null; + if (response.Headers.TryGetValues("x-fapi-interaction-id", out IEnumerable? values)) + { + xFapiInteractionId = values.First(); + } + var statusCode = (int) response.StatusCode; + bool isSuccess = response.IsSuccessStatusCode; + string? responseBody; + try + { + // Read response body (buffered in memory by ResponseContentRead above) + responseBody = await response.Content.ReadAsStringAsync(); + } + catch (Exception ex) + { + LogAndUpdateMetricsForFailure( request, + requestContentForLog, + tppReportingRequestInfo, response, ex, stopWatch.Elapsed); + response.Dispose(); throw; } - finally - { - response?.Dispose(); - } + + // Log request + _httpRequestLogger.LogRequestStop( + new HttpRequestLoggerAdditionalData + { + RequestBody = requestContentForLog, + ResponseBody = responseBody + }, + request, + response, + stopWatch.Elapsed); // Update TPP reporting metric if (tppReportingRequestInfo is not null) { - UpdateTppReportingMetrics(tppReportingRequestInfo, (int) response.StatusCode); + UpdateTppReportingMetrics(tppReportingRequestInfo, statusCode); } - // Get selected response headers - string? xFapiInteractionId = null; - if (response.Headers.TryGetValues("x-fapi-interaction-id", out IEnumerable? values)) - { - xFapiInteractionId = values.First(); - } + response.Dispose(); - // Check HTTP status code - var statusCode = (int) response.StatusCode; - if (!response.IsSuccessStatusCode) + if (!isSuccess) { throw new ExternalApiHttpErrorException( statusCode, - $"{request.Method}", - $"{request.RequestUri}", + requestMethod, + requestUri, responseBody, xFapiInteractionId); } @@ -313,6 +317,45 @@ private static SocketsHttpHandler CreatePrimaryHandler( return (statusCode, responseBody, xFapiInteractionId); } + private void LogAndUpdateMetricsForFailure( + HttpRequestMessage request, + string? requestContentForLog, + TppReportingRequestInfo? tppReportingRequestInfo, + HttpResponseMessage? response, + Exception ex, + TimeSpan elapsed) + { + // Update TPP reporting metric + if (tppReportingRequestInfo is not null) + { + if (response is null) + { + tppReportingMetrics?.RequestNoResponseCount.Add( + 1, + new KeyValuePair("bank_profile", tppReportingRequestInfo.BankProfile), + new KeyValuePair( + "external_api_endpoint", + tppReportingRequestInfo.EndpointDescription)); + } + else + { + UpdateTppReportingMetrics(tppReportingRequestInfo, (int) response.StatusCode); + } + } + + // Log request + _httpRequestLogger.LogRequestFailed( + new HttpRequestLoggerAdditionalData + { + RequestBody = requestContentForLog, + ResponseBody = null + }, + request, + response, + ex, + elapsed); + } + private void UpdateTppReportingMetrics( TppReportingRequestInfo tppReportingRequestInfo, int statusCodeInt) @@ -354,14 +397,4 @@ public async Task LowLevelSendAsync(HttpRequestMessage requ throw; } } - - private static async Task GetStringRequestBodyAsync(HttpRequestMessage request) - { - using HttpContent? content = request.Content; - if (content is null) - { - return null; - } - return await content.ReadAsStringAsync(); - } } diff --git a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs new file mode 100644 index 000000000..352039b8e --- /dev/null +++ b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs @@ -0,0 +1,45 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using FinnovationLabs.OpenBanking.Library.Connector.Extensions; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; + +namespace FinnovationLabs.OpenBanking.Library.Connector.Http; + +internal record ExternalApiHttpRequestIoError( + string RequestHttpMethod, + string RequestUrl, + HttpRequestError HttpRequestError) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.ExternalApiHttpRequestIoError; + public override string Title => "External API HTTP request I/O error"; + + public override string Detail => + $"An HTTP request to an external API endpoint failed with I/O error of type '{HttpRequestError.ToString().ToCamelCase()}'."; + + public override int StatusCode => 502; + + public override IReadOnlyDictionary Extensions { get; } = new Dictionary + { + ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), + ["requestHttpMethod"] = RequestHttpMethod, + ["httpRequestError"] = HttpRequestError.ToString().ToCamelCase() + }; +} + +internal record ExternalApiHttpRequestTimeout( + string RequestHttpMethod, + string RequestUrl) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.ExternalApiHttpRequestTimeout; + public override string Title => "External API HTTP request time-out"; + public override string Detail => "An HTTP request to an external API endpoint timed out."; + public override int StatusCode => 504; + + public override IReadOnlyDictionary Extensions { get; } = new Dictionary + { + ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), + ["requestHttpMethod"] = RequestHttpMethod + }; +} From acbd124683f983a0559aae6bff442b0d697053e5 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 23 May 2026 20:33:14 +0100 Subject: [PATCH 37/49] Move content out of Web project --- .../Controllers/ErrorController.cs | 18 ------- .../Extensions/FluentResponseExtensions.cs | 49 ------------------- .../Models/Public/Response/HttpResponse.cs | 44 ----------------- .../Public/Response/HttpResponseMessages.cs | 31 ------------ .../OpenBanking.Library.Connector.Web.csproj | 7 --- .../ExternalApiHttpErrorExceptionFilter.cs | 4 +- .../Filters}/GlobalExceptionFilter.cs | 4 +- src/OpenBanking.WebApp.Connector/Program.cs | 21 +------- .../wwwroot/index.html | 3 +- .../BankTests/AppContextFixture.cs | 2 +- .../TestingAccountAccessConsentsController.cs | 4 +- .../AuthContext/TestingRedirectsController.cs | 6 +-- ...estingDomesticPaymentConsentsController.cs | 4 +- .../TestingDomesticVrpConsentsController.cs | 4 +- .../ApplicationBuilderExtensions.cs | 8 +-- .../AccountAccessConsentSubtest.cs | 1 - .../DomesticPaymentConsentSubtest.cs | 1 - .../DomesticVrpConsentSubtest.cs | 1 - ...Banking.Library.Connector.BankTests.csproj | 6 +++ .../RedirectObservable.cs | 1 - .../RedirectObserver.cs | 1 - .../TestingAuthResult.cs | 4 +- .../TestingMethods.cs | 5 +- .../wwwroot/auth/fragment-redirect.html | 2 +- .../wwwroot/auth/js/main.js | 0 25 files changed, 31 insertions(+), 200 deletions(-) delete mode 100644 src/OpenBanking.Library.Connector.Web/Extensions/FluentResponseExtensions.cs delete mode 100644 src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponse.cs delete mode 100644 src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponseMessages.cs rename src/{OpenBanking.Library.Connector.Web => OpenBanking.WebApp.Connector/Filters}/ExternalApiHttpErrorExceptionFilter.cs (95%) rename src/{OpenBanking.Library.Connector.Web => OpenBanking.WebApp.Connector/Filters}/GlobalExceptionFilter.cs (91%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs (94%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/Controllers/AuthContext/TestingRedirectsController.cs (96%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs (94%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs (94%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/Extensions/ApplicationBuilderExtensions.cs (79%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/TestingAuthResult.cs (70%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/TestingMethods.cs (94%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/wwwroot/auth/fragment-redirect.html (98%) rename {src/OpenBanking.Library.Connector.Web => test/OpenBanking.Library.Connector.BankTests}/wwwroot/auth/js/main.js (100%) diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/ErrorController.cs b/src/OpenBanking.Library.Connector.Web/Controllers/ErrorController.cs index 5ad713841..9cdc9e32c 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/ErrorController.cs +++ b/src/OpenBanking.Library.Connector.Web/Controllers/ErrorController.cs @@ -12,24 +12,6 @@ public class ErrorController : ControllerBase { [Route("/error")] public IActionResult HandleError() => - // Exception exception = - // HttpContext.Features.Get()!.Error; - - // Catch unhandled input validation errors - // if (exception is ArgumentNullException) - // { - // var problemDetails = new ProblemDetails - // { - // Detail = exception.Message, - // Status = StatusCodes.Status400BadRequest, - // Title = "An input argument was null." - // }; - // - // return new ObjectResult(problemDetails) - // { - // StatusCode = StatusCodes.Status400BadRequest - // }; - // } // Default to returning no details Problem(); diff --git a/src/OpenBanking.Library.Connector.Web/Extensions/FluentResponseExtensions.cs b/src/OpenBanking.Library.Connector.Web/Extensions/FluentResponseExtensions.cs deleted file mode 100644 index 064b98784..000000000 --- a/src/OpenBanking.Library.Connector.Web/Extensions/FluentResponseExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. -// Finnovation Labs Limited licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.Web.Models.Public.Response; - -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions; - -public static class FluentResponseExtensions -{ - public static HttpResponse ToHttpResponse(this IFluentResponse value) => - new(value.GetHttpResponseMessages()); - - public static HttpResponse ToHttpResponse(this IFluentResponse value) - where TData : class => new(value.GetHttpResponseMessages(), value.Data); - - public static HttpResponseMessages? GetHttpResponseMessages(this IFluentResponse value) - { - HttpResponseMessages? messages = null; - if (value.Messages.Any()) - { - messages = new HttpResponseMessages(); - - List infos = value.Messages.OfType().Select(m => m.Message) - .ToList(); - if (infos.Count > 0) - { - messages.InformationMessages = infos; - } - - List warnings = value.Messages.OfType().Select(m => m.Message) - .ToList(); - if (warnings.Count > 0) - { - messages.WarningMessages = warnings; - } - - List errors = value.Messages.OfType().Select(m => m.Message) - .ToList(); - if (errors.Count > 0) - { - messages.ErrorMessages = errors; - } - } - - return messages; - } -} diff --git a/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponse.cs b/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponse.cs deleted file mode 100644 index 6f1358bbd..000000000 --- a/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponse.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. -// Finnovation Labs Limited licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Models.Public.Response; - -/// -/// HTTP response for request that does not return data -/// -public class HttpResponse -{ - public HttpResponse(HttpResponseMessages? messages) - { - Messages = messages; - } - - /// - /// Messages from Open Banking Connector. - /// - public HttpResponseMessages? Messages { get; } -} - -/// -/// HTTP response for request that returns data -/// -public class HttpResponse - where TData : class -{ - public HttpResponse(HttpResponseMessages? messages, TData? data) - { - Messages = messages; - Data = data; - } - - /// - /// Messages from Open Banking Connector. - /// - public HttpResponseMessages? Messages { get; } - - /// - /// Data from Open Banking Connector. - /// - public TData? Data { get; } -} diff --git a/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponseMessages.cs b/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponseMessages.cs deleted file mode 100644 index eb22f5c77..000000000 --- a/src/OpenBanking.Library.Connector.Web/Models/Public/Response/HttpResponseMessages.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. -// Finnovation Labs Limited licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; - -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Models.Public.Response; - -/// -/// Messages from Open Banking Connector. -/// -public class HttpResponseMessages -{ - /// - /// Information messages. - /// - [JsonProperty("Info", DefaultValueHandling = DefaultValueHandling.Ignore)] - public IList? InformationMessages { get; set; } - - /// - /// Warning messages. - /// - [JsonProperty("Warnings", DefaultValueHandling = DefaultValueHandling.Ignore)] - public IList? WarningMessages { get; set; } - - /// - /// Error messages. - /// - [JsonProperty("Errors", DefaultValueHandling = DefaultValueHandling.Ignore)] - public IList? ErrorMessages { get; set; } -} diff --git a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj index b7572cd1e..d0171c1b6 100644 --- a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj +++ b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj @@ -4,7 +4,6 @@ net9.0 enable enable - true FinnovationLabs.OpenBanking.Library.Connector.Web FinnovationLabs.OpenBanking.Library.Connector.Web Finnovation Labs Limited and Contributors @@ -25,8 +24,6 @@ - - @@ -39,10 +36,6 @@ - - - - true $(NoWarn);1591 diff --git a/src/OpenBanking.Library.Connector.Web/ExternalApiHttpErrorExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs similarity index 95% rename from src/OpenBanking.Library.Connector.Web/ExternalApiHttpErrorExceptionFilter.cs rename to src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs index 0e287198a..bcc0c7338 100644 --- a/src/OpenBanking.Library.Connector.Web/ExternalApiHttpErrorExceptionFilter.cs +++ b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Net.Http.Headers; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web; +namespace FinnovationLabs.OpenBanking.WebApp.Connector.Filters; public class ExternalApiHttpErrorExceptionFilter : IActionFilter, IOrderedFilter { diff --git a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs similarity index 91% rename from src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs rename to src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs index 846a3a8d5..5895d6a7f 100644 --- a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs +++ b/src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web; +namespace FinnovationLabs.OpenBanking.WebApp.Connector.Filters; public class GlobalExceptionFilter(ProblemDetailsFactory problemDetailsFactory) : IExceptionFilter { diff --git a/src/OpenBanking.WebApp.Connector/Program.cs b/src/OpenBanking.WebApp.Connector/Program.cs index 432f1d6db..355b43cdb 100644 --- a/src/OpenBanking.WebApp.Connector/Program.cs +++ b/src/OpenBanking.WebApp.Connector/Program.cs @@ -5,16 +5,14 @@ using System.Reflection; using FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; -using FinnovationLabs.OpenBanking.Library.Connector.Web; using FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions; using FinnovationLabs.OpenBanking.WebApp.Connector.Extensions; +using FinnovationLabs.OpenBanking.WebApp.Connector.Filters; using Microsoft.OpenApi; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using AccountAndTransactionModelsPublic = FinnovationLabs.OpenBanking.Library.BankApiModels.UkObRw.V4p0.NSwagAisp.Models; -using ServiceCollectionExtensionsWeb = - FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions.ServiceCollectionExtensions; using ServiceCollectionExtensionsGenericHost = FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions.ServiceCollectionExtensions; @@ -80,15 +78,6 @@ Version = serviceVersion, Description = "Auth Contexts API for Open Banking Connector Web App" }); - options.SwaggerDoc( - "test", - new OpenApiInfo - { - Title = "Testing (non-production) API", - Version = serviceVersion, - Description = - "Testing API for Open Banking Connector Web App. Endpoints should not be used in production." - }); // Add XML from this assembly var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; @@ -102,10 +91,6 @@ // Add XML from OpenBankingLibrary.GenericHost xmlFilename = $"{typeof(ServiceCollectionExtensionsGenericHost).GetTypeInfo().Assembly.GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); - // Add XML from OpenBankingLibrary.Web - xmlFilename = - $"{typeof(ServiceCollectionExtensionsWeb).GetTypeInfo().Assembly.GetName().Name}.xml"; - options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); }) .AddSwaggerGenNewtonsoftSupport() // Add memory cache @@ -150,9 +135,6 @@ // Add local static files app.UseStaticFiles(); -// Add web host static files -app.UseWebHostStaticFiles(); - // Add Swagger generation app.UseSwagger(); app.UseSwaggerUI( @@ -163,7 +145,6 @@ c.SwaggerEndpoint("/swagger/pisp/swagger.json", "Payment Initiation API"); c.SwaggerEndpoint("/swagger/vrp/swagger.json", "Variable Recurring Payments API"); c.SwaggerEndpoint("/swagger/auth-contexts/swagger.json", "Auth Contexts API"); - c.SwaggerEndpoint("/swagger/test/swagger.json", "Testing (non-production) API"); }); // Add controller endpoints diff --git a/src/OpenBanking.WebApp.Connector/wwwroot/index.html b/src/OpenBanking.WebApp.Connector/wwwroot/index.html index 6585bd7ac..0aabad6e8 100644 --- a/src/OpenBanking.WebApp.Connector/wwwroot/index.html +++ b/src/OpenBanking.WebApp.Connector/wwwroot/index.html @@ -34,8 +34,7 @@
Open Banking Connector
Open-source Open Banking connectivity

Contents

- OpenAPI documentation
- Testing (non-production): Fragment redirect page + OpenAPI documentation

diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs index 0ea2c5c5e..97bd96aa1 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs @@ -74,7 +74,7 @@ public AppContextFixture() app.UseHsts(); } - // Add web host static files + // Add static files app.UseWebHostStaticFiles(); // Add controller endpoints diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs similarity index 94% rename from src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs index dcbe92b57..87c8eff8b 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.AccountAndTransaction; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AccountAndTransaction; [ApiController] [ApiExplorerSettings(GroupName = "test")] diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs similarity index 96% rename from src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs index 4af539ecd..044e92de3 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs @@ -1,17 +1,17 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.ComponentModel.DataAnnotations; +using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.AccountAndTransaction; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.AuthContext; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AuthContext; [ApiController] [ApiExplorerSettings(GroupName = "test")] diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs similarity index 94% rename from src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs index b352f3031..1692fd78d 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.PaymentInitiation; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.PaymentInitiation; [ApiController] [ApiExplorerSettings(GroupName = "test")] diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs similarity index 94% rename from src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs index 1786c6cd7..9516cf760 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.VariableRecurringPayments; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.VariableRecurringPayments; [ApiController] [ApiExplorerSettings(GroupName = "test")] diff --git a/src/OpenBanking.Library.Connector.Web/Extensions/ApplicationBuilderExtensions.cs b/test/OpenBanking.Library.Connector.BankTests/Extensions/ApplicationBuilderExtensions.cs similarity index 79% rename from src/OpenBanking.Library.Connector.Web/Extensions/ApplicationBuilderExtensions.cs rename to test/OpenBanking.Library.Connector.BankTests/Extensions/ApplicationBuilderExtensions.cs index 464037ec8..5ed7a197f 100644 --- a/src/OpenBanking.Library.Connector.Web/Extensions/ApplicationBuilderExtensions.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Extensions/ApplicationBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.FileProviders; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Extensions; public static class ApplicationBuilderExtensions { @@ -25,12 +25,12 @@ public static IApplicationBuilder UseWebHostStaticFiles(this IApplicationBuilder app.UseRewriter(rewriterOptions); // Add shared static files from this assembly - var webHostAssembly = Assembly.GetAssembly(typeof(ApplicationBuilderExtensions))!; + var bankTestsAssembly = Assembly.GetAssembly(typeof(ApplicationBuilderExtensions))!; app.UseStaticFiles( new StaticFileOptions { FileProvider = new ManifestEmbeddedFileProvider( - webHostAssembly, + bankTestsAssembly, "wwwroot") }); diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs index 79368825c..385c07242 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs @@ -18,7 +18,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; -using FinnovationLabs.OpenBanking.Library.Connector.Web; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using AccountAccessConsentAuthContext = diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs index 1a2cfb0ab..c3e6c23de 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs @@ -13,7 +13,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; -using FinnovationLabs.OpenBanking.Library.Connector.Web; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.FunctionalSubtests.PaymentInitiation. DomesticPaymentConsent; diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs index 2c69e191c..b19fdc8ef 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs @@ -17,7 +17,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; using FinnovationLabs.OpenBanking.Library.Connector.Operations; using FinnovationLabs.OpenBanking.Library.Connector.Persistence; -using FinnovationLabs.OpenBanking.Library.Connector.Web; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using DomesticVrpConsentAuthContext = diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 210836034..5d708e2fb 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -10,6 +10,7 @@ Finnovation Labs Limited Finnovation Labs Limited false + true
@@ -18,9 +19,14 @@ + + + + + diff --git a/test/OpenBanking.Library.Connector.BankTests/RedirectObservable.cs b/test/OpenBanking.Library.Connector.BankTests/RedirectObservable.cs index 74d0883c3..aa9ff2341 100644 --- a/test/OpenBanking.Library.Connector.BankTests/RedirectObservable.cs +++ b/test/OpenBanking.Library.Connector.BankTests/RedirectObservable.cs @@ -8,7 +8,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Web; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests; diff --git a/test/OpenBanking.Library.Connector.BankTests/RedirectObserver.cs b/test/OpenBanking.Library.Connector.BankTests/RedirectObserver.cs index 8540c7483..0553c5bb2 100644 --- a/test/OpenBanking.Library.Connector.BankTests/RedirectObserver.cs +++ b/test/OpenBanking.Library.Connector.BankTests/RedirectObserver.cs @@ -8,7 +8,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; -using FinnovationLabs.OpenBanking.Library.Connector.Web; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests; diff --git a/src/OpenBanking.Library.Connector.Web/TestingAuthResult.cs b/test/OpenBanking.Library.Connector.BankTests/TestingAuthResult.cs similarity index 70% rename from src/OpenBanking.Library.Connector.Web/TestingAuthResult.cs rename to test/OpenBanking.Library.Connector.BankTests/TestingAuthResult.cs index 09f8f3b8e..cc54f4577 100644 --- a/src/OpenBanking.Library.Connector.Web/TestingAuthResult.cs +++ b/test/OpenBanking.Library.Connector.BankTests/TestingAuthResult.cs @@ -1,8 +1,8 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace FinnovationLabs.OpenBanking.Library.Connector.Web; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests; public class TestingAuthResult { diff --git a/src/OpenBanking.Library.Connector.Web/TestingMethods.cs b/test/OpenBanking.Library.Connector.BankTests/TestingMethods.cs similarity index 94% rename from src/OpenBanking.Library.Connector.Web/TestingMethods.cs rename to test/OpenBanking.Library.Connector.BankTests/TestingMethods.cs index 55899a302..b6943b912 100644 --- a/src/OpenBanking.Library.Connector.Web/TestingMethods.cs +++ b/test/OpenBanking.Library.Connector.BankTests/TestingMethods.cs @@ -1,4 +1,4 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. +// Licensed to Finnovation Labs Limited under one or more agreements. // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,7 +7,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests; public class TestingMethods { @@ -53,7 +53,6 @@ public void RegisterCreateDomesticVrpConsentAuthContext( CreateDomesticVrpConsentAuthContext = createDomesticVrpConsentAuthContext; } - public void RegisterProcessRedirect( Func> processRedirect) { diff --git a/src/OpenBanking.Library.Connector.Web/wwwroot/auth/fragment-redirect.html b/test/OpenBanking.Library.Connector.BankTests/wwwroot/auth/fragment-redirect.html similarity index 98% rename from src/OpenBanking.Library.Connector.Web/wwwroot/auth/fragment-redirect.html rename to test/OpenBanking.Library.Connector.BankTests/wwwroot/auth/fragment-redirect.html index 847bff96f..1e46c290d 100644 --- a/src/OpenBanking.Library.Connector.Web/wwwroot/auth/fragment-redirect.html +++ b/test/OpenBanking.Library.Connector.BankTests/wwwroot/auth/fragment-redirect.html @@ -1,4 +1,4 @@ - + diff --git a/src/OpenBanking.Library.Connector.Web/wwwroot/auth/js/main.js b/test/OpenBanking.Library.Connector.BankTests/wwwroot/auth/js/main.js similarity index 100% rename from src/OpenBanking.Library.Connector.Web/wwwroot/auth/js/main.js rename to test/OpenBanking.Library.Connector.BankTests/wwwroot/auth/js/main.js From 68236d5ae4aa96b0decab74ecd992de214b7118b Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 23 May 2026 22:54:41 +0100 Subject: [PATCH 38/49] [Test] Remove GenericHost project dependencies from BankTests project --- .../AppConfiguration.cs | 6 - .../BankTests/AppContextFixture.cs | 15 +- .../BankTests/AppTests.cs | 10 +- .../Configuration/BankTestSettings.cs | 37 +---- .../BankTestSettingsValidator.cs | 41 ++++++ .../TestingAccountAccessConsentsController.cs | 31 +---- .../AuthContext/TestingRedirectsController.cs | 129 +++++------------- ...estingDomesticPaymentConsentsController.cs | 33 +---- .../TestingDomesticVrpConsentsController.cs | 33 +---- .../Extensions/ServiceCollectionExtensions.cs | 14 +- .../AccountAccessConsentSubtest.cs | 8 +- .../DomesticVrpConsentSubtest.cs | 8 +- 12 files changed, 112 insertions(+), 253 deletions(-) create mode 100644 test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs diff --git a/test/OpenBanking.Library.Connector.BankTests/AppConfiguration.cs b/test/OpenBanking.Library.Connector.BankTests/AppConfiguration.cs index 9ff681014..c8670eeaf 100644 --- a/test/OpenBanking.Library.Connector.BankTests/AppConfiguration.cs +++ b/test/OpenBanking.Library.Connector.BankTests/AppConfiguration.cs @@ -4,8 +4,6 @@ using System.Reflection; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; -using FinnovationLabs.OpenBanking.Library.Connector.Configuration; -using FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.UserSecrets; @@ -49,8 +47,4 @@ static AppConfiguration() public static string RequestsDirectory { get; } public static IConfiguration Configuration { get; } - - public static TSettings GetSettings() - where TSettings : class, ISettings, new() => - ServiceCollectionExtensions.GetSettings(Configuration); } diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs index 97bd96aa1..611e4d21e 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs @@ -4,7 +4,6 @@ using System.Reflection; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Extensions; -using FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions; using FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -37,12 +36,10 @@ public AppContextFixture() Assembly webHostAssembly = typeof(ServiceCollectionExtensions).Assembly; builder.Services - // Add .NET generic host app services - .AddGenericHostServices(builder.Configuration) // Add .NET web host app services .AddWebHostServices(builder.Configuration, null) // Add bank testing services - .AddBankTestingServices(builder.Configuration) + .AddBankTestingServices() // Add memory cache .AddMemoryCache() // Add controllers @@ -67,12 +64,10 @@ public AppContextFixture() WebApplication app = builder.Build(); Host = app; - // Errors - if (!app.Environment.IsDevelopment()) - { - app.UseExceptionHandler("/error"); - app.UseHsts(); - } + // Errors: always suppress exception details (including in development) + app.UseExceptionHandler("/error"); + + app.UseHsts(); // Add static files app.UseWebHostStaticFiles(); diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index 4a6b0895a..41afb4536 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs @@ -14,8 +14,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.BankTests.FunctionalSubtests.VariableRecurringPayments. DomesticVrpConsent; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Models.Repository; -using FinnovationLabs.OpenBanking.Library.Connector.Configuration; -using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management.Request; @@ -26,6 +24,7 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.Playwright; using ObSealCertificateRequest = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management.Request.ObSealCertificate; @@ -361,14 +360,11 @@ private async Task TestAllInner(BankTestData testData) // Get bank test settings BankTestSettings bankTestSettings = - testServiceProvider.GetRequiredService>().GetSettings(); - - // Get logger - var instrumentationClient = testServiceProvider.GetRequiredService(); + testServiceProvider.GetRequiredService>().Value; // Get bank profile definitions var bankProfileDefinitions = - testServiceProvider.GetRequiredService(); + appServiceProvider.GetRequiredService(); BankProfile bankProfile = bankProfileDefinitions.GetBankProfile(testData.BankProfile); diff --git a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs index 89537dd4e..a1cfe2176 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs @@ -2,7 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.Configuration; using FinnovationLabs.OpenBanking.Library.Connector.Utility; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Configuration; @@ -115,8 +114,10 @@ public class ExecutablePath public string? Linux { get; set; } } -public class BankTestSettings : ISettings +public class BankTestSettings { + public const string ConfigSectionName = "OpenBankingConnector:BankTests"; + public ConsentAuthoriserOptions Auth { get; set; } = new(); /// @@ -129,38 +130,6 @@ public class BankTestSettings : ISettings /// public bool LogExternalApiData { get; set; } = false; - public string SettingsGroupName => "OpenBankingConnector:BankTests"; - - public BankTestSettings Validate() - { - if (!Directory.Exists(GetDataDirectoryForCurrentOs())) - { - throw new DirectoryNotFoundException( - "Can't locate data path specified in bank test setting DataDirectory:" + - $"{GetDataDirectoryForCurrentOs()}. Please update app settings."); - } - - // Check executable path in the case where this is not ignored - if (!Auth.PlaywrightLaunch.IgnoreExecutablePathAndArgs) - { - // Check executable path is not null - if (Auth.PlaywrightLaunch.GetExecutablePathForCurrentOs() is null) - { - throw new ArgumentException("Please specify an executable path in app settings."); - } - - // Check executable path exists - if (!File.Exists(Auth.PlaywrightLaunch.GetExecutablePathForCurrentOs())) - { - throw new DirectoryNotFoundException( - "Can't locate executable path specified in bank test setting ExecutablePath:" + - $"{Auth.PlaywrightLaunch.GetExecutablePathForCurrentOs()}. Please update app settings."); - } - } - - return this; - } - // Gets data directory for current OS platform public string GetDataDirectoryForCurrentOs() => OsPlatformEnumHelper.GetCurrentOsPlatform() switch diff --git a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs new file mode 100644 index 000000000..14bfb3e85 --- /dev/null +++ b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs @@ -0,0 +1,41 @@ +// Licensed to Finnovation Labs Limited under one or more agreements. +// Finnovation Labs Limited licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Extensions.Options; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Configuration; + +public class BankTestSettingsValidator : IValidateOptions +{ + public ValidateOptionsResult Validate(string? name, BankTestSettings settings) + { + var failures = new List(); + + if (!Directory.Exists(settings.GetDataDirectoryForCurrentOs())) + { + failures.Add( + $"Can't locate data path specified in bank test setting DataDirectory: " + + $"{settings.GetDataDirectoryForCurrentOs()}. Please update app settings."); + } + + if (!settings.Auth.PlaywrightLaunch.IgnoreExecutablePathAndArgs) + { + string? executablePath = settings.Auth.PlaywrightLaunch.GetExecutablePathForCurrentOs(); + if (executablePath is null) + { + failures.Add("Please specify an executable path in app settings."); + } + else if (!File.Exists(executablePath)) + { + failures.Add( + $"Can't locate executable path specified in bank test setting ExecutablePath: " + + $"{executablePath}. Please update app settings."); + } + } + + return failures.Count > 0 + ? ValidateOptionsResult.Fail(failures) + : ValidateOptionsResult.Success; + } +} diff --git a/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs index 87c8eff8b..86b10c019 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs @@ -2,8 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Response; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.Ac [Route("aisp/account-access-consents")] public class TestingAccountAccessConsentsController : ControllerBase { - private readonly IRequestBuilder _requestBuilder; - - public TestingAccountAccessConsentsController(IRequestBuilder requestBuilder) - { - _requestBuilder = requestBuilder; - } - public static string BrowserCookieKey => "__Host-Session-Id"; /// @@ -43,26 +34,12 @@ public async Task GetAsync( string? reference) { // Create auth context - AccountAccessConsentAuthContextCreateResponse authContextResponse; - if (TestingMethods.Instance.CreateAccountAccessConsentAuthContext is not null) + if (TestingMethods.Instance.CreateAccountAccessConsentAuthContext is null) { - authContextResponse = - await TestingMethods.Instance.CreateAccountAccessConsentAuthContext(accountAccessConsentId); - } - else - { - var authContextRequest = new AccountAccessConsentAuthContext - { - AccountAccessConsentId = accountAccessConsentId, - Reference = reference, - CreatedBy = modifiedBy - }; - authContextResponse = await _requestBuilder - .AccountAndTransaction - .AccountAccessConsents - .AuthContexts - .CreateLocalAsync(authContextRequest); + throw new InvalidOperationException(); } + AccountAccessConsentAuthContextCreateResponse authContextResponse = + await TestingMethods.Instance.CreateAccountAccessConsentAuthContext(accountAccessConsentId); // Set cookie Response.Cookies.Append( diff --git a/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs index 044e92de3..5b0497bfc 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs @@ -4,9 +4,6 @@ using System.ComponentModel.DataAnnotations; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AccountAndTransaction; -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Response; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -19,13 +16,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.Au [Route("auth")] public class TestingRedirectsController : ControllerBase { - private readonly IRequestBuilder _requestBuilder; - - public TestingRedirectsController(IRequestBuilder requestBuilder) - { - _requestBuilder = requestBuilder; - } - /// /// OAuth2 query redirect endpoint /// @@ -47,41 +37,25 @@ [Required] [FromQuery(Name = "state")] { if (TestingMethods.Instance.ProcessRedirect is null) { - var authResult = - new AuthResult - { - ResponseMode = OAuth2ResponseMode.Query, - State = state, - OAuth2RedirectOptionalParameters = new OAuth2RedirectOptionalParameters - { - Error = error, - IdToken = idToken, - Code = code - } - }; - _ = await _requestBuilder - .AuthContexts - .UpdateAuthResultAsync(authResult, "Redirect to /auth/query-redirect"); + throw new InvalidOperationException(); } - else - { - // Create form collection from query parameters - List> formCollection = Request.Query - .SelectMany( - pair => pair.Value, - (pair, value) => new KeyValuePair(pair.Key, value)) - .ToList(); - // Add response_mode - formCollection.Add(new KeyValuePair("response_mode", "query")); - var authResult = - new TestingAuthResult - { - State = state, - RedirectParameters = formCollection - }; - await TestingMethods.Instance.ProcessRedirect(authResult); - } + // Create form collection from query parameters + List> formCollection = Request.Query + .SelectMany( + pair => pair.Value, + (pair, value) => new KeyValuePair(pair.Key, value)) + .ToList(); + + // Add response_mode + formCollection.Add(new KeyValuePair("response_mode", "query")); + var authResult = + new TestingAuthResult + { + State = state, + RedirectParameters = formCollection + }; + await TestingMethods.Instance.ProcessRedirect(authResult); return Ok(); // We do not return data to bank following query redirect } @@ -118,64 +92,31 @@ [Required] [FromForm(Name = "state")] string cookieKey = TestingAccountAccessConsentsController.BrowserCookieKey; Request.Cookies.TryGetValue(cookieKey, out string? appSessionId); - AuthContextUpdateAuthResultResponse fluentResponse; if (TestingMethods.Instance.ProcessRedirect is null) { - // Parse response_mode (ASP.NET model binding will only do simple conversion) - OAuth2ResponseMode? oAuth2ResponseMode = responseMode switch - { - null => null, - "query" => OAuth2ResponseMode.Query, - "fragment" => OAuth2ResponseMode.Fragment, - "form_post" => OAuth2ResponseMode.FormPost, - _ => throw new ArgumentOutOfRangeException( - nameof(responseMode), - responseMode, - "Unknown value for response_mode.") - }; - - // Operation - var authResult = - new AuthResult - { - ResponseMode = oAuth2ResponseMode, - RedirectUrl = redirectUrl, - AppSessionId = appSessionId, - State = state, - OAuth2RedirectOptionalParameters = new OAuth2RedirectOptionalParameters - { - Error = error, - IdToken = idToken, - Code = code - } - }; - fluentResponse = await _requestBuilder - .AuthContexts - .UpdateAuthResultAsync(authResult); + throw new InvalidOperationException(); } - else - { - // Create form collection - List> formCollection = Request.Form - .SelectMany( - pair => pair.Value, - (pair, value) => new KeyValuePair(pair.Key, value)).ToList(); - // Add app session ID to form collection - if (appSessionId is not null) - { - formCollection.Add(new KeyValuePair("app_session_id", appSessionId)); - } + // Create form collection + List> formCollection = Request.Form + .SelectMany( + pair => pair.Value, + (pair, value) => new KeyValuePair(pair.Key, value)).ToList(); - var authResult = - new TestingAuthResult - { - State = state, - RedirectParameters = formCollection - }; - fluentResponse = await TestingMethods.Instance.ProcessRedirect(authResult); + // Add app session ID to form collection + if (appSessionId is not null) + { + formCollection.Add(new KeyValuePair("app_session_id", appSessionId)); } + var authResult = + new TestingAuthResult + { + State = state, + RedirectParameters = formCollection + }; + AuthContextUpdateAuthResultResponse fluentResponse = await TestingMethods.Instance.ProcessRedirect(authResult); + return Created("about:blank", fluentResponse); } } diff --git a/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs index 1692fd78d..8518e40f7 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs @@ -2,8 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.PaymentInitiation.Response; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.Pa [Route("pisp/domestic-payment-consents")] public class TestingDomesticPaymentConsentsController : ControllerBase { - private readonly IRequestBuilder _requestBuilder; - - public TestingDomesticPaymentConsentsController(IRequestBuilder requestBuilder) - { - _requestBuilder = requestBuilder; - } - public static string BrowserCookieKey => "__Host-Session-Id"; /// @@ -43,28 +34,12 @@ public async Task GetAsync( string? reference) { // Create auth context - - // Register state parameter - DomesticPaymentConsentAuthContextCreateResponse authContextResponse; - if (TestingMethods.Instance.CreateDomesticPaymentConsentAuthContext is not null) - { - authContextResponse = await TestingMethods.Instance.CreateDomesticPaymentConsentAuthContext( - domesticPaymentConsentId); - } - else + if (TestingMethods.Instance.CreateDomesticPaymentConsentAuthContext is null) { - var authContextRequest = new DomesticPaymentConsentAuthContext - { - DomesticPaymentConsentId = domesticPaymentConsentId, - Reference = reference, - CreatedBy = modifiedBy - }; - authContextResponse = await _requestBuilder - .PaymentInitiation - .DomesticPaymentConsents - .AuthContexts - .CreateLocalAsync(authContextRequest); + throw new InvalidOperationException(); } + DomesticPaymentConsentAuthContextCreateResponse authContextResponse = + await TestingMethods.Instance.CreateDomesticPaymentConsentAuthContext(domesticPaymentConsentId); // Set cookie Response.Cookies.Append( diff --git a/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs index 9516cf760..298190d9b 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs @@ -2,8 +2,6 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Response; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.Va [Route("vrp/domestic-vrp-consents")] public class TestingDomesticVrpConsentsController : ControllerBase { - private readonly IRequestBuilder _requestBuilder; - - public TestingDomesticVrpConsentsController(IRequestBuilder requestBuilder) - { - _requestBuilder = requestBuilder; - } - public static string BrowserCookieKey => "__Host-Session-Id"; /// @@ -43,28 +34,12 @@ public async Task GetAsync( string? reference) { // Create auth context - - // Register state parameter - DomesticVrpConsentAuthContextCreateResponse authContextResponse; - if (TestingMethods.Instance.CreateDomesticVrpConsentAuthContext is not null) - { - authContextResponse = - await TestingMethods.Instance.CreateDomesticVrpConsentAuthContext(domesticVrpConsentId); - } - else + if (TestingMethods.Instance.CreateDomesticVrpConsentAuthContext is null) { - var authContextRequest = new DomesticVrpConsentAuthContext - { - DomesticVrpConsentId = domesticVrpConsentId, - Reference = reference, - CreatedBy = modifiedBy - }; - authContextResponse = await _requestBuilder - .VariableRecurringPayments - .DomesticVrpConsents - .AuthContexts - .CreateLocalAsync(authContextRequest); + throw new InvalidOperationException(); } + DomesticVrpConsentAuthContextCreateResponse authContextResponse = + await TestingMethods.Instance.CreateDomesticVrpConsentAuthContext(domesticVrpConsentId); // Set cookie Response.Cookies.Append( diff --git a/test/OpenBanking.Library.Connector.BankTests/Extensions/ServiceCollectionExtensions.cs b/test/OpenBanking.Library.Connector.BankTests/Extensions/ServiceCollectionExtensions.cs index 9167e264f..045fcb54b 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Extensions/ServiceCollectionExtensions.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Extensions/ServiceCollectionExtensions.cs @@ -3,21 +3,21 @@ // See the LICENSE file in the project root for more information. using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Configuration; -using FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Extensions; public static class ServiceCollectionExtensions { - public static IServiceCollection AddBankTestingServices( - this IServiceCollection services, - IConfiguration configuration) + public static IServiceCollection AddBankTestingServices(this IServiceCollection services) { - // Add settings groups services - .AddSettingsGroup(); + .AddOptions() + .BindConfiguration(BankTestSettings.ConfigSectionName) + .ValidateOnStart(); + + services.AddSingleton, BankTestSettingsValidator>(); return services; } diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs index 385c07242..da1e70c7c 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs @@ -7,8 +7,6 @@ using FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.BrowserInteraction; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Models.Repository; -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; -using FinnovationLabs.OpenBanking.Library.Connector.GenericHost; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.AccountAndTransaction; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; @@ -20,6 +18,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; using AccountAccessConsentAuthContext = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Request. AccountAccessConsentAuthContext; @@ -372,11 +371,10 @@ await accountAndTransactionApiClient.TransactionsRead( { { // Get new application services scope - using IServiceScopeContainer serviceScopeContainer = - new ServiceScopeFromDependencyInjection(appServiceProvider); + using IServiceScope scope = appServiceProvider.CreateScope(); // Get consent - IDbService dbService = serviceScopeContainer.DbService; + var dbService = scope.ServiceProvider.GetRequiredService(); IDbMethods dbMethods = dbService.GetDbMethods(); IDbEntityMethods consentEntityMethods = diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs index b19fdc8ef..64b73db98 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs @@ -6,9 +6,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.BankTests.BankTests; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.BrowserInteraction; using FinnovationLabs.OpenBanking.Library.Connector.BankTests.Models.Repository; -using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using FinnovationLabs.OpenBanking.Library.Connector.Fluent.Primitives; -using FinnovationLabs.OpenBanking.Library.Connector.GenericHost; using FinnovationLabs.OpenBanking.Library.Connector.Models.Fapi; using FinnovationLabs.OpenBanking.Library.Connector.Models.Persistent.VariableRecurringPayments; using FinnovationLabs.OpenBanking.Library.Connector.Models.Public; @@ -19,6 +17,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; using DomesticVrpConsentAuthContext = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request. DomesticVrpConsentAuthContext; @@ -227,11 +226,10 @@ await variableRecurringPaymentsApiClient.DomesticVrpReadPaymentDetails( { { // Get new application services scope - using IServiceScopeContainer serviceScopeContainer = - new ServiceScopeFromDependencyInjection(appServiceProvider); + using IServiceScope scope = appServiceProvider.CreateScope(); // Get consent - IDbService dbService = serviceScopeContainer.DbService; + var dbService = scope.ServiceProvider.GetRequiredService(); IDbMethods dbMethods = dbService.GetDbMethods(); IDbEntityMethods consentEntityMethods = From 6cf47dba3210dec9fe42b3a94d5aae26f25227df Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 23 May 2026 21:06:02 +0100 Subject: [PATCH 39/49] Improve application start-up logging --- .../Extensions/ServiceCollectionExtensions.cs | 5 +++- .../WebAppInformationHostedService.cs | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs b/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs index b699bc558..1354ecc08 100644 --- a/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs @@ -28,7 +28,10 @@ public static IServiceCollection AddWebHostServices( services.AddOpenTelemetry(serviceVersion, openTelemetrySettings); // Startup tasks - services.AddHostedService(); + services.AddHostedService( + sp => serviceVersion is not null + ? ActivatorUtilities.CreateInstance(sp, serviceVersion) + : ActivatorUtilities.CreateInstance(sp)); services.AddHealthChecks(); diff --git a/src/OpenBanking.Library.Connector.Web/HostedServices/WebAppInformationHostedService.cs b/src/OpenBanking.Library.Connector.Web/HostedServices/WebAppInformationHostedService.cs index 52ce8eb5e..264398717 100644 --- a/src/OpenBanking.Library.Connector.Web/HostedServices/WebAppInformationHostedService.cs +++ b/src/OpenBanking.Library.Connector.Web/HostedServices/WebAppInformationHostedService.cs @@ -17,26 +17,38 @@ public class WebAppInformationHostedService : IHostedService private readonly ApplicationPartManager _applicationPartManager; private readonly IConfigurationRoot _configurationRoot; private readonly EndpointDataSource _endpointDataSource; + private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IHostEnvironment _hostEnvironment; private readonly ILogger _logger; + private readonly string? _serviceVersion; public WebAppInformationHostedService( ILogger logger, ApplicationPartManager applicationPartManager, EndpointDataSource endpointDataSource, IConfiguration configuration, - IHostEnvironment hostEnvironment) + IHostEnvironment hostEnvironment, + IHostApplicationLifetime hostApplicationLifetime, + string? serviceVersion = null) { _logger = logger; _applicationPartManager = applicationPartManager; _endpointDataSource = endpointDataSource; _hostEnvironment = hostEnvironment; + _hostApplicationLifetime = hostApplicationLifetime; _configurationRoot = (IConfigurationRoot) configuration; + _serviceVersion = serviceVersion; } public Task StartAsync(CancellationToken cancellationToken) { - // Log Operating System + // Log web app version + if (_serviceVersion is not null) + { + _logger.LogInformation("Open Banking Connector Web App version: {Version}", _serviceVersion); + } + + // Log operating system string osName = OsPlatformEnumHelper.GetCurrentOsPlatform() switch { OsPlatformEnum.MacOs => "macOS", @@ -45,7 +57,7 @@ public Task StartAsync(CancellationToken cancellationToken) _ => throw new ArgumentOutOfRangeException() }; - _logger.LogInformation("Operating System detected:" + Environment.NewLine + $"{osName}"); + _logger.LogInformation("Operating system detected: {OsName}", osName); if (_hostEnvironment.IsDevelopment()) { @@ -59,11 +71,10 @@ public Task StartAsync(CancellationToken cancellationToken) IEnumerable controllerNames = feature.Controllers.Select(x => x.Name); _logger.LogInformation(string.Join(Environment.NewLine, controllerNames.Prepend("Controllers found:"))); - // Log endpoints found - Task.Run( - async () => + // Log endpoints found (once middleware is fully built) + _hostApplicationLifetime.ApplicationStarted.Register( + () => { - await Task.Delay(3000); // wait 3 seconds for middleware to be built IEnumerable endpointNames = _endpointDataSource.Endpoints.Select(x => x.DisplayName); _logger.LogInformation(string.Join(Environment.NewLine, endpointNames.Prepend("Endpoints found:"))); }); From d0d7032423afb77ef875e60f6839b1b8bf0177b4 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 12:45:05 +0100 Subject: [PATCH 40/49] Remove GenericHost project dependencies from Web project --- .../Extensions/ServiceCollectionExtensions.cs | 164 ++++++++++++++++- ...nking.Library.Connector.GenericHost.csproj | 5 + .../Extensions/ServiceCollectionExtensions.cs | 169 +----------------- .../OpenBanking.Library.Connector.Web.csproj | 8 +- .../Configuration/OpenTelemetrySettings.cs | 14 +- .../OpenBanking.WebApp.Connector.csproj | 5 + src/OpenBanking.WebApp.Connector/Program.cs | 7 +- .../BankTests/AppContextFixture.cs | 2 +- 8 files changed, 193 insertions(+), 181 deletions(-) diff --git a/src/OpenBanking.Library.Connector.GenericHost/Extensions/ServiceCollectionExtensions.cs b/src/OpenBanking.Library.Connector.GenericHost/Extensions/ServiceCollectionExtensions.cs index ab8b1b579..de7ebcdd5 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/Extensions/ServiceCollectionExtensions.cs +++ b/src/OpenBanking.Library.Connector.GenericHost/Extensions/ServiceCollectionExtensions.cs @@ -18,7 +18,14 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using MongoDB.Driver; +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; using TimeProvider = FinnovationLabs.OpenBanking.Library.Connector.Services.TimeProvider; namespace FinnovationLabs.OpenBanking.Library.Connector.GenericHost.Extensions; @@ -27,7 +34,9 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddGenericHostServices( this IServiceCollection services, - IConfiguration configuration) + IConfiguration configuration, + string? serviceVersion = null, + Action? addAspNetCoreTracingInstrumentation = null) { // Add settings groups services @@ -132,6 +141,10 @@ public static IServiceCollection AddGenericHostServices( // Startup tasks services.AddHostedService(); + // Add OpenTelemetry services + var openTelemetrySettings = GetSettings(configuration); + services.AddOpenTelemetryServices(serviceVersion, openTelemetrySettings, addAspNetCoreTracingInstrumentation); + return services; } @@ -164,4 +177,153 @@ public static IServiceCollection AddSettingsGroup(this IServiceCollec return services; } + + public static ILoggingBuilder AddGenericHostLogging( + this ILoggingBuilder loggingBuilder, + IConfiguration configuration, + string? serviceVersion) + { + var openTelemetrySettings = GetSettings(configuration); + loggingBuilder.AddOpenTelemetryLogging(serviceVersion, openTelemetrySettings); + return loggingBuilder; + } + + private static void AddOpenTelemetryServices( + this IServiceCollection services, + string? serviceVersion, + OpenTelemetrySettings openTelemetrySettings, + Action? addAspNetCoreTracingInstrumentation = null) + { + bool useConsoleExporter = openTelemetrySettings.UseConsoleExporter; + + ProviderFilter tracingProviderFilter = openTelemetrySettings.Tracing.ProviderFilter; + + string? tracingOtlpExporterUrlProcessed = openTelemetrySettings.Tracing.OtlpExporterUrlProcessed; + + void ConfigureTracing(TracerProviderBuilder builder) + { + if (tracingProviderFilter.HasFlag(ProviderFilter.HttpClient)) + { + builder.AddHttpClientInstrumentation(); + } + + if (tracingProviderFilter.HasFlag(ProviderFilter.EfCore)) + { + builder.AddEntityFrameworkCoreInstrumentation(); + } + + if (tracingProviderFilter.HasFlag(ProviderFilter.AspNetCore) && + addAspNetCoreTracingInstrumentation is not null) + { + addAspNetCoreTracingInstrumentation(builder); + } + + if (useConsoleExporter) + { + builder.AddConsoleExporter(); + } + + if (tracingOtlpExporterUrlProcessed is not null) + { + builder.AddOtlpExporter(opt => opt.Endpoint = new Uri(tracingOtlpExporterUrlProcessed)); + } + } + + string? metricsOtlpExporterUrlProcessed = openTelemetrySettings.Metrics.OtlpExporterUrlProcessed; + + void ConfigureMetrics(MeterProviderBuilder builder) + { + //builder.AddMeter("System.Net.Http"); + builder.AddMeter("TppReportingMetrics"); + + if (useConsoleExporter) + { + builder.AddConsoleExporter( + (_, metricReaderOptions) => + { + metricReaderOptions.PeriodicExportingMetricReaderOptions = + new PeriodicExportingMetricReaderOptions + { + ExportIntervalMilliseconds = openTelemetrySettings.Metrics + .MetricReaderExportIntervalMilliseconds + }; + metricReaderOptions.TemporalityPreference = + openTelemetrySettings.Metrics.MetricReaderTemporality; + }); + } + + if (metricsOtlpExporterUrlProcessed is not null) + { + builder.AddOtlpExporter( + (otlpExporterOptions, metricReaderOptions) => + { + otlpExporterOptions.Endpoint = new Uri(metricsOtlpExporterUrlProcessed); + metricReaderOptions.PeriodicExportingMetricReaderOptions = + new PeriodicExportingMetricReaderOptions + { + ExportIntervalMilliseconds = openTelemetrySettings.Metrics + .MetricReaderExportIntervalMilliseconds + }; + metricReaderOptions.TemporalityPreference = + openTelemetrySettings.Metrics.MetricReaderTemporality; + }); + } + } + + + Sdk.SetDefaultTextMapPropagator( + new CompositeTextMapPropagator( + Array.Empty())); // See https://github.com/dotnet/runtime/issues/90407 + + bool anyTracingExporter = openTelemetrySettings.HasAnyTracingExporter; + bool anyMetricsExporter = openTelemetrySettings.HasAnyMetricsExporter; + + if (anyTracingExporter || anyMetricsExporter) + { + OpenTelemetryBuilder otelBuilder = services.AddOpenTelemetry() + .ConfigureResource( + resource => resource.AddService( + openTelemetrySettings.ServiceName, + openTelemetrySettings.ServiceNamespace, + serviceVersion, + false, + openTelemetrySettings.ServiceInstanceId)); + + if (anyTracingExporter) + { + otelBuilder.WithTracing(ConfigureTracing); + } + + if (anyMetricsExporter) + { + otelBuilder.WithMetrics(ConfigureMetrics); + } + } + } + + private static void AddOpenTelemetryLogging( + this ILoggingBuilder loggingBuilder, + string? serviceVersion, + OpenTelemetrySettings openTelemetrySettings) + { + if (openTelemetrySettings.Logging.OtlpExporterUrlProcessed is not null) + { + loggingBuilder.AddOpenTelemetry( + options => + { + options + .SetResourceBuilder( + ResourceBuilder.CreateDefault() + .AddService( + openTelemetrySettings.ServiceName, + openTelemetrySettings.ServiceNamespace, + serviceVersion, + false, + openTelemetrySettings.ServiceInstanceId)) + .AddOtlpExporter( + otlpExporterOptions => otlpExporterOptions.Endpoint = + new Uri(openTelemetrySettings.Logging.OtlpExporterUrlProcessed)); + }); + } + } } diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index 8e5378e8c..b888e3f9e 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -32,6 +32,11 @@ + + + + + diff --git a/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs b/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs index 1354ecc08..c65d2cad8 100644 --- a/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs @@ -2,17 +2,8 @@ // Finnovation Labs Limited licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FinnovationLabs.OpenBanking.Library.Connector.Models.Configuration; using FinnovationLabs.OpenBanking.Library.Connector.Web.HostedServices; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using OpenTelemetry; -using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Logs; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Extensions; @@ -20,13 +11,8 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddWebHostServices( this IServiceCollection services, - IConfiguration configuration, - string? serviceVersion) + string? serviceVersion = null) { - var openTelemetrySettings = - GenericHost.Extensions.ServiceCollectionExtensions.GetSettings(configuration); - services.AddOpenTelemetry(serviceVersion, openTelemetrySettings); - // Startup tasks services.AddHostedService( sp => serviceVersion is not null @@ -37,157 +23,4 @@ public static IServiceCollection AddWebHostServices( return services; } - - public static ILoggingBuilder AddWebHostLogging( - this ILoggingBuilder loggingBuilder, - IConfiguration configuration, - string? serviceVersion) - { - var openTelemetrySettings = - GenericHost.Extensions.ServiceCollectionExtensions.GetSettings(configuration); - loggingBuilder.AddOpenTelemetry(serviceVersion, openTelemetrySettings); - - return loggingBuilder; - } - - private static void AddOpenTelemetry( - this ILoggingBuilder loggingBuilder, - string? serviceVersion, - OpenTelemetrySettings openTelemetrySettings) - { - string? otlpExporterUrl = openTelemetrySettings.Logging.OtlpExporterUrl.Length > 0 - ? openTelemetrySettings.Logging.OtlpExporterUrl - : null; - - if (otlpExporterUrl is not null) - { - loggingBuilder.AddOpenTelemetry( - options => - { - options - .SetResourceBuilder( - ResourceBuilder.CreateDefault() - .AddService( - openTelemetrySettings.ServiceName, - openTelemetrySettings.ServiceNamespace, - serviceVersion, - false, - openTelemetrySettings.ServiceInstanceId)) - .AddOtlpExporter( - otlpExporterOptions => otlpExporterOptions.Endpoint = new Uri(otlpExporterUrl)); - }); - } - } - - private static void AddOpenTelemetry( - this IServiceCollection services, - string? serviceVersion, - OpenTelemetrySettings openTelemetrySettings) - { - Sdk.SetDefaultTextMapPropagator( - new CompositeTextMapPropagator( - Array.Empty())); // See https://github.com/dotnet/runtime/issues/90407 - - bool useConsoleExporter = openTelemetrySettings.UseConsoleExporter; - - ProviderFilter tracingProviderFilter = openTelemetrySettings.Tracing.ProviderFilter; - - string? tracingOtlpExporterUrl = openTelemetrySettings.Tracing.OtlpExporterUrl.Length > 0 - ? openTelemetrySettings.Tracing.OtlpExporterUrl - : null; - - string? metricsOtlpExporterUrl = openTelemetrySettings.Metrics.OtlpExporterUrl.Length > 0 - ? openTelemetrySettings.Metrics.OtlpExporterUrl - : null; - - void ConfigureTracing(TracerProviderBuilder builder) - { - if (tracingProviderFilter.HasFlag(ProviderFilter.AspNetCore)) - { - builder.AddAspNetCoreInstrumentation(); - } - - if (tracingProviderFilter.HasFlag(ProviderFilter.HttpClient)) - { - builder.AddHttpClientInstrumentation(); - } - - if (tracingProviderFilter.HasFlag(ProviderFilter.EfCore)) - { - builder.AddEntityFrameworkCoreInstrumentation(); - } - - if (useConsoleExporter) - { - builder.AddConsoleExporter(); - } - - if (tracingOtlpExporterUrl is not null) - { - builder.AddOtlpExporter(opt => opt.Endpoint = new Uri(tracingOtlpExporterUrl)); - } - } - - void ConfigureMetrics(MeterProviderBuilder builder) - { - //builder.AddMeter("System.Net.Http"); - builder.AddMeter("TppReportingMetrics"); - - if (useConsoleExporter) - { - builder.AddConsoleExporter( - (_, metricReaderOptions) => - { - metricReaderOptions.PeriodicExportingMetricReaderOptions = - new PeriodicExportingMetricReaderOptions - { - ExportIntervalMilliseconds = openTelemetrySettings.Metrics - .MetricReaderExportIntervalMilliseconds - }; - metricReaderOptions.TemporalityPreference = - openTelemetrySettings.Metrics.MetricReaderTemporality; - }); - } - - if (metricsOtlpExporterUrl is not null) - { - builder.AddOtlpExporter( - (otlpExporterOptions, metricReaderOptions) => - { - otlpExporterOptions.Endpoint = new Uri(metricsOtlpExporterUrl); - metricReaderOptions.PeriodicExportingMetricReaderOptions = - new PeriodicExportingMetricReaderOptions - { - ExportIntervalMilliseconds = openTelemetrySettings.Metrics - .MetricReaderExportIntervalMilliseconds - }; - metricReaderOptions.TemporalityPreference = - openTelemetrySettings.Metrics.MetricReaderTemporality; - }); - } - } - - bool useTracing = tracingOtlpExporterUrl is not null || useConsoleExporter; - bool useMetrics = metricsOtlpExporterUrl is not null || useConsoleExporter; - - if (useTracing || useMetrics) - { - IOpenTelemetryBuilder builder = services.AddOpenTelemetry() - .ConfigureResource( - resource => resource.AddService( - openTelemetrySettings.ServiceName, - openTelemetrySettings.ServiceNamespace, - serviceVersion, - false, - openTelemetrySettings.ServiceInstanceId)); - if (useTracing) - { - builder.WithTracing(ConfigureTracing); - } - if (useMetrics) - { - builder.WithMetrics(ConfigureMetrics); - } - } - } } diff --git a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj index d0171c1b6..26cf0482e 100644 --- a/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj +++ b/src/OpenBanking.Library.Connector.Web/OpenBanking.Library.Connector.Web.csproj @@ -24,16 +24,10 @@ - - - - - - - + diff --git a/src/OpenBanking.Library.Connector/Models/Configuration/OpenTelemetrySettings.cs b/src/OpenBanking.Library.Connector/Models/Configuration/OpenTelemetrySettings.cs index d37a8e960..6066c9e88 100644 --- a/src/OpenBanking.Library.Connector/Models/Configuration/OpenTelemetrySettings.cs +++ b/src/OpenBanking.Library.Connector/Models/Configuration/OpenTelemetrySettings.cs @@ -20,12 +20,16 @@ public class TracingSettings { public string OtlpExporterUrl { get; set; } = string.Empty; + public string? OtlpExporterUrlProcessed => string.IsNullOrWhiteSpace(OtlpExporterUrl) ? null : OtlpExporterUrl; + public ProviderFilter ProviderFilter { get; set; } = ProviderFilter.AspNetCore | ProviderFilter.HttpClient; } public class LoggingSettings { public string OtlpExporterUrl { get; set; } = string.Empty; + + public string? OtlpExporterUrlProcessed => string.IsNullOrWhiteSpace(OtlpExporterUrl) ? null : OtlpExporterUrl; } /// @@ -39,7 +43,7 @@ public class OpenTelemetrySettings : ISettings public string ServiceInstanceId { get; set; } = "Default"; - public bool UseConsoleExporter { get; set; } = false; + public bool UseConsoleExporter { get; set; } public LoggingSettings Logging { get; set; } = new(); @@ -47,6 +51,12 @@ public class OpenTelemetrySettings : ISettings public MetricsSettings Metrics { get; set; } = new(); + public bool HasAnyTracingExporter => + UseConsoleExporter || Tracing.OtlpExporterUrlProcessed is not null; + + public bool HasAnyMetricsExporter => + UseConsoleExporter || Metrics.OtlpExporterUrlProcessed is not null; + public string SettingsGroupName => "OpenBankingConnector:OpenTelemetry"; public OpenTelemetrySettings Validate() => this; @@ -56,6 +66,8 @@ public class MetricsSettings { public string OtlpExporterUrl { get; set; } = string.Empty; + public string? OtlpExporterUrlProcessed => string.IsNullOrWhiteSpace(OtlpExporterUrl) ? null : OtlpExporterUrl; + public int MetricReaderExportIntervalMilliseconds { get; set; } = 30000; public MetricReaderTemporalityPreference MetricReaderTemporality { get; set; } = diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index d78bb1e3e..763b7cf83 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -26,6 +26,11 @@ + + + + + diff --git a/src/OpenBanking.WebApp.Connector/Program.cs b/src/OpenBanking.WebApp.Connector/Program.cs index 355b43cdb..2d1e823c3 100644 --- a/src/OpenBanking.WebApp.Connector/Program.cs +++ b/src/OpenBanking.WebApp.Connector/Program.cs @@ -11,6 +11,7 @@ using Microsoft.OpenApi; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using OpenTelemetry.Trace; using AccountAndTransactionModelsPublic = FinnovationLabs.OpenBanking.Library.BankApiModels.UkObRw.V4p0.NSwagAisp.Models; using ServiceCollectionExtensionsGenericHost = @@ -31,9 +32,9 @@ builder.Services // Add .NET generic host app services - .AddGenericHostServices(builder.Configuration) + .AddGenericHostServices(builder.Configuration, serviceVersion, b => b.AddAspNetCoreInstrumentation()) // Add .NET web host app services - .AddWebHostServices(builder.Configuration, serviceVersion) + .AddWebHostServices(serviceVersion) // Configure Swagger .AddSwaggerGen( options => @@ -117,7 +118,7 @@ .Logging .ClearProviders() .AddConsole() - .AddWebHostLogging(builder.Configuration, serviceVersion); + .AddGenericHostLogging(builder.Configuration, serviceVersion); // Build app WebApplication app = builder.Build(); diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs index 611e4d21e..27a32d5c1 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs @@ -37,7 +37,7 @@ public AppContextFixture() builder.Services // Add .NET web host app services - .AddWebHostServices(builder.Configuration, null) + .AddWebHostServices() // Add bank testing services .AddBankTestingServices() // Add memory cache From 61abee85603b512e6335ab7a06fc4e56aa632d82 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 13:10:51 +0100 Subject: [PATCH 41/49] Update .NET dependencies --- ...nBanking.Library.Connector.GenericHost.csproj | 16 ++++++++-------- .../OpenBanking.Library.Connector.csproj | 4 ++-- .../OpenBanking.WebApp.Connector.csproj | 7 ++----- ...penBanking.Library.Connector.BankTests.csproj | 12 ++++++------ 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index b888e3f9e..a7b798e24 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,14 +24,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index 5a892b115..3e93be922 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -27,9 +27,9 @@ - + - + diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 763b7cf83..1639feb1f 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -16,16 +16,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 5d708e2fb..61781f521 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -16,11 +16,11 @@ - - - - - + + + + + From 387c50b65a43d51f2393bc2782d09aca849eee90 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 24 May 2026 21:59:54 +0100 Subject: [PATCH 42/49] [Docs] Update docs --- docs/apis/account-and-transaction/openapi-aisp.json | 2 +- docs/apis/auth-contexts/openapi-auth.json | 2 +- docs/apis/management/openapi-manage.json | 2 +- docs/apis/payment-initiation/openapi-pisp.json | 2 +- docs/apis/variable-recurring-payments/openapi-vrp.json | 2 +- .../OpenBanking.WebApp.Connector.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index 86a2cbc38..1b4570b65 100644 --- a/docs/apis/account-and-transaction/openapi-aisp.json +++ b/docs/apis/account-and-transaction/openapi-aisp.json @@ -3,7 +3,7 @@ "info": { "title": "Account and Transaction API", "description": "Account and Transaction API for Open Banking Connector Web App", - "version": "18.5.0" + "version": "19.0.0" }, "paths": { "/aisp/account-access-consent-auth-contexts": { diff --git a/docs/apis/auth-contexts/openapi-auth.json b/docs/apis/auth-contexts/openapi-auth.json index 9295b7266..1cb0c5b79 100644 --- a/docs/apis/auth-contexts/openapi-auth.json +++ b/docs/apis/auth-contexts/openapi-auth.json @@ -3,7 +3,7 @@ "info": { "title": "Auth Contexts API", "description": "Auth Contexts API for Open Banking Connector Web App", - "version": "18.5.0" + "version": "19.0.0" }, "paths": { "/auth/redirect-delegate": { diff --git a/docs/apis/management/openapi-manage.json b/docs/apis/management/openapi-manage.json index 4c6277731..217afa3d8 100644 --- a/docs/apis/management/openapi-manage.json +++ b/docs/apis/management/openapi-manage.json @@ -3,7 +3,7 @@ "info": { "title": "Management API", "description": "Management API for Open Banking Connector Web App", - "version": "18.5.0" + "version": "19.0.0" }, "paths": { "/manage/bank-registrations": { diff --git a/docs/apis/payment-initiation/openapi-pisp.json b/docs/apis/payment-initiation/openapi-pisp.json index 346d12e28..6c471f057 100644 --- a/docs/apis/payment-initiation/openapi-pisp.json +++ b/docs/apis/payment-initiation/openapi-pisp.json @@ -3,7 +3,7 @@ "info": { "title": "Payment Initiation API", "description": "Payment Initiation API for Open Banking Connector Web App", - "version": "18.5.0" + "version": "19.0.0" }, "paths": { "/pisp/domestic-payment-consent-auth-contexts": { diff --git a/docs/apis/variable-recurring-payments/openapi-vrp.json b/docs/apis/variable-recurring-payments/openapi-vrp.json index c85d22ea3..d3165ef34 100644 --- a/docs/apis/variable-recurring-payments/openapi-vrp.json +++ b/docs/apis/variable-recurring-payments/openapi-vrp.json @@ -3,7 +3,7 @@ "info": { "title": "Variable Recurring Payments API", "description": "Variable Recurring Payments API for Open Banking Connector Web App", - "version": "18.5.0" + "version": "19.0.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 1639feb1f..2cdd9b349 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 18.5.0 + 19.0.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false From e9d19029f1d5dde52439d45db26b4e61753ef809 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:10:30 +0100 Subject: [PATCH 43/49] Update .NET dependencies --- ...nking.Library.Connector.GenericHost.csproj | 22 +++++++++---------- .../OpenBanking.Library.Connector.csproj | 8 +++---- .../OpenBanking.WebApp.Connector.csproj | 8 +++---- ...Banking.Library.Connector.BankTests.csproj | 10 ++++----- ....Library.Connector.IntegrationTests.csproj | 3 ++- ...Banking.Library.Connector.UnitTests.csproj | 3 ++- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj index a7b798e24..eb80fb52e 100644 --- a/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj +++ b/src/OpenBanking.Library.Connector.GenericHost/OpenBanking.Library.Connector.GenericHost.csproj @@ -24,17 +24,17 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index 3e93be922..2d208fa99 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -27,13 +27,13 @@ - + - - + + - + diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 2cdd9b349..41a4d12ad 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -16,13 +16,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj index 61781f521..1ae9f180a 100644 --- a/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj +++ b/test/OpenBanking.Library.Connector.BankTests/OpenBanking.Library.Connector.BankTests.csproj @@ -11,15 +11,15 @@ Finnovation Labs Limited false true + true - - - - - + + + + diff --git a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj index 83acd4a8c..65cfe189e 100644 --- a/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj +++ b/test/OpenBanking.Library.Connector.IntegrationTests/OpenBanking.Library.Connector.IntegrationTests.csproj @@ -9,11 +9,12 @@ Finnovation Labs Limited and Contributors Finnovation Labs Limited false + true - + all diff --git a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj index ba9999dd7..d4a9604a1 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj +++ b/test/OpenBanking.Library.Connector.UnitTests/OpenBanking.Library.Connector.UnitTests.csproj @@ -9,11 +9,12 @@ Finnovation Labs Limited and Contributors Finnovation Labs Limited false + true - + all From 5c1263a77dbad3ce6a8b5289047a179caf7f2479 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 14 Jun 2026 15:50:01 +0100 Subject: [PATCH 44/49] [Test] Prepare for removal of old test logging system --- .../BankTests/AppTests.cs | 28 ++---------- .../Configuration/BankTestSettings.cs | 20 --------- .../BankTestSettingsValidator.cs | 7 --- .../AccountAccessConsentSubtest.cs | 15 ++++--- .../DomesticPaymentConsentSubtest.cs | 28 +++++++----- .../DomesticVrpConsentSubtest.cs | 43 +++++++++++-------- 6 files changed, 55 insertions(+), 86 deletions(-) diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index 41afb4536..40bfb78dd 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs @@ -383,22 +383,6 @@ private async Task TestAllInner(BankTestData testData) // Get application memory cache var memoryCache = appServiceProvider.GetRequiredService(); - // Create test data writers - var topLevelFolderName = "genericAppTests"; - var testDataProcessorFluentRequestLogging = new FilePathBuilder( - Path.Combine(bankTestSettings.GetDataDirectoryForCurrentOs(), $"{topLevelFolderName}/fluent"), - testName, - ".json"); - - FilePathBuilder? testDataProcessorApiLogging = null; - if (bankTestSettings.LogExternalApiData) - { - testDataProcessorApiLogging = new FilePathBuilder( - Path.Combine(bankTestSettings.GetDataDirectoryForCurrentOs(), $"{topLevelFolderName}/api"), - testName, - ".json"); - } - // Create consent auth PlaywrightLaunchOptions launchOptions = bankTestSettings.Auth.PlaywrightLaunch; @@ -544,9 +528,7 @@ await accountAccessConsentSubtest.RunTest( testData.TestAuth, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("aisp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser, @@ -598,9 +580,7 @@ await domesticPaymentConsentSubtest.RunTest( paymentsEnv, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("pisp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser); @@ -629,9 +609,7 @@ await domesticVrpConsentSubtest.RunTest( paymentsEnv, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("vrp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser, diff --git a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs index a1cfe2176..00fbedc18 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs @@ -119,24 +119,4 @@ public class BankTestSettings public const string ConfigSectionName = "OpenBankingConnector:BankTests"; public ConsentAuthoriserOptions Auth { get; set; } = new(); - - /// - /// Path to data folder used for logging, "API overrides", and bank user information. - /// - public OsSpecificDirectory DataDirectory { get; set; } = new(); - - /// - /// Log external API requests/responses. Off by default. - /// - public bool LogExternalApiData { get; set; } = false; - - // Gets data directory for current OS platform - public string GetDataDirectoryForCurrentOs() => - OsPlatformEnumHelper.GetCurrentOsPlatform() switch - { - OsPlatformEnum.MacOs => DataDirectory.MacOs, - OsPlatformEnum.Linux => DataDirectory.Linux, - OsPlatformEnum.Windows => DataDirectory.Windows, - _ => throw new ArgumentOutOfRangeException() - }; } diff --git a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs index 14bfb3e85..718f7742e 100644 --- a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs @@ -12,13 +12,6 @@ public ValidateOptionsResult Validate(string? name, BankTestSettings settings) { var failures = new List(); - if (!Directory.Exists(settings.GetDataDirectoryForCurrentOs())) - { - failures.Add( - $"Can't locate data path specified in bank test setting DataDirectory: " + - $"{settings.GetDataDirectoryForCurrentOs()}. Please update app settings."); - } - if (!settings.Auth.PlaywrightLaunch.IgnoreExecutablePathAndArgs) { string? executablePath = settings.Auth.PlaywrightLaunch.GetExecutablePathForCurrentOs(); diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs index da1e70c7c..1c0176113 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/AccountAndTransaction/AccountAccessConsent/AccountAccessConsentSubtest.cs @@ -43,7 +43,7 @@ public async Task RunTest( bool testAuth, string testNameUnique, string modifiedBy, - FilePathBuilder aispFluentRequestLogging, + FilePathBuilder? aispFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser, @@ -478,7 +478,7 @@ private static async Guid bankRegistrationId, string testNameUnique, string modifiedBy, - FilePathBuilder aispFluentRequestLogging) + FilePathBuilder? aispFluentRequestLogging) { var accountAccessConsentRequest = new AccountAccessConsentRequest @@ -508,10 +508,13 @@ private static async default(DateTimeOffset); // substitute logging placeholder } - await aispFluentRequestLogging - .AppendToPath("accountAccessConsent") - .AppendToPath("postRequest") - .WriteFile(accountAccessConsentRequest); + if (aispFluentRequestLogging is not null) + { + await aispFluentRequestLogging + .AppendToPath("accountAccessConsent") + .AppendToPath("postRequest") + .WriteFile(accountAccessConsentRequest); + } accountAccessConsentRequest.BankRegistrationId = bankRegistrationId; // remove logging placeholder if (expDateTime is not null) { diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs index c3e6c23de..e6711fdfd 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/PaymentInitiation/DomesticPaymentConsent/DomesticPaymentConsentSubtest.cs @@ -36,7 +36,7 @@ public async Task RunTest( PaymentsEnv paymentsEnv, string testNameUnique, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser) @@ -222,7 +222,7 @@ private static async Task GetDomesticPaymentRequest( Guid domesticPaymentConsentId, bool pispUseV4, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -288,10 +288,13 @@ private static async Task GetDomesticPaymentRequest( externalApiRequest), // customise external API request using bank profile ModifiedBy = "placeholder" // logging placeholder }; - await pispFluentRequestLogging - .AppendToPath("domesticPayment") - .AppendToPath("postRequest") - .WriteFile(domesticPaymentRequest); + if (pispFluentRequestLogging is not null) + { + await pispFluentRequestLogging + .AppendToPath("domesticPayment") + .AppendToPath("postRequest") + .WriteFile(domesticPaymentRequest); + } domesticPaymentRequest.DomesticPaymentConsentId = domesticPaymentConsentId; // replace logging placeholder domesticPaymentRequest.ExternalApiRequest.Data.Initiation.InstructionIdentification = @@ -327,7 +330,7 @@ private static async Task GetDomesticPaymentConse bool pispUseV4, string testNameUnique, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -398,10 +401,13 @@ private static async Task GetDomesticPaymentConse CreatedBy = "placeholder" // logging placeholder }; - await pispFluentRequestLogging - .AppendToPath("domesticPaymentConsent") - .AppendToPath("postRequest") - .WriteFile(domesticPaymentConsentRequest); + if (pispFluentRequestLogging is not null) + { + await pispFluentRequestLogging + .AppendToPath("domesticPaymentConsent") + .AppendToPath("postRequest") + .WriteFile(domesticPaymentConsentRequest); + } domesticPaymentConsentRequest.BankRegistrationId = bankRegistrationId; // replace logging placeholder domesticPaymentConsentRequest.ExternalApiRequest.Data.Initiation.InstructionIdentification = diff --git a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs index 64b73db98..dbcb1804d 100644 --- a/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs +++ b/test/OpenBanking.Library.Connector.BankTests/FunctionalSubtests/VariableRecurringPayments/DomesticVrpConsent/DomesticVrpConsentSubtest.cs @@ -42,7 +42,7 @@ public async Task RunTest( PaymentsEnv paymentsEnv, string testNameUnique, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser, @@ -333,7 +333,7 @@ await variableRecurringPaymentsApiClient.DomesticVrpConsentCreateFundsConfirmati private static async Task GetDomesticVrpConsentFundsConfirmationRequest( string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string amount, string referenceName, VariableRecurringPaymentsApiSettings variableRecurringPaymentsApiSettings) @@ -361,11 +361,14 @@ private static async Task GetDomesti ModifiedBy = "placeholder" // logging placeholder }; - await vrpFluentRequestLogging - .AppendToPath("domesticVrpConsent") - .AppendToPath("fundsConfirmation") - .AppendToPath("postRequest") - .WriteFile(domesticVrpConsentFundsConfirmationRequest); + if (vrpFluentRequestLogging is not null) + { + await vrpFluentRequestLogging + .AppendToPath("domesticVrpConsent") + .AppendToPath("fundsConfirmation") + .AppendToPath("postRequest") + .WriteFile(domesticVrpConsentFundsConfirmationRequest); + } domesticVrpConsentFundsConfirmationRequest.ExternalApiRequest.Data.InstructedAmount.Amount = amount; // replace logging placeholder @@ -379,7 +382,7 @@ await vrpFluentRequestLogging private static async Task GetDomesticVrpRequest( Guid domesticVrpConsentId, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -464,10 +467,13 @@ private static async Task GetDomesticVrpRequest( ExternalApiRequest = externalApiRequest, ModifiedBy = "placeholder" // logging placeholder }; - await vrpFluentRequestLogging - .AppendToPath("domesticVrp") - .AppendToPath("postRequest") - .WriteFile(domesticVrpRequest); + if (vrpFluentRequestLogging is not null) + { + await vrpFluentRequestLogging + .AppendToPath("domesticVrp") + .AppendToPath("postRequest") + .WriteFile(domesticVrpRequest); + } domesticVrpRequest.DomesticVrpConsentId = domesticVrpConsentId; // replace logging placeholder domesticVrpRequest.ModifiedBy = modifiedBy; // replace logging placeholder @@ -520,7 +526,7 @@ private static async Task GetDomesticVrpConsentReques Guid bankRegistrationId, string testNameUnique, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string referenceName, PaymentsEnv paymentsEnv, VariableRecurringPaymentsApiSettings variableRecurringPaymentsApiSettings) @@ -601,10 +607,13 @@ private static async Task GetDomesticVrpConsentReques CreatedBy = "placeholder" // logging placeholder }; - await vrpFluentRequestLogging - .AppendToPath("domesticVrpConsent") - .AppendToPath("postRequest") - .WriteFile(domesticVrpConsentRequest); + if (vrpFluentRequestLogging is not null) + { + await vrpFluentRequestLogging + .AppendToPath("domesticVrpConsent") + .AppendToPath("postRequest") + .WriteFile(domesticVrpConsentRequest); + } domesticVrpConsentRequest.BankRegistrationId = bankRegistrationId; // replace logging placeholder domesticVrpConsentRequest.ExternalApiRequest.Data.Initiation.CreditorAccount!.SchemeName = From ae683d28e0e164e527201c26cf6b90630c8efa10 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 14 Jun 2026 19:50:41 +0100 Subject: [PATCH 45/49] Replace "pass-through" errors with ExternalApiHttpRequestFailure response --- .../Fluent/HttpResponseException.cs | 3 ++- .../Http/ApiClient.cs | 25 ++++++++++++++----- .../Http/ExternalApiHttpErrorException.cs | 21 ---------------- .../Http/ExternalApiHttpRequestErrors.cs | 25 +++++++++++++++++++ .../ExternalApiHttpErrorExceptionFilter.cs | 21 +++------------- .../Http/ApiClientTests.cs | 5 ++-- 6 files changed, 53 insertions(+), 47 deletions(-) delete mode 100644 src/OpenBanking.Library.Connector/Http/ExternalApiHttpErrorException.cs diff --git a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs index 4b96e3aba..25953d8eb 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -14,7 +14,8 @@ public enum ServerErrorType ConsentFromRequestNotFound, IdTokenValidationError, ExternalApiHttpRequestIoError, - ExternalApiHttpRequestTimeout + ExternalApiHttpRequestTimeout, + ExternalApiHttpRequestFailure } public abstract record ServerError diff --git a/src/OpenBanking.Library.Connector/Http/ApiClient.cs b/src/OpenBanking.Library.Connector/Http/ApiClient.cs index 753dabcdc..c7b774cc2 100644 --- a/src/OpenBanking.Library.Connector/Http/ApiClient.cs +++ b/src/OpenBanking.Library.Connector/Http/ApiClient.cs @@ -14,6 +14,7 @@ using FinnovationLabs.OpenBanking.Library.Connector.Metrics; using Microsoft.Extensions.Http.Logging; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace FinnovationLabs.OpenBanking.Library.Connector.Http; @@ -306,12 +307,24 @@ private static SocketsHttpHandler CreatePrimaryHandler( if (!isSuccess) { - throw new ExternalApiHttpErrorException( - statusCode, - requestMethod, - requestUri, - responseBody, - xFapiInteractionId); + // Try to parse response body as JSON + object parsedResponseBody; + try + { + parsedResponseBody = JToken.Parse(responseBody); + } + catch (JsonException) + { + parsedResponseBody = responseBody; + } + + throw new HttpResponseException( + new ExternalApiHttpRequestFailure( + requestMethod, + requestUri, + statusCode, + parsedResponseBody, + xFapiInteractionId)); } return (statusCode, responseBody, xFapiInteractionId); diff --git a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpErrorException.cs b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpErrorException.cs deleted file mode 100644 index 9163ed1ab..000000000 --- a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpErrorException.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to Finnovation Labs Limited under one or more agreements. -// Finnovation Labs Limited licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace FinnovationLabs.OpenBanking.Library.Connector.Http; - -public class ExternalApiHttpErrorException : ExternalApiAccessException -{ - public ExternalApiHttpErrorException( - int responseStatusCode, - string requestHttpMethod, - string requestUrl, - string responseMessage, - string? xFapiInteractionId) : base( - "Pass-through error from external API endpoint", - responseStatusCode, - requestHttpMethod, - requestUrl, - responseMessage, - xFapiInteractionId) { } -} diff --git a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs index 352039b8e..3dd2b03d9 100644 --- a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs +++ b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs @@ -43,3 +43,28 @@ internal record ExternalApiHttpRequestTimeout( ["requestHttpMethod"] = RequestHttpMethod }; } + +internal record ExternalApiHttpRequestFailure( + string RequestHttpMethod, + string RequestUrl, + int ResponseStatusCode, + object ParsedResponseBody, + string? XFapiInteractionId) : ServerError +{ + public override ServerErrorType ServerErrorType => ServerErrorType.ExternalApiHttpRequestFailure; + public override string Title => "External API HTTP request failure"; + + public override string Detail => + $"An HTTP request to an external API endpoint returned an error response with HTTP status code {ResponseStatusCode}."; + + public override int StatusCode => 502; + + public override IReadOnlyDictionary Extensions { get; } = new Dictionary + { + ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), + ["requestHttpMethod"] = RequestHttpMethod, + ["responseStatusCode"] = ResponseStatusCode, + ["responseBody"] = ParsedResponseBody, + ["xFapiInteractionId"] = XFapiInteractionId + }; +} diff --git a/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs index bcc0c7338..412b61823 100644 --- a/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs +++ b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs @@ -18,25 +18,15 @@ public void OnActionExecuting(ActionExecutingContext context) { } public void OnActionExecuted(ActionExecutedContext context) { - if (context.Exception is ExternalApiAccessException httpResponseException) + if (context.Exception is ExternalApiResponseDeserialisationException httpResponseException) { - int statusCode; - if (context.Exception is ExternalApiResponseDeserialisationException ex) - { - statusCode = 500; - } - else - { - statusCode = httpResponseException.ResponseStatusCode; - } - var jsonObject = new JsonObject { ["title"] = httpResponseException.Message, ["detail"] = $"External API endpoint responded with HTTP status code {httpResponseException.ResponseStatusCode}. See properties " + "'endpointHttpMethod', 'endpointUrl' and 'endpointResponse' for more details.", - ["status"] = statusCode, + ["status"] = 500, ["endpointHttpMethod"] = httpResponseException.RequestHttpMethod, ["endpointUrl"] = httpResponseException.RequestUrl }; @@ -46,10 +36,7 @@ public void OnActionExecuted(ActionExecutedContext context) jsonObject["endpointFapiInteractionId"] = httpResponseException.XFapiInteractionId; } - if (context.Exception is ExternalApiResponseDeserialisationException ex2) - { - jsonObject["deserialisationError"] = ex2.DeserialisationErrorMessage; - } + jsonObject["deserialisationError"] = httpResponseException.DeserialisationErrorMessage; JsonNode? responseMessage; try @@ -71,7 +58,7 @@ public void OnActionExecuted(ActionExecutedContext context) { Content = jsonString, ContentType = mediaTypeHeaderValue.ToString(), - StatusCode = statusCode + StatusCode = 500 }; context.ExceptionHandled = true; diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs index ff81d5fad..9878cd5fb 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs @@ -4,6 +4,7 @@ using System.Net; using System.Text; +using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using FinnovationLabs.OpenBanking.Library.Connector.Http; using FinnovationLabs.OpenBanking.Library.Connector.Instrumentation; using Newtonsoft.Json; @@ -185,7 +186,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - await Assert.ThrowsAsync(a); + await Assert.ThrowsAsync(a); } @@ -222,7 +223,7 @@ await api.SendExpectingJsonResponseAsync( null, null); - await Assert.ThrowsAsync(a); + await Assert.ThrowsAsync(a); instrumentationClient.Received(1).Trace(Arg.Any()); } From 78ef399c547322c01c949f650ce5a9dff30a0baf Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 14 Jun 2026 20:43:30 +0100 Subject: [PATCH 46/49] Support exposeSuccessResponseBody for de-serialisation error responses --- .../Http/ApiClient.cs | 6 ++++-- .../ExternalApiDeserialisationException.cs | 6 +++++- .../Http/HttpRequestBuilder.cs | 6 ++++-- .../Http/IApiClient.cs | 3 ++- ...countAndTransactionPostRequestProcessor.cs | 3 ++- .../ExternalApi/ApiGetRequestProcessor.cs | 3 ++- .../AuthGrantPostRequestProcessor.cs | 3 ++- .../BankRegistrationGetRequestProcessor.cs | 3 ++- .../BankRegistrationPostRequestProcessor.cs | 3 ++- .../Operations/ExternalApi/GrantPost.cs | 10 +++++++-- .../PaymentInitiationPostRequestProcessor.cs | 3 ++- .../Operations/OpenIdConfigurationRead.cs | 6 +++++- .../ExternalApiHttpErrorExceptionFilter.cs | 21 +++++++++++-------- .../Http/ApiClientTests.cs | 12 +++++++---- 14 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/OpenBanking.Library.Connector/Http/ApiClient.cs b/src/OpenBanking.Library.Connector/Http/ApiClient.cs index c7b774cc2..0674f1b9b 100644 --- a/src/OpenBanking.Library.Connector/Http/ApiClient.cs +++ b/src/OpenBanking.Library.Connector/Http/ApiClient.cs @@ -69,7 +69,8 @@ public ApiClient(IInstrumentationClient instrumentationClient, HttpClient unwrap HttpRequestMessage request, string? requestContentForLog, TppReportingRequestInfo? tppReportingRequestInfo, - JsonSerializerSettings? jsonSerializerSettings) + JsonSerializerSettings? jsonSerializerSettings, + bool exposeSuccessResponseBodyInError) where T : class { ArgumentNullException.ThrowIfNull(request); @@ -97,7 +98,8 @@ public ApiClient(IInstrumentationClient instrumentationClient, HttpClient unwrap request.RequestUri!.ToString(), responseBody, xFapiInteractionId, - ex.Message); + ex.Message, + exposeSuccessResponseBodyInError); } if (responseBodyTyped is null) diff --git a/src/OpenBanking.Library.Connector/Http/ExternalApiDeserialisationException.cs b/src/OpenBanking.Library.Connector/Http/ExternalApiDeserialisationException.cs index ddb908d6e..34d35cb7e 100644 --- a/src/OpenBanking.Library.Connector/Http/ExternalApiDeserialisationException.cs +++ b/src/OpenBanking.Library.Connector/Http/ExternalApiDeserialisationException.cs @@ -12,7 +12,8 @@ public ExternalApiResponseDeserialisationException( string requestUrl, string responseMessage, string? xFapiInteractionId, - string deserialisationErrorMessage) : base( + string deserialisationErrorMessage, + bool exposeSuccessResponseBody) : base( "De-serialisation error when converting data from external API endpoint", responseStatusCode, requestHttpMethod, @@ -21,7 +22,10 @@ public ExternalApiResponseDeserialisationException( xFapiInteractionId) { DeserialisationErrorMessage = deserialisationErrorMessage; + ExposeSuccessResponseBody = exposeSuccessResponseBody; } public string DeserialisationErrorMessage { get; } + + public bool ExposeSuccessResponseBody { get; } } diff --git a/src/OpenBanking.Library.Connector/Http/HttpRequestBuilder.cs b/src/OpenBanking.Library.Connector/Http/HttpRequestBuilder.cs index cd686d2ff..ba7572d6c 100644 --- a/src/OpenBanking.Library.Connector/Http/HttpRequestBuilder.cs +++ b/src/OpenBanking.Library.Connector/Http/HttpRequestBuilder.cs @@ -81,7 +81,8 @@ public HttpRequestBuilder SetTextContent(string content, string contentType) public async Task<(T response, string? xFapiInteractionId)> SendExpectingJsonResponseAsync( IApiClient client, TppReportingRequestInfo? tppReportingRequestInfo, - JsonSerializerSettings? jsonSerializerSettings = null) + JsonSerializerSettings? jsonSerializerSettings, + bool exposeSuccessResponseBodyInError) where T : class { client.ArgNotNull(nameof(client)); @@ -97,7 +98,8 @@ public HttpRequestBuilder SetTextContent(string content, string contentType) message, requestData.RequestContentForLog, tppReportingRequestInfo, - jsonSerializerSettings); + jsonSerializerSettings, + exposeSuccessResponseBodyInError); } public async Task SendExpectingStringResponseAsync( diff --git a/src/OpenBanking.Library.Connector/Http/IApiClient.cs b/src/OpenBanking.Library.Connector/Http/IApiClient.cs index 03c5464f6..693392e48 100644 --- a/src/OpenBanking.Library.Connector/Http/IApiClient.cs +++ b/src/OpenBanking.Library.Connector/Http/IApiClient.cs @@ -13,7 +13,8 @@ public interface IApiClient HttpRequestMessage request, string? requestContentForLog, TppReportingRequestInfo? tppReportingRequestInfo, - JsonSerializerSettings? jsonSerializerSettings) + JsonSerializerSettings? jsonSerializerSettings, + bool exposeSuccessResponseBodyInError) where T : class; Task SendExpectingNoResponseAsync( diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/AccountAndTransaction/AccountAndTransactionPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/AccountAndTransaction/AccountAndTransactionPostRequestProcessor.cs index daa0a3797..365228b01 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/AccountAndTransaction/AccountAndTransactionPostRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/AccountAndTransaction/AccountAndTransactionPostRequestProcessor.cs @@ -61,7 +61,8 @@ public AccountAndTransactionPostRequestProcessor( .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - responseJsonSerializerSettings); + responseJsonSerializerSettings, + true); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/ApiGetRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/ApiGetRequestProcessor.cs index 53aa50ae2..9c51618d5 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/ApiGetRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/ApiGetRequestProcessor.cs @@ -49,7 +49,8 @@ public ApiGetRequestProcessor(string financialId, string accessToken) .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - jsonSerializerSettings); + jsonSerializerSettings, + true); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/AuthGrantPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/AuthGrantPostRequestProcessor.cs index 93c6999cd..946486214 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/AuthGrantPostRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/AuthGrantPostRequestProcessor.cs @@ -82,7 +82,8 @@ public AuthGrantPostRequestProcessor( .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - responseJsonSerializerSettings); + responseJsonSerializerSettings, + false); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationGetRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationGetRequestProcessor.cs index fc1e899b2..d75ff8ba1 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationGetRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationGetRequestProcessor.cs @@ -43,7 +43,8 @@ public BankRegistrationGetRequestProcessor(string accessToken) .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - jsonSerializerSettings); + jsonSerializerSettings, + false); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationPostRequestProcessor.cs index cf19c6c6f..2fda633aa 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationPostRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/BankConfiguration/BankRegistrationPostRequestProcessor.cs @@ -89,7 +89,8 @@ public BankRegistrationPostRequestProcessor( .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - responseJsonSerializerSettings); + responseJsonSerializerSettings, + false); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs index 16a19c918..8adbce843 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/GrantPost.cs @@ -665,9 +665,15 @@ private async Task GetJwksAsync( { Keys = (await message.SendExpectingJsonResponseAsync>( _apiClient, - tppReportingRequestInfo)).response + tppReportingRequestInfo, + null, + true)).response } - : (await message.SendExpectingJsonResponseAsync(_apiClient, tppReportingRequestInfo)).response; + : (await message.SendExpectingJsonResponseAsync( + _apiClient, + tppReportingRequestInfo, + null, + true)).response; return jwks; } diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs index d69113f5a..46a4b684a 100644 --- a/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs +++ b/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs @@ -102,7 +102,8 @@ public PaymentInitiationPostRequestProcessor( .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - responseJsonSerializerSettings); + responseJsonSerializerSettings, + true); return (response, xFapiInteractionId); } diff --git a/src/OpenBanking.Library.Connector/Operations/OpenIdConfigurationRead.cs b/src/OpenBanking.Library.Connector/Operations/OpenIdConfigurationRead.cs index e3968aca2..7243a2fe3 100644 --- a/src/OpenBanking.Library.Connector/Operations/OpenIdConfigurationRead.cs +++ b/src/OpenBanking.Library.Connector/Operations/OpenIdConfigurationRead.cs @@ -60,7 +60,11 @@ public async (OpenIdConfiguration openIdConfiguration, string? xFapiInteractionId) = await new HttpRequestBuilder() .SetMethod(HttpMethod.Get) .SetUri(openIdConfigurationUrl) - .SendExpectingJsonResponseAsync(_apiClient, tppReportingRequestInfo); + .SendExpectingJsonResponseAsync( + _apiClient, + tppReportingRequestInfo, + null, + true); // Update OpenID Provider Configuration based on overrides IList? responseModesSupportedOverride = diff --git a/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs index 412b61823..2f8b121d0 100644 --- a/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs +++ b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs @@ -38,17 +38,20 @@ public void OnActionExecuted(ActionExecutedContext context) jsonObject["deserialisationError"] = httpResponseException.DeserialisationErrorMessage; - JsonNode? responseMessage; - try + if (httpResponseException.ExposeSuccessResponseBody) { - responseMessage = JsonNode.Parse(httpResponseException.ResponseMessage); - } - catch - { - responseMessage = httpResponseException.ResponseMessage; - } + JsonNode? responseMessage; + try + { + responseMessage = JsonNode.Parse(httpResponseException.ResponseMessage); + } + catch (JsonException) + { + responseMessage = httpResponseException.ResponseMessage; + } - jsonObject["endpointResponse"] = responseMessage; + jsonObject["endpointResponse"] = responseMessage; + } string jsonString = JsonSerializer.Serialize(jsonObject); diff --git a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs index 9878cd5fb..d28806277 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs @@ -116,7 +116,8 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); Assert.Equal(entity.Message, result.Message); } @@ -147,7 +148,8 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); await Assert.ThrowsAsync(a); } @@ -184,7 +186,8 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); await Assert.ThrowsAsync(a); } @@ -221,7 +224,8 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); await Assert.ThrowsAsync(a); instrumentationClient.Received(1).Trace(Arg.Any()); From 4fcb2033255cfc4193397500479e8fc0b2c2f41c Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 14 Jun 2026 21:09:59 +0100 Subject: [PATCH 47/49] Remove URL filtering for HTTP request errors --- .../Http/ExternalApiHttpRequestErrors.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs index 3dd2b03d9..4ae8ce07f 100644 --- a/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs +++ b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.cs @@ -22,7 +22,7 @@ internal record ExternalApiHttpRequestIoError( public override IReadOnlyDictionary Extensions { get; } = new Dictionary { - ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), + ["requestUrl"] = RequestUrl, ["requestHttpMethod"] = RequestHttpMethod, ["httpRequestError"] = HttpRequestError.ToString().ToCamelCase() }; @@ -39,7 +39,7 @@ internal record ExternalApiHttpRequestTimeout( public override IReadOnlyDictionary Extensions { get; } = new Dictionary { - ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), + ["requestUrl"] = RequestUrl, ["requestHttpMethod"] = RequestHttpMethod }; } @@ -59,12 +59,21 @@ internal record ExternalApiHttpRequestFailure( public override int StatusCode => 502; - public override IReadOnlyDictionary Extensions { get; } = new Dictionary - { - ["requestUrlWithoutQuery"] = new Uri(RequestUrl).GetLeftPart(UriPartial.Path), - ["requestHttpMethod"] = RequestHttpMethod, - ["responseStatusCode"] = ResponseStatusCode, - ["responseBody"] = ParsedResponseBody, - ["xFapiInteractionId"] = XFapiInteractionId - }; + public override IReadOnlyDictionary Extensions { get; } = + XFapiInteractionId is null + ? new Dictionary + { + ["requestUrl"] = RequestUrl, + ["requestHttpMethod"] = RequestHttpMethod, + ["responseStatusCode"] = ResponseStatusCode, + ["responseBody"] = ParsedResponseBody + } + : new Dictionary + { + ["requestUrl"] = RequestUrl, + ["requestHttpMethod"] = RequestHttpMethod, + ["responseStatusCode"] = ResponseStatusCode, + ["responseBody"] = ParsedResponseBody, + ["xFapiInteractionId"] = XFapiInteractionId + }; } From dba3b0fc2426001618992790f8933344c658c7c5 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Sun, 14 Jun 2026 22:38:47 +0100 Subject: [PATCH 48/49] Add M&S bank to HSBC --- docs/bank-integrations.md | 2 +- .../BankProfiles/BankGroups/Hsbc.cs | 6 ++- .../BankProfiles/BankProfileEnum.cs | 3 ++ .../BankProfiles/BankProfileExtensions.cs | 1 + .../BankProfiles/Generators/HsbcGenerator.cs | 37 ++++++++++++------- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/bank-integrations.md b/docs/bank-integrations.md index 1e3ed4987..7b2f01fd0 100644 --- a/docs/bank-integrations.md +++ b/docs/bank-integrations.md @@ -9,7 +9,7 @@ UK Open Banking uses the [Financial-grade API](https://openid.net/specs/openid-f | Barclays | Personal
Wealth
Barclaycard
Business
Corporate
Barclaycard Commercial Payments | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Barclays_Personal
Barclays_Wealth
Barclays_Barclaycard
Barclays_Business
Barclays_Corporate
Barclays_BarclaycardCommercialPayments | AISP,
PISP (domestic payments),
VRP | | Chase | All brands | :white_large_square: | Chase_Chase | AISP,
PISP (domestic payments) | | Co-operative | Sandbox
Co-operative
Smile | :white_check_mark:
:white_large_square:
:white_large_square: | Cooperative_CooperativeSandbox
Cooperative_Cooperative
Cooperative_Smile | AISP | -| HSBC | FirstDirect
UK Personal
UK Business
UK Kinetic
HSBCnet UK | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Hsbc_FirstDirect
Hsbc_UkPersonal
Hsbc_UkBusiness
Hsbc_UkKinetic
Hsbc_HsbcNetUk | AISP,
PISP (domestic payments),
VRP | +| HSBC | FirstDirect
UK Personal
UK Business
UK Kinetic
HSBCnet UK
M&S Bank | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Hsbc_FirstDirect
Hsbc_UkPersonal
Hsbc_UkBusiness
Hsbc_UkKinetic
Hsbc_HsbcNetUk
Hsbc_MAndS *(AISP only)* | AISP,
PISP (domestic payments),
VRP | | Lloyds | Lloyds Personal
Lloyds Business
Lloyds Commercial
Halifax Personal
Bank of Scotland Personal
Bank of Scotland Business
Bank of Scotland Commercial
MBNA Personal | :white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square:
:white_large_square: | Lloyds_LloydsPersonal
Lloyds_LloydsBusiness
Lloyds_LloydsCommerical
Lloyds_HalifaxPersonal
Lloyds_BankOfScotlandPersonal
Lloyds_BankOfScotlandBusiness
Lloyds_BankOfScotlandCommerical
Lloyds_MbnaPersonal *(no VRPs)* | AISP,
PISP (domestic payments),
VRP | | Monzo | All brands | :white_large_square: | Monzo_Monzo | AISP,
PISP (domestic payments),
VRP | | Nationwide | Nationwide
Virgin (Merged APIs) | :white_large_square:
:white_large_square: | Nationwide_Nationwide
Nationwide_VirginMerged *(no VRPs)* | AISP,
PISP (domestic payments),
VRP | diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Hsbc.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Hsbc.cs index a009c7536..2e328066b 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Hsbc.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/Hsbc.cs @@ -14,7 +14,8 @@ public enum HsbcBank UkBusiness, UkKinetic, UkPersonal, - HsbcNetUk + HsbcNetUk, + MAndS } public class Hsbc() : BankGroupBase(BankGroup.Hsbc) @@ -27,7 +28,8 @@ public class Hsbc() : BankGroupBase(BankGroup.H [BankProfileEnum.Hsbc_UkBusiness] = HsbcBank.UkBusiness, [BankProfileEnum.Hsbc_UkKinetic] = HsbcBank.UkKinetic, [BankProfileEnum.Hsbc_UkPersonal] = HsbcBank.UkPersonal, - [BankProfileEnum.Hsbc_HsbcNetUk] = HsbcBank.HsbcNetUk + [BankProfileEnum.Hsbc_HsbcNetUk] = HsbcBank.HsbcNetUk, + [BankProfileEnum.Hsbc_MAndS] = HsbcBank.MAndS }; public override HsbcRegistrationGroup GetRegistrationGroup( diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index ddbf3c238..c4f4558d1 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs @@ -70,6 +70,9 @@ public enum BankProfileEnum [EnumMember(Value = "Hsbc_HsbcNetUk")] Hsbc_HsbcNetUk, + [EnumMember(Value = "Hsbc_MAndS")] + Hsbc_MAndS, + [EnumMember(Value = "Lloyds_Sandbox")] Lloyds_Sandbox, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs index 72cb56c48..f58853279 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileExtensions.cs @@ -31,6 +31,7 @@ public static BankGroup GetBankGroup(this BankProfileEnum bankProfileEnum) => BankProfileEnum.Hsbc_UkKinetic => BankGroup.Hsbc, BankProfileEnum.Hsbc_UkPersonal => BankGroup.Hsbc, BankProfileEnum.Hsbc_HsbcNetUk => BankGroup.Hsbc, + BankProfileEnum.Hsbc_MAndS => BankGroup.Hsbc, BankProfileEnum.Lloyds_Sandbox => BankGroup.Lloyds, BankProfileEnum.Lloyds_LloydsPersonal => BankGroup.Lloyds, BankProfileEnum.Lloyds_LloydsBusiness => BankGroup.Lloyds, diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs index 3d66f46ec..0b57991db 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/HsbcGenerator.cs @@ -50,26 +50,35 @@ public override BankProfile GetBankProfile( HsbcBank.HsbcNetUk => // from: https://develop.hsbc.com/sites/default/files/open_banking/HSBC%20Open%20Banking%20TPP%20Implementation%20Guide%20(v3.1).pdf "https://api.ob.hsbcnet.com", + HsbcBank.MAndS => + // from: https://develop.hsbc.com/ob-api-documentation/account-information-ms-bank + "https://api.ob.mandsbank.com", _ => throw new ArgumentOutOfRangeException() }; return new BankProfile( _bankGroupData.GetBankProfile(bank), issuerUrl, GetFinancialId(bank), - GetAccountAndTransactionApi(bank), + bank is not HsbcBank.MAndS ? GetAccountAndTransactionApi(bank) : null, GetAccountAndTransactionV4Api(bank), - new PaymentInitiationApi { BaseUrl = GetPaymentsBaseUrl(bank, "v3.1") }, - new PaymentInitiationApi - { - BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), - ApiVersion = PaymentInitiationApiVersion.Version4p0 - }, - new VariableRecurringPaymentsApi { BaseUrl = GetPaymentsBaseUrl(bank, "v3.1") }, - new VariableRecurringPaymentsApi - { - BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), - ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 - }, + bank is not HsbcBank.MAndS ? new PaymentInitiationApi { BaseUrl = GetPaymentsBaseUrl(bank, "v3.1") } : null, + bank is not HsbcBank.MAndS + ? new PaymentInitiationApi + { + BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), + ApiVersion = PaymentInitiationApiVersion.Version4p0 + } + : null, + bank is not HsbcBank.MAndS + ? new VariableRecurringPaymentsApi { BaseUrl = GetPaymentsBaseUrl(bank, "v3.1") } + : null, + bank is not HsbcBank.MAndS + ? new VariableRecurringPaymentsApi + { + BaseUrl = GetPaymentsBaseUrl(bank, "v4.0"), + ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0 + } + : null, bank is not HsbcBank.Sandbox, instrumentationClient) { @@ -199,6 +208,8 @@ public override BankProfile GetBankProfile( "https://api.ob.hsbc.co.uk/obie/open-banking/v4.0/aisp", HsbcBank.HsbcNetUk => "https://api.ob.hsbcnet.com/obie/open-banking/v4.0/aisp", + HsbcBank.MAndS => + "https://api.ob.mandsbank.com/obie/open-banking/v4.0/aisp", _ => throw new ArgumentOutOfRangeException() } }; From 149b72b47d403964b20bf8e627bb08de1fc4c6f5 Mon Sep 17 00:00:00 2001 From: Mark Murphy <26673915+markm77@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:23:20 +0100 Subject: [PATCH 49/49] [Docs] Update docs --- docs/apis/account-and-transaction/openapi-aisp.json | 2 +- docs/apis/auth-contexts/openapi-auth.json | 2 +- docs/apis/management/openapi-manage.json | 3 ++- docs/apis/payment-initiation/openapi-pisp.json | 2 +- docs/apis/variable-recurring-payments/openapi-vrp.json | 2 +- .../OpenBanking.WebApp.Connector.csproj | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index 1b4570b65..a04363394 100644 --- a/docs/apis/account-and-transaction/openapi-aisp.json +++ b/docs/apis/account-and-transaction/openapi-aisp.json @@ -3,7 +3,7 @@ "info": { "title": "Account and Transaction API", "description": "Account and Transaction API for Open Banking Connector Web App", - "version": "19.0.0" + "version": "20.0.0" }, "paths": { "/aisp/account-access-consent-auth-contexts": { diff --git a/docs/apis/auth-contexts/openapi-auth.json b/docs/apis/auth-contexts/openapi-auth.json index 1cb0c5b79..8af98e23e 100644 --- a/docs/apis/auth-contexts/openapi-auth.json +++ b/docs/apis/auth-contexts/openapi-auth.json @@ -3,7 +3,7 @@ "info": { "title": "Auth Contexts API", "description": "Auth Contexts API for Open Banking Connector Web App", - "version": "19.0.0" + "version": "20.0.0" }, "paths": { "/auth/redirect-delegate": { diff --git a/docs/apis/management/openapi-manage.json b/docs/apis/management/openapi-manage.json index 217afa3d8..f912eb46f 100644 --- a/docs/apis/management/openapi-manage.json +++ b/docs/apis/management/openapi-manage.json @@ -3,7 +3,7 @@ "info": { "title": "Management API", "description": "Management API for Open Banking Connector Web App", - "version": "19.0.0" + "version": "20.0.0" }, "paths": { "/manage/bank-registrations": { @@ -756,6 +756,7 @@ "Hsbc_UkKinetic", "Hsbc_UkPersonal", "Hsbc_HsbcNetUk", + "Hsbc_MAndS", "Lloyds_Sandbox", "Lloyds_LloydsPersonal", "Lloyds_LloydsBusiness", diff --git a/docs/apis/payment-initiation/openapi-pisp.json b/docs/apis/payment-initiation/openapi-pisp.json index 6c471f057..36973817c 100644 --- a/docs/apis/payment-initiation/openapi-pisp.json +++ b/docs/apis/payment-initiation/openapi-pisp.json @@ -3,7 +3,7 @@ "info": { "title": "Payment Initiation API", "description": "Payment Initiation API for Open Banking Connector Web App", - "version": "19.0.0" + "version": "20.0.0" }, "paths": { "/pisp/domestic-payment-consent-auth-contexts": { diff --git a/docs/apis/variable-recurring-payments/openapi-vrp.json b/docs/apis/variable-recurring-payments/openapi-vrp.json index d3165ef34..2bfbaaf2c 100644 --- a/docs/apis/variable-recurring-payments/openapi-vrp.json +++ b/docs/apis/variable-recurring-payments/openapi-vrp.json @@ -3,7 +3,7 @@ "info": { "title": "Variable Recurring Payments API", "description": "Variable Recurring Payments API for Open Banking Connector Web App", - "version": "19.0.0" + "version": "20.0.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index 41a4d12ad..513402721 100644 --- a/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj +++ b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj @@ -7,7 +7,7 @@ aa921213-9461-4f9e-8fec-153624ec67ad FinnovationLabs.OpenBanking.WebApp.Connector FinnovationLabs.OpenBanking.WebApp.Connector - 19.0.0 + 20.0.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false