fix(ksef): resolve secretRef/secret credentials mismatch on connection create - #1319
Conversation
…n create
FE sends {authType, secret}, but the credentials-shape validator required
secretRef, so every KSeF connection create request failed with
BadRequestException. The adapter factory also assumed an unreachable
second credentials indirection (secretRef -> another credentialsResolver
lookup) that nothing in the system ever populates.
Aligns KsefCredentials on a single `secret` field per the Allegro/
PrestaShop pattern, and sources contextNip from connection.config.seller.nip
(already collected by the setup form) instead of the removed indirection.
Closes #1318
Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
Tech reviewSummaryThe fix direction is correct: the FE wizard has always sent Issues[IMPORTANT] -
[SUGGESTION] -
[SUGGESTION] -
[SUGGESTION] - back-compat for hand-provisioned credential rows
[SUGGESTION] -
[SUGGESTION] -
Verdict🔄 Approve with changes - the code change is sound and well-tested; fix the setup-guide docs (IMPORTANT) before merge. CI was still pending at review time. |
…fixes (#1319) Address tech-review findings on PR #1319: - IMPORTANT: setup-guide.md still documented the removed { authType, secretRef } credentials shape; rewritten to the real { authType, secret } contract behind connection.credentialsRef, replaced the stale 'Planned addition (C5)' seller callout with the shipped config.seller row (now load-bearing for the session context NIP), and fixed the stale limitations row. - resolveContextNip now returns the trimmed NIP (lands verbatim in <ContextNip>). - ksef-setup.schema.ts header no longer resurrects the dead secretRef term and states contextIdentifier does not affect authentication; the wizard field help says the same. - factory spec: renamed the missing-seller test (it trips the context-NIP guard) and added a NIP-present/malformed-address case so resolveSeller's own guard stays covered. Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Review addressed in da0000f - all findings (1 IMPORTANT + 5 SUGGESTIONS) resolved:
Scoped quality gate: |
Tech review (round 2)SummaryThe fix is correct and complete. All three layers (FE schema, credentials-shape validator, adapter factory) now agree on Issues[IMPORTANT] -
[SUGGESTION] -
[SUGGESTION] -
[SUGGESTION] - back-compat note
Verdict🔄 Approve with changes - fix the NIP trim inconsistency in |
…es (#1319) resolveSeller now returns the trimmed nip and name so the FA(3) Podmiot1 block and the <ContextNip> handshake see one canonical value. The malformed-address spec pins the resolveSeller guard by message, the KsefCredentials.secret doc no longer reintroduces reference semantics for qualified-seal, and docs/lessons.md records the cross-layer contract-drift lesson from #1318. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: norbert-kulus-blockydevs <norbert.kulus@blockydevs.com>
|
Round-2 review addressed in f25f879 - all findings (1 IMPORTANT + 3 SUGGESTIONS) resolved:
Scoped quality gate: |
piotrswierzy
left a comment
There was a problem hiding this comment.
Review — Approve
Verified the three-layer reconciliation end-to-end against the branch — the fix is correct and minimal.
What I confirmed
- FE already sends the right shape:
ksef-setup.schema.ts → toCreateConnectionInputemitscredentials: { authType, secret }. No FE change needed, as claimed. - Validator now gates a non-empty
secret, uses neutral error text, and never echoes the value. - The removed second resolver (
KsefResolvedTokenSecret+ the asynccredentialsResolver.getonsecretRef) was genuinely dead — nothing ever populated that nested row. Dropping it loses no behavior. contextNipnow sourced fromconfig.seller.nipis correct for the<ContextNip>token handshake (seller's own NIP in the self-issuance case);config.contextIdentifierstays display/future-scoping only, and that's documented.- No lingering
secretRefin runtime code — remaining hits are the unrelatedwebhookSecretRefhelper and historicaldocs/plans/*. - Back-compat is acknowledged (hand-provisioned old-shape rows fail fast at boot with
KsefConfigException).
Cross-reference to my #1284 note: I previously flagged that the KSeF README documented credentials as { token, authType, secretRef }, which was already wrong vs. code. This PR settles the canonical shape as { authType, secret } — please make sure #1284's README (and any other docs) documents { authType, secret }, not secretRef or token.
Suggestions (non-blocking)
ksef-adapter.factory.ts—seller.nipis now trimmed + guarded independently in bothresolveContextNipandresolveSeller, andresolveAuthMaterialruns beforeresolveSeller(hence the "(context NIP unresolvable)" test rename). Consider resolving the seller once and derivingcontextNipfrom the validated profile, soresolveSellerstays the single seller-validation seam.- The new
lessons.mdentry recommends one cross-layer test (shared fixture, or anapps/apiintegration test creating a KSeF connection). This PR aligns the per-layer specs but each still builds its own{authType, secret}literal — the same drift class the lesson warns about. Worth a follow-up so the lesson is enforced, not just documented. docs/plans/implementation-plan-invoicing-fe-redesign.md:138still sayssecretRef— minor doc sweep.
Nothing blocking. Good, well-scoped fix with the back-compat impact called out.
Summary
BadRequestException: Invalid KSeF credentials: must include a non-empty \secretRef` stringbecause three layers disagreed on the credentials shape: the FE form sends{authType, secret}, the credentials-shape validator requiredsecretRef`, and the adapter factory assumed an unreachable second credentials indirection that nothing in the system ever populates.KsefCredentials.secretReftosecret, updates the shape validator to match, and removes the dead second-resolver call inksef-adapter.factory.ts-contextNip(needed for the KSeF XML handshake) is now sourced fromconnection.config.seller.nip, which the setup form already collects.ksef-setup.schema.tsalready sent the correct shape.Closes #1318
Test plan
pnpm --filter @openlinker/integrations-ksef type-check- cleanpnpm --filter @openlinker/integrations-ksef test- all 26 suites / 274 tests pass, including an end-to-end factory-creation test exercising{authType: 'ksef-token', secret: '...'}throughcreateAdapterswith no exception thrownapps/api,apps/worker) referencessecretRef/KsefCredentials/KsefResolvedTokenSecretBack-compat note
Wizard-created connections never worked with the old shape, so there is nothing to migrate for them. However, any hand-provisioned credential row in the old
{ authType, secretRef }+ nested-secret shape (e.g. rows created during early E2E testing) will now fail at adapter boot withKsefConfigException: KSeF credentials missing authType or secret. The fix is to re-save the connection credentials through the wizard (or re-provision the row as{ authType, secret }).