Consorsbank: detect login SCA on HKIDN and decoupled 0030+3955 app approval#218
Open
ArlindNocaj wants to merge 8 commits into
Open
Consorsbank: detect login SCA on HKIDN and decoupled 0030+3955 app approval#218ArlindNocaj wants to merge 8 commits into
ArlindNocaj wants to merge 8 commits into
Conversation
These three issues were discovered by comparing mitmdump traces of the working hbci4j Java library against python-fints when connecting to Consorsbank (BLZ 76030080). After applying all three fixes, transactions are fetched successfully, matching the Java output exactly. 1. security.py: Use security_method_version=2 for two-step TAN auth Per the ZKA FinTS spec (page 58), two-step TAN methods (security_function != '999') require version 2 in the SecurityProfile of the HNSHK signature header. The previous hardcoded value of 1 caused Consorsbank to reject the request. Ref: raphaelm#99 2. formals.py: Include full account details in KTI1.from_sepa_account KTI1.from_sepa_account only populated iban and bic, but Consorsbank requires the full account details (account_number, subaccount_number, bank_identifier). Other classes like KTZ1 already include these fields — KTI1 was the only one missing them. 3. client.py: Add force_twostep_tan parameter for banks that require HKTAN despite HIPINS saying otherwise Some banks (Consorsbank) report HKKAZ:N in HIPINS yet reject requests without HKTAN (error 9075). The new opt-in force_twostep_tan parameter (set of segment types) allows users to override HIPINS for specific segments. Defaults to empty set, so existing behavior is unchanged. All three fixes are backwards-compatible and all existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Some banks (e.g. Consorsbank) attach the 0030/3955 response code to the original command segment (HKCCS) rather than to the HKTAN segment. This caused _send_pay_with_possible_retry() to miss the TAN challenge and return a plain TransactionResponse instead of NeedTANResponse. Added fallback: after checking tan_seg responses, also check command_seg responses for 0030/3955 codes. Also: - Add photoTAN QR code handling to transfers.rst full example - Fix typo (result.decoupled → res.decoupled) in transfers.rst - Add Consorsbank to tested.rst (Transactions + Transfer) - Add security function 900 (photoTAN / SecurePlus) - Add sample_consorsbank.py showing photoTAN transfer flow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sponse 1. Implement VoP polling (FinTS spec E.8.3.1): when the bank returns HIVPP with a polling_id but no vop_id, re-send HKVPP with polling_id + aufsetzpunkt (from HIRMS 3040) until the VoP check resolves and a vop_id is returned. 2. Broaden 3945 response code detection in VoP flow: check all HIRMG/HIRMS segments, not just tan_seg responses, since some banks attach it to different segments. 3. Add TAN fallback in approve_vop_response: after VoP approval, check command_seg and global HIRMG/HIRMS segments for 0030/3955 TAN-required codes (mirrors Fix 4 from PR raphaelm#210 but in the VoP approval path). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consorsbank (BLZ 76030080) attaches the dialog-init strong-authentication response code 0030 to the HKIDN segment rather than the HKTAN segment. The previous dialog.init() only inspected responses(tan_seg) (HKTAN), so init_tan_response was never set, the client proceeded to send the next command without completing login SCA, and the bank aborted the dialog with 9800/9120 (no TAN challenge ever surfaced). Check both the HKTAN and HKIDN references for the 0030/3955 login-SCA code. Verified end-to-end against Consorsbank: login TAN is now surfaced and a SEPA transfer completes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a SEPA transfer (or other order) is authorised via the new
Consorsbank App, the bank returns the order-received code 0030 together
with 3955 ("Sicherheitsfreigabe erfolgt über anderen Kanal") attached to
the same (HKCCS command) segment. The previous code iterated the
responses and set NeedTANResponse.decoupled from the FIRST matching code,
which is 0030, so the challenge was wrongly treated as a typed TAN and
the user was asked to enter a TAN that does not exist.
Set decoupled=True whenever 3955 is present among the segment's
responses, regardless of order. Behaviour is unchanged when only one of
the codes is present (0030 alone -> typed, 3955 alone -> decoupled), so
this is a safe, minimal change. Applied consistently in
_send_with_possible_retry, _send_pay_with_possible_retry and the
dialog-init login-SCA handler.
Also rewrite sample_consorsbank.py for the post-2025 TAN migration:
the old SecurePlus App is decommissioned (its TANs are rejected with
9941), so the sample now uses the Consorsbank App (mechanism 901,
zka_id 'Decoupled'). Login uses a typed 9-digit app TAN; the SEPA
transfer is approved in the app (decoupled) and polled to completion.
The physical SecurePlus TAN generator (mechanism 900) remains selectable
via FINTS_TAN_MECHANISM. Verified end-to-end: a real SEPA transfer was
accepted and booked.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… and SecurePlus App shutdown Clarify that Consorsbank exposes two two-step TAN mechanisms and both are handled by the sample's single TAN handler (which checks decoupled before challenge_matrix): * 901 Consorsbank App: typed 9-digit login TAN + decoupled transfer approval. * 900 SecurePlus TAN Generator: no login SCA (3076) + order-bound photoTAN QR (challenge_matrix) typed for the transfer. Note the SecurePlus *App* (the smartphone app, not the hardware generator) was shut down for Consorsbank online banking on 2026-04-25; since then any TAN it produces — including ones scanned from the 900 photoTAN QR — is rejected with 9941. Only the new Consorsbank App or the physical SecurePlus TAN-generator device produce valid TANs. The 900 code path itself is correct. Source: https://www.kritische-anleger.de/consorsbank/consorsbank-schafft-secureplus-app-zum-05-08-ab/ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consorsbank (BLZ 76030080) reports some operations as not requiring TANs in HIPINS, but rejects them without an HKTAN envelope. Keep this bank-specific compatibility default inside python-fints so applications can remain simple consumers and only override force_twostep_tan when they explicitly need to. Default segments: HKCCS, HKKAZ and HKSAL. Passing force_twostep_tan explicitly still overrides the default. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Some Fiducia/Atruvia banks return FinTS response 9010 with text 'Anzahl der Unterschriften ist nicht ausreichend' while a SecureGo plus distributed-signature approval is still pending. This is not a BPD/bootstrap error and should not abort the dialog. Keep the existing BPD safeguard for 9010 during dialog initialization, but allow 9010 inside an open dialog and map the insufficient-signatures response to a pending NeedTANResponse so callers can continue polling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two protocol-level fixes that make login and SEPA transfers work against Consorsbank (BLZ 76030080), plus a refreshed
sample_consorsbank.pycovering both current TAN methods. Both fixes are small and backwards-compatible (all existing tests pass) and were verified end-to-end with a real SEPA transfer that was accepted and booked.1. Detect login-SCA response attached to
HKIDNFor PSD2 strong customer authentication the client sends an
HKTAN(process 4) in the dialog-init. Consorsbank attaches the resulting0030/3955response to theHKIDNsegment, not theHKTANsegment.FinTSDialog.init()only inspectedresponses(tan_seg)(HKTAN), soinit_tan_responsewas never set, the client proceeded to send the next command without completing login SCA, and the bank aborted the dialog with9800/9120— no TAN challenge ever surfaced. We now check both theHKTANandHKIDNreferences.2. Flag
decoupledwhen0030and3955arrive togetherWhen an order is approved via the Consorsbank App, the bank returns
0030("Auftrag empfangen – Sicherheitsfreigabe erforderlich") together with3955("Sicherheitsfreigabe erfolgt über anderen Kanal") on the same (HKCCS) segment. The code setNeedTANResponse.decoupledfrom the first matching code (0030), so the challenge was wrongly treated as a typed TAN and the user was asked to type a TAN that doesn't exist. We now setdecoupled=Truewhenever3955is present among the segment's responses, regardless of order. Behaviour is unchanged when only one code is present (0030→ typed,3955→ decoupled).3.
sample_consorsbank.pyRewritten to cover both current Consorsbank TAN mechanisms with one TAN handler (which checks
decoupledbeforechallenge_matrix):zka_id="Decoupled"): typed 9-digit login TAN + decoupled transfer approval in the app.zka_id="photoTAN"): no login SCA (bank answers dialog-init with3076) + an order-bound photoTAN QR (challenge_matrix) that is scanned, then the TAN is typed.Verification
pytest: 69 passed, 1 skipped, 1 xfailed (no regressions).3076(no TAN) and a valid order-bound photoTAN QR is produced and decoded correctly.Note on the SecurePlus App (relevant to mechanism 900)
The SecurePlus App (the smartphone app — distinct from the physical SecurePlus TAN-generator device) was shut down for Consorsbank online banking on 2026-04-25. Since then any TAN it produces — including ones scanned from the
900photoTAN QR — is rejected with9941 TAN ungültig; effectively the app-based photoTAN sunset together with the SecurePlus App. Only the new Consorsbank App (901) or the physical SecurePlus TAN-generator device (900) produce valid TANs. The900code path in this library is correct; only the decommissioned app's TAN is refused by the bank.Source: https://www.kritische-anleger.de/consorsbank/consorsbank-schafft-secureplus-app-zum-05-08-ab/ (and the official Consorsbank HBCI FAQ).
Notes
The three earlier Consorsbank compatibility fixes (HNSHK
security_method_version=2, fullKTI1.from_sepa_account,force_twostep_tan) are already onmaster; this PR adds only the login-SCA-on-HKIDNdetection and the0030+3955decoupled detection.