Skip to content

IS-11346: render page symbols for HAAPI steps#210

Open
aleixsuau wants to merge 56 commits into
fix/integration/IS-5161/login-web-app/IS-11345-display-logosfrom
feature/IS-5161/IS-11346-display-page-symbols
Open

IS-11346: render page symbols for HAAPI steps#210
aleixsuau wants to merge 56 commits into
fix/integration/IS-5161/login-web-app/IS-11345-display-logosfrom
feature/IS-5161/IS-11346-display-page-symbols

Conversation

@aleixsuau
Copy link
Copy Markdown
Contributor

@aleixsuau aleixsuau commented May 28, 2026

IS-11346-dev-mock.patch
Jira: https://curity.atlassian.net/browse/IS-11346

Summary

LWA-side rendering of theme.pageSymbols (which IS-11318 wires up server-side). A new <PageSymbol> component resolves a symbol URL for the current step's HAAPI viewName and renders it above the step's messages/actions/links.

Resolution order in resolvePageSymbol:

  1. exact match in pageSymbols.views[viewName]
  2. plugin-type match in pageSymbols.plugins[<pluginType>]<pluginType> is the middle segment of viewNames shaped authenticator | authentication-action | consentor / <pluginType> / …
  3. fallback to pageSymbols.default
  4. otherwise renders nothing

The BankID built-in places the symbol above the lifted QR link via a new pageSymbolElement slot in ViewNameBuiltInUIProps.

To test, apply the following patch file and change DEV_PAGE_SYMBOLS.views and DEV_PAGE_SYMBOLS.plugins to emulate the use cases.

IS-11346-dev-mock.patch

Urban Sanden and others added 30 commits May 13, 2026 13:44
… selector and scoping submit button margin.
Catch every WebAuthn ceremony failure in the runners and surface it as a
synthesised HaapiUnexpectedProblemStep via the existing error.app pipeline
(rather than escalating to the React error boundary as a programming bug).
Two-bucket discriminator (cancelOrTimeout / failed) matching Velocity
parity. Copy comes from step.metadata.messages.error.clientOperation.webauthn
with empty-string fallback while the BE keys land.

* Runner-level synthesis: runWebAuthn{Registration,Authentication} catch the
  full error catalog (parse DOMException, create/get DOMException, TypeError,
  null credential, unsupported API) and throw a synthesised HaapiStepperError
  built via formatErrorStepData(HaapiUnexpectedProblemStep).
* Dispatcher wrapping: performClientOperation wraps each WebAuthn runner in a
  .then/.catch chain returning ClientOperationResult discriminated union. The
  catch discriminates HaapiStepperError vs raw rejections via a type guard;
  raw errors propagate to the React boundary as programming bugs.
* Type hierarchy: HaapiWebAuthnRegistrationClientOperationAction is now a
  proper discriminated union (passkeys | any-device) so consumers narrow via
  positive + negative type guards without casts. HaapiWebAuthnAnyDeviceArgs
  is also a discriminated union (platform-required | crossPlatform-required)
  expressing the "at least one of two" HAAPI spec invariant.
* Metadata surface: HaapiMetadata.messages.error.clientOperation.webauthn
  carries the per-ceremony copy (cancelOrTimeout shared, registration,
  authentication).
