diff --git a/docs/apis/account-and-transaction/openapi-aisp.json b/docs/apis/account-and-transaction/openapi-aisp.json index b8f8dfb5e..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": "18.2.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 6456bce83..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": "18.2.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 f6694b022..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": "18.2.0" + "version": "20.0.0" }, "paths": { "/manage/bank-registrations": { @@ -730,7 +730,9 @@ "Santander", "Starling", "Tide", - "Tsb" + "Tsb", + "Wise", + "Zopa" ], "type": "string" }, @@ -754,6 +756,7 @@ "Hsbc_UkKinetic", "Hsbc_UkPersonal", "Hsbc_HsbcNetUk", + "Hsbc_MAndS", "Lloyds_Sandbox", "Lloyds_LloydsPersonal", "Lloyds_LloydsBusiness", @@ -766,6 +769,7 @@ "Monzo_Monzo", "Monzo_Sandbox", "Nationwide_Nationwide", + "Nationwide_VirginMerged", "NatWest_NatWestSandbox", "NatWest_NatWest", "NatWest_NatWestBankline", @@ -790,7 +794,9 @@ "Santander_Corporate", "Starling_Starling", "Tide_Tide", - "Tsb_Tsb" + "Tsb_Tsb", + "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 1a2a2fa42..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": "18.2.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 9300585b5..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": "18.2.0" + "version": "20.0.0" }, "paths": { "/vrp/domestic-vrp-consent-auth-contexts": { diff --git a/docs/bank-integrations.md b/docs/bank-integrations.md index 1222991ac..7b2f01fd0 100644 --- a/docs/bank-integrations.md +++ b/docs/bank-integrations.md @@ -9,14 +9,16 @@ 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 | -| 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 | +| 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 | 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) | \ 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) | +| Zopa | All brands | :white_large_square: | Zopa_Zopa | PISP (domestic payments) | 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/OpenBanking.Library.BankApiModels.csproj b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj index 87254ad55..86823b679 100644 --- a/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj +++ b/src/OpenBanking.Library.BankApiModels/OpenBanking.Library.BankApiModels.csproj @@ -23,9 +23,9 @@ - + - + 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.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 e4d17a85c..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,14 +24,19 @@ - - - - - - - - + + + + + + + + + + + + + diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs b/src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs deleted file mode 100644 index 4af539ecd..000000000 --- a/src/OpenBanking.Library.Connector.Web/Controllers/AuthContext/TestingRedirectsController.cs +++ /dev/null @@ -1,181 +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 System.ComponentModel.DataAnnotations; -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; - -[ApiController] -[ApiExplorerSettings(GroupName = "test")] -[Tags("Post-Auth Redirects")] -[Route("auth")] -public class TestingRedirectsController : ControllerBase -{ - private readonly IRequestBuilder _requestBuilder; - - public TestingRedirectsController(IRequestBuilder requestBuilder) - { - _requestBuilder = requestBuilder; - } - - /// - /// OAuth2 query redirect endpoint - /// - /// - /// - /// - /// - /// - [HttpGet("query-redirect")] - public async Task QueryRedirectGetAsync( - [Required] [FromQuery(Name = "state")] - string state, - [FromQuery(Name = "code")] - string? code, - [FromQuery(Name = "id_token")] - string? idToken, - [FromQuery(Name = "error")] - string? error) - { - 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"); - } - 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); - } - - return Ok(); // We do not return data to bank following query redirect - } - - /// - /// Delegate endpoint for forwarding data captured elsewhere from OAuth2 redirect - /// - /// - /// - /// - /// - /// - /// - /// - /// - [HttpPost("fragment-redirect-delegate")] - [Consumes("application/x-www-form-urlencoded")] - [ProducesResponseType(StatusCodes.Status201Created)] - public async Task> FragmentRedirectDelegatePostAsync( - [Required] [FromForm(Name = "state")] - string state, - [FromForm(Name = "code")] - string? code, - [FromForm(Name = "id_token")] - string? idToken, - [FromForm(Name = "error")] - string? error, - [FromForm(Name = "response_mode")] - string? responseMode, - [FromForm(Name = "redirect_uri")] - string? redirectUrl) - { - // Check for cookie - 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); - } - 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)); - } - - var authResult = - new TestingAuthResult - { - State = state, - RedirectParameters = formCollection - }; - fluentResponse = await TestingMethods.Instance.ProcessRedirect(authResult); - } - - return Created("about:blank", fluentResponse); - } -} 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/Extensions/ServiceCollectionExtensions.cs b/src/OpenBanking.Library.Connector.Web/Extensions/ServiceCollectionExtensions.cs index b699bc558..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,171 +11,16 @@ 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(); + services.AddHostedService( + sp => serviceVersion is not null + ? ActivatorUtilities.CreateInstance(sp, serviceVersion) + : ActivatorUtilities.CreateInstance(sp)); services.AddHealthChecks(); 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/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:"))); }); 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 5d298e78a..26cf0482e 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,22 +24,10 @@ - - - - - - - - - - - - - + diff --git a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs index efefcee45..bb29824c0 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroup.cs @@ -54,5 +54,11 @@ public enum BankGroup Tide, [EnumMember(Value = "Tsb")] - Tsb + Tsb, + + [EnumMember(Value = "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 f03d766f4..cefd67fc2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankGroups/BankGroupExtensions.cs @@ -24,7 +24,9 @@ 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(), + [BankGroup.Zopa] = new Zopa() }; public static IBankGroupData GetBankGroupData(this BankGroup bankGroup) 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/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/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/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/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/BankProfile.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfile.cs index 9d2ce899f..12688df8a 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); @@ -141,6 +136,8 @@ public AccountAccessConsentExternalApiRequestAdjustments AccountAccessConsentTemplateExternalApiRequestAdjustments { get; set; } = x => x; public bool UseBalancesNotAccountEndpointInSecondSession { get; init; } + + public int AccountAccessConsentPostCreateDelaySeconds { get; init; } = 0; } public class PaymentInitiationApiSettings @@ -306,8 +303,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/BankProfileEnum.cs b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileEnum.cs index fc797e4ce..c4f4558d1 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, @@ -69,6 +70,9 @@ public enum BankProfileEnum [EnumMember(Value = "Hsbc_HsbcNetUk")] Hsbc_HsbcNetUk, + [EnumMember(Value = "Hsbc_MAndS")] + Hsbc_MAndS, + [EnumMember(Value = "Lloyds_Sandbox")] Lloyds_Sandbox, @@ -105,6 +109,9 @@ public enum BankProfileEnum [EnumMember(Value = "Nationwide_Nationwide")] Nationwide_Nationwide, + [EnumMember(Value = "Nationwide_VirginMerged")] + Nationwide_VirginMerged, + [EnumMember(Value = "NatWest_NatWestSandbox")] NatWest_NatWestSandbox, @@ -178,5 +185,11 @@ public enum BankProfileEnum Tide_Tide, [EnumMember(Value = "Tsb_Tsb")] - Tsb_Tsb + Tsb_Tsb, + + [EnumMember(Value = "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 d93456c7d..f58853279 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, @@ -29,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, @@ -41,6 +44,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, @@ -66,6 +70,8 @@ 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, + 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 f724c0dbe..cf05bfda2 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/BankProfileService.cs @@ -55,7 +55,9 @@ 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), + [BankGroup.Zopa] = new ZopaGenerator(bankProfilesSettingsProvider) }; } @@ -82,6 +84,8 @@ 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), + BankGroup.Zopa => GetBankProfile(profileEnum, _instrumentationClient), _ => throw new ArgumentOutOfRangeException() }, LazyThreadSafetyMode.ExecutionAndPublication)) 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/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/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..6bd6509bf 100644 --- a/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs +++ b/src/OpenBanking.Library.Connector/BankProfiles/CustomBehaviour/IdTokenProcessingCustomBehaviour.cs @@ -3,12 +3,16 @@ // 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 { - public bool? DoNotValidateIdToken { get; set; } + /// + /// ID token "sub" claim type. + /// + public IdTokenSubClaimType? IdTokenSubClaimType { get; set; } public bool? IdTokenNonceClaimIsPreviousValue { get; set; } @@ -25,4 +29,53 @@ public class IdTokenProcessingCustomBehaviour public string? IssClaim { get; set; } public DateTimeOffsetUnixConverterEnum? IdTokenExpirationTimeClaimJsonConverter { get; set; } + + 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/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/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/BankProfiles/Generators/BarclaysGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/BarclaysGenerator.cs index fc297da67..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,14 +68,13 @@ public override BankProfile GetBankProfile( ApiVersion = VariableRecurringPaymentsApiVersion.Version4p0, BaseUrl = GetApiBaseUrl("v4.0/pisp") }, - bank is not BarclaysBank.Sandbox, + true, instrumentationClient) { BankConfigurationApiSettings = new BankConfigurationApiSettings { UseRegistrationEndpoint = false, - TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt, - IdTokenSubClaimType = IdTokenSubClaimType.EndUserId + TokenEndpointAuthMethod = TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt }, AccountAndTransactionApiSettings = new AccountAndTransactionApiSettings { @@ -127,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) { @@ -151,22 +135,10 @@ or BarclaysBank.BarclaycardCommercialPayments }, CustomBehaviour = new CustomBehaviourClass { + BaseIdTokenProcessingCustomBehaviour = + 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 = @@ -199,9 +171,7 @@ or BarclaysBank.BarclaycardCommercialPayments DomesticVrp = new DomesticVrpCustomBehaviour { PostResponseLinksMayOmitId = true, - PreferMisspeltContractPresentIndicator = true, - ResponseDataStatusMayBeMissingOrWrong = true, - ResponseDataRefundMayBeMissingOrWrong = true + PreferMisspeltContractPresentIndicator = true } }, VariableRecurringPaymentsApiSettings = new VariableRecurringPaymentsApiSettings @@ -213,9 +183,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..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 = @@ -95,8 +96,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..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) { @@ -150,16 +159,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 @@ -209,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() } }; diff --git a/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs b/src/OpenBanking.Library.Connector/BankProfiles/Generators/LloydsGenerator.cs index 0189660c4..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) { @@ -135,6 +137,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 +231,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 { @@ -247,19 +250,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..56618c14e 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) { @@ -122,9 +120,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 +156,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 @@ -176,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, @@ -219,35 +231,61 @@ 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 { 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 = @@ -260,7 +298,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 @@ -269,36 +307,17 @@ 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 } }, - 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..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,18 +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 }, - AspspBrandId = 12, - 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) + }; } 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 c7a357119..f3179cf99 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 => @@ -139,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..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 }, @@ -148,8 +149,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 d59b77306..60043d1f4 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,39 @@ 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 } }, - DefaultResponseMode = OAuth2ResponseMode.Query, - AspspBrandId = 1510 + PaymentInitiationApiSettings = new PaymentInitiationApiSettings + { + PreferPartyToPartyPaymentContextCode = true, + UseContractPresentIndicator = false + }, + DefaultResponseMode = OAuth2ResponseMode.Query }; - 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 } 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..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 = @@ -82,8 +83,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 new file mode 100644 index 000000000..d5d88ed11 --- /dev/null +++ b/src/OpenBanking.Library.Connector/BankProfiles/Generators/WiseGenerator.cs @@ -0,0 +1,118 @@ +// 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 + }; + } + + 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/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/Extensions/StringExtensions.cs b/src/OpenBanking.Library.Connector/Extensions/StringExtensions.cs index 507bc22cc..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; @@ -23,4 +24,34 @@ 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..]; + } + + 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 b8405c4c5..25953d8eb 100644 --- a/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs +++ b/src/OpenBanking.Library.Connector/Fluent/HttpResponseException.cs @@ -2,26 +2,41 @@ // 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 + AuthContextStale, + ConsentNotFound, + ConsentFromRequestNotFound, + IdTokenValidationError, + ExternalApiHttpRequestIoError, + ExternalApiHttpRequestTimeout, + ExternalApiHttpRequestFailure +} + +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/GlobalUsings.cs b/src/OpenBanking.Library.Connector/GlobalUsings.cs index 7e91a9519..b0a475d53 100644 --- a/src/OpenBanking.Library.Connector/GlobalUsings.cs +++ b/src/OpenBanking.Library.Connector/GlobalUsings.cs @@ -34,8 +34,16 @@ 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; +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/Http/ApiClient.cs b/src/OpenBanking.Library.Connector/Http/ApiClient.cs index c50e4a22b..0674f1b9b 100644 --- a/src/OpenBanking.Library.Connector/Http/ApiClient.cs +++ b/src/OpenBanking.Library.Connector/Http/ApiClient.cs @@ -9,10 +9,12 @@ 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; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace FinnovationLabs.OpenBanking.Library.Connector.Http; @@ -67,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); @@ -91,11 +94,12 @@ public ApiClient(IInstrumentationClient instrumentationClient, HttpClient unwrap { throw new ExternalApiResponseDeserialisationException( statusCode, - $"{request.Method}", - $"{request.RequestUri}", + request.Method.ToString(), + request.RequestUri!.ToString(), responseBody, xFapiInteractionId, - ex.Message); + ex.Message, + exposeSuccessResponseBodyInError); } if (responseBodyTyped is null) @@ -113,7 +117,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 +134,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 +219,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,88 +232,143 @@ 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) + { + throw new HttpResponseException( + new ExternalApiHttpRequestIoError(requestMethod, requestUri, httpIoException.HttpRequestError)); + } + + if (ex is TaskCanceledException { InnerException: TimeoutException }) { - 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 ExternalApiHttpRequestTimeout(requestMethod, requestUri)); } - // Log request - _httpRequestLogger.LogRequestFailed( - new HttpRequestLoggerAdditionalData - { - RequestBody = requestContentForLog, - ResponseBody = responseBody - }, + 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)) + response.Dispose(); + + if (!isSuccess) { - xFapiInteractionId = values.First(); + // 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)); } - // Check HTTP status code - var statusCode = (int) response.StatusCode; - if (!response.IsSuccessStatusCode) + 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) { - throw new ExternalApiHttpErrorException( - statusCode, - $"{request.Method}", - $"{request.RequestUri}", - responseBody, - xFapiInteractionId); + 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); + } } - return (statusCode, responseBody, xFapiInteractionId); + // Log request + _httpRequestLogger.LogRequestFailed( + new HttpRequestLoggerAdditionalData + { + RequestBody = requestContentForLog, + ResponseBody = null + }, + request, + response, + ex, + elapsed); } private void UpdateTppReportingMetrics( @@ -354,14 +412,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/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/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 new file mode 100644 index 000000000..4ae8ce07f --- /dev/null +++ b/src/OpenBanking.Library.Connector/Http/ExternalApiHttpRequestErrors.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; + +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 + { + ["requestUrl"] = RequestUrl, + ["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 + { + ["requestUrl"] = RequestUrl, + ["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; } = + 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 + }; +} 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/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/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.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/OpenBanking.Library.Connector.csproj b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj index dda8b74e9..2d208fa99 100644 --- a/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj +++ b/src/OpenBanking.Library.Connector/OpenBanking.Library.Connector.csproj @@ -23,17 +23,17 @@ - + - - - - - + + + + + - + 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..9255ebb2b 100644 --- a/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs +++ b/src/OpenBanking.Library.Connector/Operations/AccountAndTransaction/AccountAccessConsentCommon.cs @@ -2,11 +2,11 @@ // 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; 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 +50,8 @@ public async SoftwareStatementEntity softwareStatement, ExternalApiSecretEntity? externalApiSecret)> GetAccountAccessConsent( Guid consentId, - bool dbTracking) + bool dbTracking, + ConsentIdSource consentIdSource) { IQueryable db = dbTracking ? _entityMethods.DbSet @@ -68,7 +69,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 +85,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); @@ -199,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/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 7d38a6a5f..4b44bbfb2 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; @@ -61,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); @@ -75,7 +77,6 @@ public AccountGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -87,13 +88,10 @@ public AccountGet( (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, @@ -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..1af5ea818 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; @@ -61,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); @@ -75,7 +77,6 @@ public BalanceGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -87,13 +88,10 @@ public BalanceGet( (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, @@ -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..e88b87598 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; @@ -64,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); @@ -78,7 +80,6 @@ public DirectDebitGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -90,13 +91,10 @@ public DirectDebitGet( (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, @@ -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..711cd246d 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; @@ -168,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); @@ -182,7 +184,6 @@ public MonzoPotGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -194,13 +195,10 @@ public MonzoPotGet( (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, @@ -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..03e1712d0 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; @@ -61,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); @@ -75,7 +77,6 @@ public Party2Get( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -87,13 +88,10 @@ public Party2Get( (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, @@ -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..5280d0ce7 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; @@ -61,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); @@ -75,7 +77,6 @@ public PartyGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -87,13 +88,10 @@ public PartyGet( (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, @@ -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..36a50e04f 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; @@ -61,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); @@ -75,7 +77,6 @@ public StandingOrderGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -87,13 +88,10 @@ public StandingOrderGet( (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, @@ -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..811ddbb56 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; @@ -62,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); @@ -76,7 +78,6 @@ public TransactionGet( string bankFinancialId = bankProfile.AccountAndTransactionApiSettings.GetFinancialId?.Invoke(aispUseV4) ?? bankProfile.FinancialId; - IdTokenSubClaimType idTokenSubClaimType = bankProfile.BankConfigurationApiSettings.IdTokenSubClaimType; // Get IApiClient IApiClient apiClient = bankRegistration.UseSimulatedBank @@ -88,13 +89,10 @@ public TransactionGet( (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, @@ -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/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 6d8be96e9..c203e18a6 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; @@ -149,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 { @@ -161,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 @@ -174,19 +165,13 @@ 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 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 @@ -223,7 +208,8 @@ public async SoftwareStatementEntity softwareStatementEntity, ExternalApiSecretEntity? externalApiSecret) = await _accountAccessConsentCommon.GetAccountAccessConsent( ac.AccountAccessConsentId, - true); + true, + ConsentIdSource.DatabaseForeignKey); AccessTokenEntity? accessToken = await _accountAccessConsentCommon.GetAccessToken(persistedConsent.Id, true); @@ -243,7 +229,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 = @@ -263,7 +250,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 = @@ -339,7 +327,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,45 +390,38 @@ 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; // Validate ID token including nonce - string? requestObjectAudClaim = consentAuthGetCustomBehaviour?.AudClaim; - string bankTokenIssuerClaim = - requestObjectAudClaim ?? - issuerUrl; DateTimeOffset modified = _timeProvider.GetUtcNow(); if (idToken is not null) { - bool doNotValidateIdToken = - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; - if (doNotValidateIdToken is false) + string? newExternalApiUserId = await _grantPost.ValidateIdTokenAuthEndpoint( + idToken, + code, + state, + consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, + customBehaviour?.BaseIdTokenProcessingCustomBehaviour, + jwksUri, + customBehaviour?.JwksGet, + issuerUrl, + externalApiClientId, + externalApiConsentId, + nonce, + supportsSca, + bankProfile.BankProfileEnum, + consent.ExternalApiUserId); + if (newExternalApiUserId != consent.ExternalApiUserId) { - string? newExternalApiUserId = await _grantPost.ValidateIdTokenAuthEndpoint( - idToken, - code, - state, - consentAuthGetCustomBehaviour?.IdTokenProcessingCustomBehaviour, - jwksUri, - customBehaviour?.JwksGet, - bankTokenIssuerClaim, - externalApiClientId, - externalApiConsentId, - nonce, - supportsSca, - bankProfile.BankProfileEnum, - idTokenSubClaimType, - consent.ExternalApiUserId); - if (newExternalApiUserId != consent.ExternalApiUserId) - { - consent.UpdateExternalApiUserId( - newExternalApiUserId, - modified, - modifiedBy); - } + consent.UpdateExternalApiUserId( + newExternalApiUserId, + modified, + modifiedBy); } } @@ -508,7 +488,7 @@ await _domesticVrpConsentCommon.GetDomesticVrpConsent( await _grantPost.PostAuthCodeGrantAsync( code, redirectUrl, - bankTokenIssuerClaim, + issuerUrl, externalApiClientId, clientSecret, externalApiConsentId, @@ -522,12 +502,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/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/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 { 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 5070280be..8adbce843 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; @@ -51,6 +52,7 @@ public GrantPost( string code, string state, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, JwksGetCustomBehaviour? jwksGetCustomBehaviour, string bankIssuerUrl, @@ -59,7 +61,6 @@ public GrantPost( string expectedNonce, bool supportsSca, BankProfileEnum? bankProfileForTppReportingMetrics, - IdTokenSubClaimType idTokenSubClaimType, string? externalApiUserId) { // Check for empty token @@ -72,6 +73,7 @@ public GrantPost( var idTokenObject = await DeserialiseIdToken( idTokenEncoded, idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, bankProfileForTppReportingMetrics, jwksGetCustomBehaviour); @@ -83,27 +85,35 @@ public GrantPost( externalApiConsentId, expectedNonce, supportsSca, - idTokenProcessingCustomBehaviour); + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + if (string.IsNullOrEmpty(idTokenObject.Subject)) + { + throw new HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.SubjectEmpty)); + } + IdTokenSubClaimType idTokenSubClaimType = + IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); string? outputExternalApiUserId = externalApiUserId; // unchanged by default switch (idTokenSubClaimType) { 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)); } } @@ -111,7 +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 HttpResponseException( + new IdTokenValidationError(idTokenObject, IdTokenValidationErrorType.SubjectClientIdMismatch)); } break; @@ -119,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; @@ -230,12 +253,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"); @@ -246,6 +269,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; @@ -325,27 +354,20 @@ public async Task PostAuthCodeGrantAsync( } // Perform validation - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour = - authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour; - bool doNotValidateIdToken = - idTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; - if (doNotValidateIdToken is false) - { - await ValidateIdTokenTokenEndpoint( - response.IdToken, - response.AccessToken, - idTokenProcessingCustomBehaviour, - jwksUri, - jwksGetCustomBehaviour, - bankIssuerUrl, - externalApiClientId, - externalApiConsentId, - expectedNonce, - bankProfileForTppReportingMetrics, - supportsSca, - idTokenSubClaimType, - externalApiUserId); - } + await ValidateIdTokenTokenEndpoint( + response.IdToken, + response.AccessToken, + authCodeGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, + jwksUri, + jwksGetCustomBehaviour, + bankIssuerUrl, + externalApiClientId, + externalApiConsentId, + expectedNonce, + bankProfileForTppReportingMetrics, + supportsSca, + externalApiUserId); } return response; @@ -366,11 +388,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"); @@ -380,6 +402,12 @@ public async Task PostRefreshTokenGrantAsync( { "refresh_token", refreshToken } }; + bool useScopeInRequest = refreshTokenGrantPostCustomBehaviour?.UseScopeInRequest ?? false; + if (useScopeInRequest) + { + keyValuePairs["scope"] = refreshTokenScope; + } + if (tokenEndpointAuthMethod is TokenEndpointAuthMethodSupportedValues.PrivateKeyJwt) { @@ -433,27 +461,20 @@ public async Task PostRefreshTokenGrantAsync( } // Perform validation - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour = - refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour; - bool doNotValidateIdToken = - idTokenProcessingCustomBehaviour?.DoNotValidateIdToken ?? false; - if (doNotValidateIdToken is false) - { - await ValidateIdTokenTokenEndpoint( - response.IdToken, - response.AccessToken, - idTokenProcessingCustomBehaviour, - jwksUri, - jwksGetCustomBehaviour, - bankIssuerUrl, - externalApiClientId, - externalApiConsentId, - expectedNonce, - bankProfileForTppReportingMetrics, - supportsSca, - idTokenSubClaimType, - externalApiUserId); - } + await ValidateIdTokenTokenEndpoint( + response.IdToken, + response.AccessToken, + refreshTokenGrantPostCustomBehaviour?.IdTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, + jwksUri, + jwksGetCustomBehaviour, + bankIssuerUrl, + externalApiClientId, + externalApiConsentId, + expectedNonce, + bankProfileForTppReportingMetrics, + supportsSca, + externalApiUserId); } return response; @@ -524,85 +545,99 @@ private void ValidateIdTokenCommon( string externalApiConsentId, string expectedNonce, bool supportsSca, - IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour) + IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour) { if (idToken.Exp < DateTimeOffset.UtcNow) { - throw new Exception("ID token has expired."); + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.IdTokenExpired)); } bool idTokenMayNotHaveConsentIdClaim = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveConsentIdClaim ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveConsentIdClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); 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 = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveNonceClaim ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveNonceClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); 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 = idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveAuthTimeClaim ?? false; + bool idTokenMayNotHaveAuthTimeClaim = + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveAuthTimeClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); 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 = - idTokenProcessingCustomBehaviour?.IdTokenMayNotHaveAcrClaim - ?? false; + IdTokenProcessingCustomBehaviour.GetIdTokenMayNotHaveAcrClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); 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 = - idTokenProcessingCustomBehaviour?.DoNotValidateIdTokenAcrClaim - ?? false; + IdTokenProcessingCustomBehaviour.GetDoNotValidateIdTokenAcrClaim( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); 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)); } } - 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."); + 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)); } } @@ -630,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; } @@ -641,6 +682,7 @@ private async Task ValidateIdTokenTokenEndpoint( string idTokenEncoded, string accessToken, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, JwksGetCustomBehaviour? jwksGetCustomBehaviour, string bankIssuerUrl, @@ -649,7 +691,6 @@ private async Task ValidateIdTokenTokenEndpoint( string expectedNonce, BankProfileEnum? bankProfileForTppReportingMetrics, bool supportsSca, - IdTokenSubClaimType idTokenSubClaimType, string? externalApiUserId) { // Check for empty token @@ -662,6 +703,7 @@ private async Task ValidateIdTokenTokenEndpoint( var idToken = await DeserialiseIdToken( idTokenEncoded, idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour, jwksUri, bankProfileForTppReportingMetrics, jwksGetCustomBehaviour); @@ -673,27 +715,48 @@ private async Task ValidateIdTokenTokenEndpoint( externalApiConsentId, expectedNonce, supportsSca, - idTokenProcessingCustomBehaviour); + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); // Validate ID token subject claim + if (string.IsNullOrEmpty(idToken.Subject)) + { + throw new HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectEmpty)); + } + IdTokenSubClaimType idTokenSubClaimType = + IdTokenProcessingCustomBehaviour.GetIdTokenSubClaimType( + idTokenProcessingCustomBehaviour, + baseIdTokenProcessingCustomBehaviour); switch (idTokenSubClaimType) { 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 HttpResponseException( + new IdTokenValidationError(idToken, IdTokenValidationErrorType.SubjectClientIdMismatch)); } break; @@ -701,12 +764,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)); } } } @@ -714,6 +779,7 @@ private async Task ValidateIdTokenTokenEndpoint( private async Task DeserialiseIdToken( string idTokenEncoded, IdTokenProcessingCustomBehaviour? idTokenProcessingCustomBehaviour, + IdTokenProcessingCustomBehaviour? baseIdTokenProcessingCustomBehaviour, string jwksUri, BankProfileEnum? bankProfileForTppReportingMetrics, JwksGetCustomBehaviour? jwksGetCustomBehaviour) @@ -730,7 +796,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/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() + }; +} diff --git a/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs b/src/OpenBanking.Library.Connector/Operations/ExternalApi/PaymentInitiation/PaymentInitiationPostRequestProcessor.cs index e449276b6..46a4b684a 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) { @@ -100,19 +102,12 @@ public PaymentInitiationPostRequestProcessor( .SendExpectingJsonResponseAsync( apiClient, tppReportingRequestInfo, - responseJsonSerializerSettings); + responseJsonSerializerSettings, + true); 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/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))); } diff --git a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs index f5e191443..184af3891 100644 --- a/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs +++ b/src/OpenBanking.Library.Connector/Operations/Management/BankRegistrationOperations.cs @@ -285,6 +285,8 @@ await _configurationRead.GetOpenIdConfigurationAsync( BankGroup.Starling => GetExistingRegistration, BankGroup.Tide => GetExistingRegistration, BankGroup.Tsb => GetExistingRegistration, + BankGroup.Wise => GetExistingRegistration, + BankGroup.Zopa => GetExistingRegistration, _ => throw new ArgumentOutOfRangeException() }; (BankRegistrationEntity? existingGroupRegistration, @@ -1259,9 +1261,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( 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.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs b/src/OpenBanking.Library.Connector/Operations/PaymentInitiation/DomesticPayment.cs index b259cf8d6..b50d8ed0b 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; @@ -249,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; @@ -271,7 +273,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 = @@ -290,12 +291,10 @@ Task CreateAsync( (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, @@ -308,10 +307,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/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 87b212ab6..98ddadb5c 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; @@ -357,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; @@ -530,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; @@ -540,7 +542,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 = @@ -559,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, @@ -577,10 +576,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..4f81f248d 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; @@ -98,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; @@ -122,7 +124,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; @@ -140,12 +141,10 @@ public async Task CreateAsync( (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, @@ -158,10 +157,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/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 760a0112d..697e9a54e 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; @@ -102,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; @@ -128,7 +130,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; @@ -147,12 +148,10 @@ Task CreateFundsConfirmationAsync( (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, @@ -165,10 +164,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( @@ -543,7 +542,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; @@ -732,7 +731,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 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 diff --git a/src/OpenBanking.Library.Connector.Web/ExternalApiHttpErrorExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs similarity index 63% rename from src/OpenBanking.Library.Connector.Web/ExternalApiHttpErrorExceptionFilter.cs rename to src/OpenBanking.WebApp.Connector/Filters/ExternalApiHttpErrorExceptionFilter.cs index 0e287198a..2f8b121d0 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 { @@ -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,22 +36,22 @@ 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 - { - responseMessage = JsonNode.Parse(httpResponseException.ResponseMessage); - } - catch + if (httpResponseException.ExposeSuccessResponseBody) { - 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); @@ -71,7 +61,7 @@ public void OnActionExecuted(ActionExecutedContext context) { Content = jsonString, ContentType = mediaTypeHeaderValue.ToString(), - StatusCode = statusCode + StatusCode = 500 }; context.ExceptionHandled = true; diff --git a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs b/src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs similarity index 53% rename from src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs rename to src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs index 58b172dcf..5895d6a7f 100644 --- a/src/OpenBanking.Library.Connector.Web/GlobalExceptionFilter.cs +++ b/src/OpenBanking.WebApp.Connector/Filters/GlobalExceptionFilter.cs @@ -1,13 +1,14 @@ -// 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 FinnovationLabs.OpenBanking.Library.Connector.Extensions; using FinnovationLabs.OpenBanking.Library.Connector.Fluent; using Microsoft.AspNetCore.Mvc; 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 { @@ -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.WebApp.Connector/OpenBanking.WebApp.Connector.csproj b/src/OpenBanking.WebApp.Connector/OpenBanking.WebApp.Connector.csproj index ef4671fd3..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 - 18.2.0 + 20.0.0 Finnovation Labs Limited and Contributors Finnovation Labs Limited false @@ -16,16 +16,18 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + diff --git a/src/OpenBanking.WebApp.Connector/Program.cs b/src/OpenBanking.WebApp.Connector/Program.cs index 62083e980..2d1e823c3 100644 --- a/src/OpenBanking.WebApp.Connector/Program.cs +++ b/src/OpenBanking.WebApp.Connector/Program.cs @@ -5,16 +5,15 @@ 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 OpenTelemetry.Trace; 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; @@ -33,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 => @@ -80,15 +79,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 +92,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 @@ -130,7 +116,9 @@ builder .Logging - .AddWebHostLogging(builder.Configuration, serviceVersion); + .ClearProviders() + .AddConsole() + .AddGenericHostLogging(builder.Configuration, serviceVersion); // Build app WebApplication app = builder.Build(); @@ -148,9 +136,6 @@ // Add local static files app.UseStaticFiles(); -// Add web host static files -app.UseWebHostStaticFiles(); - // Add Swagger generation app.UseSwagger(); app.UseSwaggerUI( @@ -161,7 +146,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/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/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/AppContextFixture.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs index 367df10dd..27a32d5c1 100644 --- a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs +++ b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppContextFixture.cs @@ -4,11 +4,11 @@ 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; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using ServiceCollectionExtensions = @@ -36,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) + .AddWebHostServices() // Add bank testing services - .AddBankTestingServices(builder.Configuration) + .AddBankTestingServices() // Add memory cache .AddMemoryCache() // Add controllers @@ -59,20 +57,19 @@ public AppContextFixture() builder .Logging - .AddWebHostLogging(builder.Configuration, null); + .ClearProviders() + .AddConsole(); // Build app 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"); - // Add web host static files + app.UseHsts(); + + // Add static files app.UseWebHostStaticFiles(); // Add controller endpoints diff --git a/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs b/test/OpenBanking.Library.Connector.BankTests/BankTests/AppTests.cs index e8e17cd84..40bfb78dd 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; @@ -23,10 +21,10 @@ 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; +using Microsoft.Extensions.Options; using Microsoft.Playwright; using ObSealCertificateRequest = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.Management.Request.ObSealCertificate; @@ -239,6 +237,14 @@ public static IEnumerable GetTestCases(TestType testType) bankProfileFromEnv, registrationScope, bankGroup), + BankGroup.Wise => GetAllInRegistrationGroup( + bankProfileFromEnv, + registrationScope, + bankGroup), + BankGroup.Zopa => GetAllInRegistrationGroup( + bankProfileFromEnv, + registrationScope, + bankGroup), _ => throw new ArgumentOutOfRangeException() }; } @@ -354,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); @@ -380,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; @@ -541,9 +528,7 @@ await accountAccessConsentSubtest.RunTest( testData.TestAuth, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("aisp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser, @@ -595,9 +580,7 @@ await domesticPaymentConsentSubtest.RunTest( paymentsEnv, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("pisp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser); @@ -626,9 +609,7 @@ await domesticVrpConsentSubtest.RunTest( paymentsEnv, testNameUnique, ModifiedBy, - testDataProcessorFluentRequestLogging - .AppendToPath("vrp") - .AppendToPath($"{subTest.ToString()}"), + null, consentAuth, authUrlLeftPart, bankUser, @@ -807,11 +788,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 @@ -844,11 +825,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/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/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/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; } 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); } } diff --git a/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettings.cs index 89537dd4e..00fbedc18 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,59 +114,9 @@ public class ExecutablePath public string? Linux { get; set; } } -public class BankTestSettings : ISettings +public class BankTestSettings { - public ConsentAuthoriserOptions Auth { get; set; } = new(); + public const string ConfigSectionName = "OpenBankingConnector:BankTests"; - /// - /// 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; - - 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 - { - OsPlatformEnum.MacOs => DataDirectory.MacOs, - OsPlatformEnum.Linux => DataDirectory.Linux, - OsPlatformEnum.Windows => DataDirectory.Windows, - _ => throw new ArgumentOutOfRangeException() - }; + public ConsentAuthoriserOptions Auth { get; set; } = new(); } 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..718f7742e --- /dev/null +++ b/test/OpenBanking.Library.Connector.BankTests/Configuration/BankTestSettingsValidator.cs @@ -0,0 +1,34 @@ +// 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 (!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/src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs similarity index 61% rename from src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs index dcbe92b57..86b10c019 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AccountAndTransaction/TestingAccountAccessConsentsController.cs @@ -1,14 +1,12 @@ -// 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 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; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.AccountAndTransaction; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AccountAndTransaction; [ApiController] [ApiExplorerSettings(GroupName = "test")] @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.AccountA [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 new file mode 100644 index 000000000..5b0497bfc --- /dev/null +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/AuthContext/TestingRedirectsController.cs @@ -0,0 +1,122 @@ +// 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.Models.Public.Response; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.AuthContext; + +[ApiController] +[ApiExplorerSettings(GroupName = "test")] +[Tags("Post-Auth Redirects")] +[Route("auth")] +public class TestingRedirectsController : ControllerBase +{ + /// + /// OAuth2 query redirect endpoint + /// + /// + /// + /// + /// + /// + [HttpGet("query-redirect")] + public async Task QueryRedirectGetAsync( + [Required] [FromQuery(Name = "state")] + string state, + [FromQuery(Name = "code")] + string? code, + [FromQuery(Name = "id_token")] + string? idToken, + [FromQuery(Name = "error")] + string? error) + { + if (TestingMethods.Instance.ProcessRedirect is null) + { + throw new InvalidOperationException(); + } + + // 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 + } + + /// + /// Delegate endpoint for forwarding data captured elsewhere from OAuth2 redirect + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpPost("fragment-redirect-delegate")] + [Consumes("application/x-www-form-urlencoded")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task> FragmentRedirectDelegatePostAsync( + [Required] [FromForm(Name = "state")] + string state, + [FromForm(Name = "code")] + string? code, + [FromForm(Name = "id_token")] + string? idToken, + [FromForm(Name = "error")] + string? error, + [FromForm(Name = "response_mode")] + string? responseMode, + [FromForm(Name = "redirect_uri")] + string? redirectUrl) + { + // Check for cookie + string cookieKey = TestingAccountAccessConsentsController.BrowserCookieKey; + Request.Cookies.TryGetValue(cookieKey, out string? appSessionId); + + if (TestingMethods.Instance.ProcessRedirect is null) + { + throw new InvalidOperationException(); + } + + // 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)); + } + + var authResult = + new TestingAuthResult + { + State = state, + RedirectParameters = formCollection + }; + AuthContextUpdateAuthResultResponse fluentResponse = await TestingMethods.Instance.ProcessRedirect(authResult); + + return Created("about:blank", fluentResponse); + } +} diff --git a/src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs similarity index 60% rename from src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs index b352f3031..8518e40f7 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/PaymentInitiation/TestingDomesticPaymentConsentsController.cs @@ -1,14 +1,12 @@ -// 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 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; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.PaymentInitiation; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.PaymentInitiation; [ApiController] [ApiExplorerSettings(GroupName = "test")] @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.PaymentI [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/src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs similarity index 60% rename from src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs rename to test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs index 1786c6cd7..298190d9b 100644 --- a/src/OpenBanking.Library.Connector.Web/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs +++ b/test/OpenBanking.Library.Connector.BankTests/Controllers/VariableRecurringPayments/TestingDomesticVrpConsentsController.cs @@ -1,14 +1,12 @@ -// 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 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; -namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.VariableRecurringPayments; +namespace FinnovationLabs.OpenBanking.Library.Connector.BankTests.Controllers.VariableRecurringPayments; [ApiController] [ApiExplorerSettings(GroupName = "test")] @@ -16,13 +14,6 @@ namespace FinnovationLabs.OpenBanking.Library.Connector.Web.Controllers.Variable [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/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/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 314b4f686..1c0176113 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; @@ -18,9 +16,9 @@ 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 Microsoft.Extensions.DependencyInjection; using AccountAccessConsentAuthContext = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.AccountAndTransaction.Request. AccountAccessConsentAuthContext; @@ -45,7 +43,7 @@ public async Task RunTest( bool testAuth, string testNameUnique, string modifiedBy, - FilePathBuilder aispFluentRequestLogging, + FilePathBuilder? aispFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser, @@ -68,6 +66,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( @@ -366,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 = @@ -474,7 +478,7 @@ private static async Guid bankRegistrationId, string testNameUnique, string modifiedBy, - FilePathBuilder aispFluentRequestLogging) + FilePathBuilder? aispFluentRequestLogging) { var accountAccessConsentRequest = new AccountAccessConsentRequest @@ -504,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 1a2cfb0ab..e6711fdfd 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; @@ -37,7 +36,7 @@ public async Task RunTest( PaymentsEnv paymentsEnv, string testNameUnique, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser) @@ -223,7 +222,7 @@ private static async Task GetDomesticPaymentRequest( Guid domesticPaymentConsentId, bool pispUseV4, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -289,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 = @@ -328,7 +330,7 @@ private static async Task GetDomesticPaymentConse bool pispUseV4, string testNameUnique, string modifiedBy, - FilePathBuilder pispFluentRequestLogging, + FilePathBuilder? pispFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -399,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 2c69e191c..dbcb1804d 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; @@ -17,9 +15,9 @@ 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 Microsoft.Extensions.DependencyInjection; using DomesticVrpConsentAuthContext = FinnovationLabs.OpenBanking.Library.Connector.Models.Public.VariableRecurringPayments.Request. DomesticVrpConsentAuthContext; @@ -44,7 +42,7 @@ public async Task RunTest( PaymentsEnv paymentsEnv, string testNameUnique, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, ConsentAuth consentAuth, string authUrlLeftPart, BankUser? bankUser, @@ -228,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 = @@ -336,7 +333,7 @@ await variableRecurringPaymentsApiClient.DomesticVrpConsentCreateFundsConfirmati private static async Task GetDomesticVrpConsentFundsConfirmationRequest( string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string amount, string referenceName, VariableRecurringPaymentsApiSettings variableRecurringPaymentsApiSettings) @@ -364,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 @@ -382,7 +382,7 @@ await vrpFluentRequestLogging private static async Task GetDomesticVrpRequest( Guid domesticVrpConsentId, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string instructionIdentification, string endToEndIdentification, string amount, @@ -467,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 @@ -523,7 +526,7 @@ private static async Task GetDomesticVrpConsentReques Guid bankRegistrationId, string testNameUnique, string modifiedBy, - FilePathBuilder vrpFluentRequestLogging, + FilePathBuilder? vrpFluentRequestLogging, string referenceName, PaymentsEnv paymentsEnv, VariableRecurringPaymentsApiSettings variableRecurringPaymentsApiSettings) @@ -604,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 = diff --git a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs index 7e91a9519..b0a475d53 100644 --- a/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs +++ b/test/OpenBanking.Library.Connector.BankTests/GlobalUsings.cs @@ -34,8 +34,16 @@ 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; +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/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); + } +} 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..1ae9f180a 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 @@ -9,16 +9,22 @@ FinnovationLabs.OpenBanking.Library.Connector.BankTests Finnovation Labs Limited Finnovation Labs Limited + false + true + true - - - - - - + + + + + + + + + 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 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 dcfb51202..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,20 +9,20 @@ Finnovation Labs Limited and Contributors Finnovation Labs Limited false + true - - - + + all 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/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 f8c9138ac..d28806277 100644 --- a/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs +++ b/test/OpenBanking.Library.Connector.UnitTests/Http/ApiClientTests.cs @@ -4,9 +4,9 @@ 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 FluentAssertions; using Newtonsoft.Json; using NSubstitute; using RichardSzalay.MockHttp; @@ -38,10 +38,10 @@ 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(); - responseContent.Should().Be(content); - response.Content.Headers.ContentType?.ToString().Should().Be(contentType + "; charset=utf-8"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); + Assert.Equal(content, responseContent); + Assert.Equal(contentType + "; charset=utf-8", response.Content.Headers.ContentType?.ToString()); } [Theory] @@ -69,7 +69,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 +87,7 @@ public void SendAsync_ExceptionLogged(string url) Func a = async () => await apiClient.LowLevelSendAsync(req); - a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } @@ -116,9 +116,10 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); - result.Message.Should().Be(entity.Message); + Assert.Equal(entity.Message, result.Message); } } @@ -147,9 +148,10 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); - await a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } [Theory] @@ -184,9 +186,10 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); - await a.Should().ThrowAsync(); + await Assert.ThrowsAsync(a); } @@ -221,9 +224,10 @@ await api.SendExpectingJsonResponseAsync( req, "", null, - null); + null, + true); - 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/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/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 4ad644e82..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,12 +9,12 @@ Finnovation Labs Limited and Contributors Finnovation Labs Limited false + true - - - + + all @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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)]