From 88d1a730b9733b89dbcc6e5ecfc736ebb29b2c1c Mon Sep 17 00:00:00 2001 From: Hongwei Date: Sat, 18 Jul 2026 08:23:20 +0200 Subject: [PATCH 1/5] fix: correct Berlin Group SCA authorisation URL segment order The payment SCA endpoints called /obp/v1.3/berlin-group/... but OBP-API registers these routes at /berlin-group/v1.3/... with no /obp/ prefix, so both the create-authorisation POST and OTP-submit PUT calls were hitting a non-existent path. --- apps/portal/src/routes/(protected)/otp/+page.server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/portal/src/routes/(protected)/otp/+page.server.ts b/apps/portal/src/routes/(protected)/otp/+page.server.ts index 4b303bac..80df6529 100644 --- a/apps/portal/src/routes/(protected)/otp/+page.server.ts +++ b/apps/portal/src/routes/(protected)/otp/+page.server.ts @@ -81,7 +81,7 @@ export const actions = { // Step 1: Create authorisation const authResponse = await obp_requests.post( - `/obp/v1.3/berlin-group/${paymentService}/${paymentProduct}/${paymentId}/authorisations`, + `/berlin-group/v1.3/${paymentService}/${paymentProduct}/${paymentId}/authorisations`, {}, token ); @@ -90,7 +90,7 @@ export const actions = { // Step 2: Submit OTP to authorisation await obp_requests.put( - `/obp/v1.3/berlin-group/${paymentService}/${paymentProduct}/${paymentId}/authorisations/${authorisationId}`, + `/berlin-group/v1.3/${paymentService}/${paymentProduct}/${paymentId}/authorisations/${authorisationId}`, { scaAuthenticationData: otp }, token ); From a80a60a6e42e9691e96a511b749d92bd7908d271 Mon Sep 17 00:00:00 2001 From: Hongwei Date: Sat, 18 Jul 2026 09:45:14 +0200 Subject: [PATCH 2/5] fix(portal): call the Berlin Group native SCA endpoints for BG consent approval The SCA confirmation page submitted the OTP to /obp/v3.1.0/banks/{bank}/consents/{id}/challenge, which only transitions consents in status INITIATED -- Berlin Group consents are created in status received, so this endpoint could never succeed regardless of OTP correctness. Call the real BG-native pair instead: start the authorisation in load() (POST .../consents/{id}/authorisations) and submit the answer in the form action (PUT .../consents/{id}/authorisations/{authorisationId}), threading the authorisationId through a hidden form field. --- apps/portal/src/lib/obp/types.ts | 12 ++++ .../+page.server.ts | 56 ++++++++++++++----- .../+page.svelte | 1 + 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/apps/portal/src/lib/obp/types.ts b/apps/portal/src/lib/obp/types.ts index 62a9e5eb..641c4bba 100644 --- a/apps/portal/src/lib/obp/types.ts +++ b/apps/portal/src/lib/obp/types.ts @@ -312,6 +312,18 @@ export interface OBPBGPaymentAuthorisation { sca_status: string; } +// Berlin Group Consent Authorisation (SCA) types +export interface OBPBGStartConsentAuthorisation { + scaStatus: string; + authorisationId: string; + pushMessage: string; + _links: { scaStatus: string }; +} +export interface OBPBGConsentAuthorisationResult { + scaStatus: string; + _links?: { scaStatus?: { href?: string } }; +} + // Personal Data Field (User Attribute) export interface OBPPersonalDataField { user_attribute_id: string; diff --git a/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.server.ts b/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.server.ts index 5e74f7b2..f3edb51b 100644 --- a/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.server.ts +++ b/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.server.ts @@ -4,20 +4,47 @@ import type { RequestEvent, Actions } from '@sveltejs/kit'; import { redirect, isRedirect } from '@sveltejs/kit'; import { obp_requests } from '$lib/obp/requests'; import { OBPRequestError } from '@obp/shared/obp'; -import { env } from '$env/dynamic/private'; +import type { + OBPBGStartConsentAuthorisation, + OBPBGConsentAuthorisationResult +} from '$lib/obp/types'; export async function load(event: RequestEvent) { const consentId = event.url.searchParams.get('CONSENT_ID'); - if (!consentId) { return { loadError: 'Missing required parameter: CONSENT_ID.', consentId: '', - }; + authorisationId: '' + }; + } + + const token = event.locals.session.data.oauth?.access_token; + if (!token) { + return { + loadError: 'No access token found in session.', + consentId, + authorisationId: '' + }; } - return { consentId }; + try { + const startResponse: OBPBGStartConsentAuthorisation = await obp_requests.post( + `/berlin-group/v1.3/consents/${consentId}/authorisations`, + { scaAuthenticationData: '' }, + token + ); + + return { consentId, authorisationId: startResponse.authorisationId }; + } catch (e) { + logger.error('Error starting BG consent authorisation:', e); + let errorMessage = 'Failed to start consent authorisation.'; + if (e instanceof OBPRequestError) { + errorMessage = e.message; + } + return { loadError: errorMessage, consentId, authorisationId: '' }; + } } export const actions = { @@ -25,35 +52,34 @@ export const actions = { const formData = await request.formData(); const otp = formData.get('otp') as string; const consentId = formData.get('consentId') as string; + const authorisationId = formData.get('authorisationId') as string; if (!otp) { return { message: 'Please enter the OTP code.' }; } + if (!authorisationId) { + return { message: 'Missing authorisation id. Please reload the page.' }; + } + const token = locals.session.data.oauth?.access_token; if (!token) { return { message: 'No access token found in session.' }; } - const defaultBankId = env.DEFAULT_BANK_ID; - if (!defaultBankId) { - logger.error('DEFAULT_BANK_ID environment variable is not set'); - return { message: 'Server configuration error: DEFAULT_BANK_ID is not set.' }; - } - try { - const response = await obp_requests.post( - `/obp/v3.1.0/banks/${defaultBankId}/consents/${consentId}/challenge`, - { answer: otp }, + const response: OBPBGConsentAuthorisationResult = await obp_requests.put( + `/berlin-group/v1.3/consents/${consentId}/authorisations/${authorisationId}`, + { scaAuthenticationData: otp }, token ); - if (response.status === 'ACCEPTED' || response.status === 'VALID') { + if (response.scaStatus === 'valid') { redirect(303, `/confirm-bg-consent-request-redirect-uri?CONSENT_ID=${consentId}`); } return { - message: `Challenge was not accepted. Status: ${response.status}` + message: `Challenge was not accepted. Status: ${response.scaStatus}` }; } catch (e) { if (isRedirect(e)) throw e; diff --git a/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.svelte b/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.svelte index 41d65eb9..2f999ee4 100644 --- a/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.svelte +++ b/apps/portal/src/routes/(protected)/confirm-bg-consent-request-sca/+page.svelte @@ -25,6 +25,7 @@
+
+ +
+

+ Select Accounts +

+ {#if data.userAccounts?.length} +

+ Choose which of your accounts the requested permissions apply to: +

+
+ {#each data.userAccounts as account} + + {/each} +
+ {:else} +

+ You have no accounts at this bank, so this consent cannot be authorised. +

+ {/if} +
+
- + - From 9f989a54994b608b907d1ff891dad8eecd7b5615 Mon Sep 17 00:00:00 2001 From: Hongwei Date: Wed, 22 Jul 2026 09:03:28 +0200 Subject: [PATCH 4/5] fix(portal): preserve query params in the UK consent form action URLs SvelteKit's "?/actionName" shorthand replaces the page's entire query string rather than appending to it. load() requires CONSENT_ID/bank_id from the URL, so submitting a bare "?/confirm" or "?/deny" stripped them -- on any non-redirecting action response (e.g. a validation failure) load() re-ran against the stripped URL and threw its own "Missing required parameter" error, masking the action's real message. Preserve the existing query string per SvelteKit's documented convention for named actions on a page with search params. --- .../uk-consent-request/+page.svelte | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/portal/src/routes/(protected)/uk-consent-request/+page.svelte b/apps/portal/src/routes/(protected)/uk-consent-request/+page.svelte index a4354d64..c22cfaf3 100644 --- a/apps/portal/src/routes/(protected)/uk-consent-request/+page.svelte +++ b/apps/portal/src/routes/(protected)/uk-consent-request/+page.svelte @@ -1,5 +1,20 @@
@@ -114,7 +129,7 @@
-
+ @@ -126,7 +141,7 @@ Confirm Consent
-
+