* Auto-start parity: manageWebAuthnAutoStart stays fire-and-forget via
  nextStep(); ceremony failures surface the same way as manual click
  (matching Velocity's .catch(handleError) pattern).
* Tests: 23 runner-level tests in webauthn.spec.ts cover the full catalog
  per ceremony; HaapiStepper.spec.tsx wiring tests consolidated under one
  WebAuthn suite (Auto-Start gating + Registration/Authentication
  success+error routing), dropping the duplicate auto-start error-surfacing
  cases since manual click + auto-start share the dispatcher plumbing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…apiStepperError

* Rename `HaapiMetadata.messages` → `HaapiMetadata.viewData` (and the dependent
  type aliases) so the metadata branch reads as view-customisation data emitted
  by the server rather than HAAPI step messages. Path becomes
  `step.metadata.viewData.error.clientOperation.webauthn.<key>`.
* Extract `getHaapiStepperError` from `webauthn.ts` to `client-operations.ts`
  as a shared helper that takes a resolved message string and synthesises a
  `HaapiStepperError` from a `HaapiUnexpectedProblemStep`. WebAuthn-specific
  message resolution stays in `webauthn.ts` (`getWebAuthnErrorMessage`); other
  client operations (BankID, EBF) can reuse the helper when their per-runtime
  error handling lands.
* Test fixtures + path-access assertions in `webauthn.spec.ts` updated to
  use the new `viewData` key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ClientOperationResult is no longer WebAuthn-specific — the EBF and BankID
runners will need it next. Extract it into a new sibling typings.ts file
alongside the operation runners; keep the WebAuthn-specific enums where
they are.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…number-country-code

IS-10863 SSP: add phone number country code
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…eft-padding-in-dev

IS-11243 Fix search input left padding
Flip the runtime semantics from `!!field.required` to `field.required ?? true`
on the four visible field UIs (text, password, select, checkbox). Matches the
JSDoc on HaapiBaseFormField.required ("Defaults to true.") and reflects the
production reality that most LWA form fields are required.

Update HaapiStepperFormUI.spec.tsx to keep `createLoginFormAction()` mirroring
production (no `required: false` overrides) and instead fill the fields each
test actually needs to clear via per-field helpers (fillUsername, fillPassword,
selectCountry, checkRememberMe).
…tion/IS-5161/login-web-app

# Conflicts:
#	src/common/css/lib/src/base/base-buttons.css
#	src/common/css/lib/src/base/base-forms.css
#	src/identity-server/styles/css/login-base.css
…-page-symbols-data

IS-11318 LWA: rework page symbols data in bootstrap configuration
…form-fields

IS-11368 LWA: forward HaapiBaseFormField.required to rendered field inputs
…eb-app/IS-11345-display-logos

IS-11345 Display configured logo in the Login Web App
Narrows the WebAuthn-runner classifier to honour the typings.ts contract:
only DOMException routes to error.app (NotAllowed/Abort -> CANCEL_OR_TIMEOUT,
any other DOMException -> FAILED). Anything else (TypeError, helper throw,
malformed-payload access errors, etc.) propagates so the React error boundary
catches it instead of being buried under 'Registration/Authentication failed'
copy. Keeps Velocity-parity for users and covers future WebAuthn-spec
DOMException names without an allowlist.

Spec updated: the registration/authentication asserts that TypeError /
'arbitrary non-DOMException' map to the FAILED copy are flipped to
.rejects.toBe(error), and the authentication side gains symmetric coverage.

Addresses PR #194 review feedback from @vahag-curity and @luisgoncalves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switches mockRunWebAuthnRegistration / mockRunWebAuthnAuthentication from
bare vi.fn() (Mock<any, any>) to vi.fn<typeof runWebAuthnX>(). With the
runner contract Promise<ClientOperationResult> in scope, TypeScript now
rejects mock fixtures that resolve outside the shape — including
mockResolvedValue(undefined) which previously compiled silently and
TypeError'd at runtime.

The Auto-Start beforeEach baseline is updated to resolve to a sentinel
clientOperationError: failedWebAuthnCeremonyError() so an unintended
runner invocation surfaces loudly via error.app instead of progressing
the stepper silently.

Addresses PR #194 review feedback from @vahag-curity: the dispatcher's
destructure has no production hazard — strict mode already prevents
runners from falling off the end of Promise<ClientOperationResult>.
The hazard was test-fixture authoring; this commit closes it at that
layer rather than adding a runtime guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds client-side support for rendering per-step icons ("page symbols") in the HAAPI stepper, resolving an image from the new theme.pageSymbols bootstrap config based on the current step's viewName. The symbol is rendered above the step's messages/actions/links in both the generic shell and the BankID built-in UI.

Changes:

  • Adds PageSymbols to the bootstrap config plus a resolvePageSymbol util implementing exact-view → plugin-type → default precedence.
  • Introduces a <PageSymbol> component and renders it at the top of HaapiStepperStepUI, threading it as a new pageSymbolElement slot into ViewNameBuiltInUIProps / BankIdViewNameBuiltInUI.
  • Adds CSS for .haapi-stepper-page-symbol and unit tests for the resolver, the component, and the step-level placement (including BankID).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/login-web-app/src/haapi-stepper/data-access/bootstrap-configuration.ts Adds PageSymbols type and optional theme.pageSymbols field.
src/login-web-app/src/shared/util/resolve-page-symbol.ts Implements view/plugin/default resolution.
src/login-web-app/src/shared/ui/PageSymbol.tsx New component that reads theme config and renders the symbol image.
src/login-web-app/src/shared/ui/PageSymbol.spec.tsx Unit tests covering precedence and no-render cases.
src/login-web-app/src/shared/util/css/styles.css Adds .haapi-stepper-page-symbol styling.
src/login-web-app/src/haapi-stepper/feature/viewnames/typings.ts Adds pageSymbolElement to ViewNameBuiltInUIProps.
src/login-web-app/src/haapi-stepper/feature/viewnames/BankIdViewNameBuiltInUI.tsx Renders the page symbol above the lifted QR link.
src/login-web-app/src/haapi-stepper/feature/steps/HaapiStepperStepUI.tsx Creates pageSymbolElement and renders it before other slots.
src/login-web-app/src/haapi-stepper/feature/steps/HaapiStepperStepUI.spec.tsx Wraps tests in AppConfigContext and adds page-symbol placement tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ader-fix-config

IS-11318 LWA: fix propagation of bootstrap configuration in the dev setup
@aleixsuau aleixsuau requested review from urre and vahag-curity May 29, 2026 12:15
@aleixsuau aleixsuau marked this pull request as ready for review May 29, 2026 13:14
aleixsuau and others added 19 commits May 29, 2026 15:19
Catch every WebAuthn ceremony failure in the runners and surface it as a
synthesised HaapiUnexpectedProblemStep via the existing error.app pipeline
(rather than escalating to the React error boundary as a programming bug).
Two-bucket discriminator (cancelOrTimeout / failed) matching Velocity
parity. Copy comes from step.metadata.messages.error.clientOperation.webauthn
with empty-string fallback while the BE keys land.

* Runner-level synthesis: runWebAuthn{Registration,Authentication} catch the
  full error catalog (parse DOMException, create/get DOMException, TypeError,
  null credential, unsupported API) and throw a synthesised HaapiStepperError
  built via formatErrorStepData(HaapiUnexpectedProblemStep).
* Dispatcher wrapping: performClientOperation wraps each WebAuthn runner in a
  .then/.catch chain returning ClientOperationResult discriminated union. The
  catch discriminates HaapiStepperError vs raw rejections via a type guard;
  raw errors propagate to the React boundary as programming bugs.
* Type hierarchy: HaapiWebAuthnRegistrationClientOperationAction is now a
  proper discriminated union (passkeys | any-device) so consumers narrow via
  positive + negative type guards without casts. HaapiWebAuthnAnyDeviceArgs
  is also a discriminated union (platform-required | crossPlatform-required)
  expressing the "at least one of two" HAAPI spec invariant.
* Metadata surface: HaapiMetadata.messages.error.clientOperation.webauthn
  carries the per-ceremony copy (cancelOrTimeout shared, registration,
  authentication).
* Auto-start parity: manageWebAuthnAutoStart stays fire-and-forget via
  nextStep(); ceremony failures surface the same way as manual click
  (matching Velocity's .catch(handleError) pattern).
* Tests: 23 runner-level tests in webauthn.spec.ts cover the full catalog
  per ceremony; HaapiStepper.spec.tsx wiring tests consolidated under one
  WebAuthn suite (Auto-Start gating + Registration/Authentication
  success+error routing), dropping the duplicate auto-start error-surfacing
  cases since manual click + auto-start share the dispatcher plumbing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…apiStepperError

* Rename `HaapiMetadata.messages` → `HaapiMetadata.viewData` (and the dependent
  type aliases) so the metadata branch reads as view-customisation data emitted
  by the server rather than HAAPI step messages. Path becomes
  `step.metadata.viewData.error.clientOperation.webauthn.<key>`.
* Extract `getHaapiStepperError` from `webauthn.ts` to `client-operations.ts`
  as a shared helper that takes a resolved message string and synthesises a
  `HaapiStepperError` from a `HaapiUnexpectedProblemStep`. WebAuthn-specific
  message resolution stays in `webauthn.ts` (`getWebAuthnErrorMessage`); other
  client operations (BankID, EBF) can reuse the helper when their per-runtime
  error handling lands.
* Test fixtures + path-access assertions in `webauthn.spec.ts` updated to
  use the new `viewData` key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ClientOperationResult is no longer WebAuthn-specific — the EBF and BankID
runners will need it next. Extract it into a new sibling typings.ts file
alongside the operation runners; keep the WebAuthn-specific enums where
they are.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Narrows the WebAuthn-runner classifier to honour the typings.ts contract:
only DOMException routes to error.app (NotAllowed/Abort -> CANCEL_OR_TIMEOUT,
any other DOMException -> FAILED). Anything else (TypeError, helper throw,
malformed-payload access errors, etc.) propagates so the React error boundary
catches it instead of being buried under 'Registration/Authentication failed'
copy. Keeps Velocity-parity for users and covers future WebAuthn-spec
DOMException names without an allowlist.

Spec updated: the registration/authentication asserts that TypeError /
'arbitrary non-DOMException' map to the FAILED copy are flipped to
.rejects.toBe(error), and the authentication side gains symmetric coverage.

Addresses PR #194 review feedback from @vahag-curity and @luisgoncalves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switches mockRunWebAuthnRegistration / mockRunWebAuthnAuthentication from
bare vi.fn() (Mock<any, any>) to vi.fn<typeof runWebAuthnX>(). With the
runner contract Promise<ClientOperationResult> in scope, TypeScript now
rejects mock fixtures that resolve outside the shape — including
mockResolvedValue(undefined) which previously compiled silently and
TypeError'd at runtime.

The Auto-Start beforeEach baseline is updated to resolve to a sentinel
clientOperationError: failedWebAuthnCeremonyError() so an unintended
runner invocation surfaces loudly via error.app instead of progressing
the stepper silently.

Addresses PR #194 review feedback from @vahag-curity: the dispatcher's
destructure has no production hazard — strict mode already prevents
runners from falling off the end of Promise<ClientOperationResult>.
The hazard was test-fixture authoring; this commit closes it at that
layer rather than adding a runtime guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…:curityio/ui-kit into feature/IS-11327/webauthn-error-handling
Decouples the WebAuthn runner from the client-operation dispatcher.
Previously the runner imported `getHaapiStepperError` from `client-operations.ts`,
which itself imports the runners — an awkward upward dependency. Co-locating the
helper with `ClientOperationResult` (typings.ts) by way of a sibling `helpers.ts`
follows the same shape used for the shared result type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-handling

IS-11327 LWA: WebAuthn ceremony errors as HAAPI step AppErrors
Adds the LWA-side rendering of `theme.pageSymbols`. The `PageSymbol`
component resolves the symbol URL for the current step's HAAPI
`viewName` against the bootstrap configuration's `pageSymbols` map:

  1. exact match in `views[viewName]`
  2. plugin-type match in `plugins[<pluginType>]` (extracted from the
     `authenticator | authentication-action | consentor` viewName format)
  3. fallback to `default`
  4. otherwise renders nothing

Wired into `HaapiStepperStepUI` so the symbol renders above the step's
messages/actions/links, with a slot in `ViewNameBuiltInUIProps` so the
BankID built-in places it above the lifted QR link.
…b.com:curityio/ui-kit into feature/IS-5161/IS-11346-display-page-symbols
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.

4 participants