Skip to content

fix: send the consent challenge within Create Consent By CONSENT_REQUEST_ID - #2957

Merged
simonredfern merged 12 commits into
OpenBankProject:developfrom
hongwei1:fix/consent-challenge-sent-in-request
Nov 3, 2026
Merged

simonredfern merged 12 commits into
OpenBankProject:developfrom
hongwei1:fix/consent-challenge-sent-in-request

Conversation

@hongwei1

@hongwei1 hongwei1 commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Create Consent By CONSENT_REQUEST_ID (v5.0.0, POST /consumer/consent-requests/CONSENT_REQUEST_ID/{EMAIL|SMS|IMPLICIT}/consents) now sends the SCA challenge as part of the request, so the connector's method-routing lookup runs on the request's live database connection and the challenge reliably reaches the user. Right after the consent request is read, and before anything is written (including a VRP consent request's view, grant, counterparty and limit), the endpoint resolves the consent consumer, the skip-SCA decision, the challenge's method and recipient, and the time-to-live check; a consent request whose challenge has nowhere to go (EMAIL without email, SMS without phone_number, IMPLICIT for a user with no address) gets a 400, as from the v3.1.0, v5.1.0 and v6.0.0 Create Consent endpoints. If the send itself fails once the consent is written, the consent is set to REJECTED, a VRP mandate is released as on revoke, and the request returns the send's error. The SMS and IMPLICIT ResourceDocs describe this, and the parity allowlist is re-recorded for them.

SMTP sessions now bound connect, read and write with mail.smtp.timeout_millis (default 30000, documented in sample.props.template). While OBP runs its tests in mail test mode, CommonsEmailWrapper keeps a bounded record of the mail it would have sent and can delay a send with mail.test.mode.delay_millis, which the new scenarios in ConsentRequestTest, VRPConsentRequestTest and CommonsEmailWrapperTest use to check that the challenge is sent before the response and that nothing is left behind when it cannot be. Not covered yet: a test for skip-SCA consumer pairs (skip_consent_sca_for_consumer_id_pairs is read once at startup), and the mapped connector's EMAIL send still reports success when SMTP fails.

…REQUEST_ID answers

In mail test mode, CommonsEmailWrapper now keeps the mail it would have
sent (bounded, oldest first) and can take mail.test.mode.delay_millis per
send, the way an SMTP round trip does. Two scenarios create a consent
with an EMAIL and an IMPLICIT challenge and expect the challenge mail to
have been sent, once and to the right address, by the time the 201
arrives. Both fail today: the challenge is sent from a Future the request
does not wait for.
…EST_ID

The EMAIL, SMS and IMPLICIT challenges were started from a Future the
request did not wait for. The send's connector method-routing lookup then
ran while the request's database connection was being closed
(SQLException: Connection is closed), and the challenge was lost; on a
deployment only a minority of challenges arrived. The send is now part
of the request. What a failed send does is settled in the commits that
follow.
…TP sessions time out

A consent request that names no email address, answered with EMAIL, must
get the 400 the other Create Consent endpoints give, and leave no consent
behind. SMTP sessions must bound connect, read and write, since mail is
now sent inside requests. The new consent scenarios reuse the suite's
create-consent URLs.
… Create Consent endpoints do

Create Consent By CONSENT_REQUEST_ID now fails when the challenge cannot
be sent, like the v3.1.0, v5.1.0 and v6.0.0 Create Consent endpoints. A
consent request that names no email address (EMAIL), no phone number
(SMS), or a user without an address for the implicit SCA method
(IMPLICIT) gets its 400 before the consent is written, since a failed
request keeps what it already wrote. Consumer pairs that skip SCA need no
recipient, as before.
… in tests

Mail is sent inside requests, so an SMTP server that goes quiet must not
hold a request and its database connection. Sessions now set
mail.smtp.connectiontimeout, mail.smtp.timeout and mail.smtp.writetimeout
from mail.smtp.timeout.millis (default 10000). The in-memory record of
test-mode mail and its delay only apply when OBP runs its tests, so a
sandbox in mail test mode does not hold challenge codes in memory.
…ge cannot be sent

A VRP consent request without an email address, answered with EMAIL, must
be refused before its view, grant, counterparty and limit are written. An
SMS challenge that cannot be sent (no SMS provider configured) must leave
the consent rejected rather than INITIATED. The EMAIL-without-email
scenario now asserts the exact answer of the consent lookup, and only the
scenarios that check timing slow the test-mode send down.
…ten, and reject a consent whose challenge failed

The consumer, the skip-SCA decision and the challenge's method and
recipient are now worked out right after the consent request is read,
before a VRP consent request writes its view, grant, counterparty and
limit. A request refused there leaves nothing behind. The send uses that
one recipient, so the check and the send cannot disagree, and the
implicit SCA method is looked up once; an implicit method other than
EMAIL or SMS sends nothing and needs no address, as before. When the
send itself fails after the consent is written (an SMS provider that is
missing or refuses), the consent is set to REJECTED before the 400, so no
one is left with a consent they can never answer.
…d document it

The name no longer reads like Jakarta Mail's own mail.smtp.timeout, and the longer default leaves room for servers that delay their greeting.
…P mandate, and no VRP write precedes a TTL refusal

A send that throws before it builds its Future (unreadable SMTP settings
outside mail test mode) must still leave the consent rejected. A VRP
consent whose SMS cannot be sent must be rejected with its view and the
PSU's grant on it released. A VRP consent refused for its time to live
must be refused before its mandate is written. An unusable
mail.smtp.timeout_millis must fall back to the default, never to "wait
forever". The SMS scenarios are skipped where an SMS provider is
configured, so they never send a real message.
…k precedes the VRP writes

When the challenge cannot be sent, the consent is rejected and, like a
revoke, its VRP mandate is released (the PSU's grant on the view, the
counterparty limit, the view). The send is started inside its Future, so
a connector that throws before building its own is handled the same way,
and the caller always gets the send's error even if this clean-up fails.
The time-to-live check moves up with the other read-only checks, before a
VRP consent request writes anything. The ResourceDocs now say that
IMPLICIT sends the challenge the implicit SCA method names, that a failed
SMS uses up the consent request, and list GetConsentImplicitSCAError.
Jakarta Mail reads 0 as wait forever, and Lift reads an unparseable value as 0, so only a positive number of milliseconds is taken.
…rceDocs

The IMPLICIT and SMS descriptions of Create Consent By CONSENT_REQUEST_ID now describe the challenge as sent, and IMPLICIT lists GetConsentImplicitSCAError. Entries re-recorded with allowlist_helper.py; the parity check reports no drift and no stale entries.
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

@simonredfern
simonredfern merged commit 0305084 into OpenBankProject:develop Nov 3, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants