Skip to content

Wallet step-up and profile

Settings for a verify-only wallet connection that control how long a wallet step-up lasts, whether a signed-in user can step up without signing in again, whether an unlinked wallet can be bound to the user by matching profile attributes, and whether verified attributes are kept on the profile. (Preview, not yet tested with a real national wallet.)

Status: Preview — not yet tested with a real national wallet

New in 1.1.0-rc.1. Tested with the project's own test wallet. Everything on this page is off by default.

Setting it up

Per verify-only connection: Identity providers → the connection → Step-up & profile, the admin API, or Terraform helix_wallet_profile_settings. Writes need realm-admin; reads need view-identity-providers. An unknown alias is 404, a sign-in connection 400. The settings are deleted with the connection and the realm, and are not part of realm export / import.

Field Meaning
stepUpLifetimeSeconds 60–86400. How long a step-up of this connection satisfies acr_values. Unset: until the session ends
matchEnabled, matchRules Bind a wallet that is linked to nobody, for a user without a wallet link, when every rule ({claim, attribute}, 2–6 rules) matches
keepEnabled, keepClaims, keepRetentionDays Keep these verified claims on the profile for 1–3650 days (default 365) after the verification
curl -s -b cookies.txt -c cookies.txt "$HELIX_URL/admin/realms/$REALM/wallet-profile" >/dev/null
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

curl -s -b cookies.txt -X PUT -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{"stepUpLifetimeSeconds": 900,
       "matchEnabled": true,
       "matchRules": [{"claim": "family_name", "attribute": "lastName"},
                      {"claim": "given_name", "attribute": "givenName"},
                      {"claim": "birthdate", "attribute": "birthdate"}],
       "keepEnabled": true, "keepClaims": ["nationalities"], "keepRetentionDays": 365}' \
  "$HELIX_URL/admin/realms/$REALM/wallet-profile/eudi-wallet-verify"

The attribute names are your realm's own profile attribute names (here lastName, givenName, birthdate); only attributes an administrator, SCIM or an import set are used.

GET /admin/realms/{realm}/wallet-profile lists the settings of every verify-only connection; GET / DELETE …/wallet-profile/{alias} read or remove one. In the console, Reset to defaults on the Step-up & profile tab is that DELETE (confirmed, audited); kept attributes stay until they expire.

resource "helix_wallet_profile_settings" "verify" {
  alias                    = "eudi-wallet-verify"
  step_up_lifetime_seconds = 900
  match_enabled            = true
  match_rules = [
    { claim = "family_name", attribute = "lastName" },
    { claim = "given_name", attribute = "givenName" },
    { claim = "birthdate", attribute = "birthdate" },
  ]
  keep_enabled        = true
  keep_claims         = ["nationalities"]
  keep_retention_days = 365
}

Step-up without the first factor

An application that needs the wallet for a signed-in user sends acr_values=http://eidas.europa.eu/LoA/high. When the session's acr does not satisfy it and the client's login flow has a top-level REQUIRED wallet verification step, the user goes straight to that step:

  • no password again, the same SSO session (sid) and the same auth_time (max_age keeps measuring the last full sign-in);
  • the wallet must be the user's (its link, or an attribute match); another person's wallet is refused and audited, and the session keeps its acr;
  • once the step completes, the authorization request is resumed once, even if the wallet's level still does not meet acr_values, so it cannot loop.

Still a full sign-in: prompt=login, an expired max_age and the realm's maximum session lifetime force a full sign-in (with the client's flow). prompt=none never steps up. An administrator's impersonation answers login_required. A client whose flow has no wallet step still gets one full sign-in, as before.

Behaviour change: a signed-in user asked for acr_values by a client whose flow has the wallet step is no longer sent to the sign-in page first.

Step-up lifetime

Without a lifetime, a raised acr stays for the SSO session until the next login. With stepUpLifetimeSeconds, after that time the session's acr is again what it was without the step-up (for example an eID's substantial, or none) — also after several step-ups in a row, which keep the original level as their base. New codes carry that level, and acr_values asks the wallet again; the user stays signed in. A refreshed ID token keeps the acr its authorization was issued with: relying parties that need a fresh step-up send acr_values again.

Binding a wallet by matching attributes

For a user without a wallet link, a wallet that is linked to nobody can be bound when every rule matches: a PID claim (one the connection requests) must equal the profile attribute after normalisation (Unicode NFKC, case-folded, trimmed, inner white space collapsed; no accent folding, nothing fuzzy). Rules need at least two distinct profile attributes.

Only values an administrator set count. Every profile attribute records who set its current value — ADMIN (admin API or console), SCIM, IMPORT, SELF (account console), REGISTRATION (sign-up) or FEDERATION_MAPPER (an identity provider) — bound to a keyed hash of that value. Matching trusts only ADMIN, SCIM and IMPORT, and only while the value is unchanged. So someone who signs up with another person's name and birth date and relays the step-up QR code to that person gets no link. Attributes from before the upgrade have no recorded source and are untrusted (upgrade note).

Refused, and audited as WALLET_ATTRIBUTE_MATCH with a reason (names only, never values):

Reason When
PARTIAL fewer than two rules, or a rule that differs or is missing
BSN a BSN-shaped name or value
UNTRUSTED_SOURCE an attribute not set by an administrator, SCIM or an import
SELF_EDITABLE a profile attribute users may edit themselves
PRIVILEGED an administrator account: administrators are never bound by attributes
AMBIGUOUS another account of the realm also matches every rule
ALREADY_LINKED the user already has a wallet link at the sign-in connection
NO_SUBJECT a PID without an administrative number when the sign-in connection does not accept the attribute tuple

The user confirms. On a match the user sees "Link this wallet to your account?" with the wallet's name, and confirms or declines (audited WALLET_ATTRIBUTE_MATCH_CONFIRMED / WALLET_ATTRIBUTE_MATCH_DECLINED). Only after confirming, and only once the login or step-up completes, is the wallet linked at the linked sign-in connection, with provenance WALLET_MATCH. Such a link never signs in an administrator until a realm admin confirms it, and never proves an administrator's wallet at a step-up. A match is never combined with a transaction confirmation. The user gets the "wallet linked" e-mail.

Keeping verified attributes

With keepEnabled, on a successful step the claims covered by keepClaims (PID claims, a credential id or <credential>.<claim>) are stored once the login or step-up completes: sealed (AES-256-GCM under a key derived from DB_ENCRYPTION, bound to realm, user and name), with the connection, credential, issuer, level, verification time and expiry. Never a BSN-shaped claim, never a derived age_over_NN. A newer verification replaces a claim's value.

  • Applications: a client whose verified-claims policy has useStoredAttributes (console: "Also use verified details kept on the profile"; Terraform use_stored_attributes) gets, when the sign-in has no wallet evidence, its release from the user's latest stored verification — only what the policy allows and the client asks for, only within the policy's max age counted from the original verification, marked "verification_process": "helix:stored" with the original time. Each delivery re-checks that the switch is still on and that the stored verification still holds every released claim.
  • The user: account console → Verified by wallet shows the values, source, verification time and expiry, with Delete (one or all; audited ACCOUNT_VERIFIED_ATTRIBUTES_DELETED). Not shown or deletable in an impersonation.
  • Administrators: Users → a user → Verified attributes (GET /admin/realms/{realm}/users/{userId}/verified-attributes, view-users) shows names, source and times. Values only through POST …/verified-attributes/reveal with reveal-bsn (audited VERIFIED_ATTRIBUTES_REVEALED). DELETE …/verified-attributes (optionally ?name=) needs manage-users.
  • Retention and GDPR: the data-retention job deletes expired values; the user's GDPR export includes verifiedAttributes (not in an impersonation; names only in an administrator's export); anonymisation and user deletion remove them.

Not in this release

A realm-wide default step-up lifetime; keeping attributes from a wallet sign-in connection; step-up without the first factor for the essential-claims step-up of verified data and for PAR requests.

See also