Skip to main content
Version: 8.2511.x.x RR

Migrating Dispatch Target From Generic Credentials to Dedicated Entities

In nevisIDM and nevisFIDO 9.2605.0 we introduced a new data model for dispatch targets. The new data model allows to assign dedicated entities to dispatch targets instead of using generic credentials. In nevisFIDO the usage of the old and new entities are controllable by a configuration. This migration guide describes how to migrate existing dispatch targets from the old data model to the new one.

::warning The migration process should be considered irreversible. It is recommended to take a backup of the database before starting the migration,

Depending on the configuration of the migration tool old Generic Credential based dispatch targets will be deleted during the migration process,

Even if they are not deleted, they set to disabled and cannot be used anymore.

Reactivation of old dispatch targets is heavily discouraged, since the possibility of data drift after migration cannot be ruled out. :::

Migration pipeline

Phase 1 — Discover users and classify generic credentials

Steps:

  • SCIM filter fidouaf_name PR discovers users with Dispatch Target generic credentials
  • SCIM filter ios_app_attestation_public_key PR discovers users with App Attestation generic credentials
  • The union of both user sets is processed; each user's generic credentials are fetched once via the Core API
  • Generic credentials are classified: those with fidouaf_name → Dispatch Target list; those with ios_app_attestation_public_key → App Attestation list
  • Each generic credential's stateName field is validated: only active and disabled are expected

Possible errors:

  • USER_CREDENTIALS_LOAD_FAILED — HTTP error fetching a user's generic credentials
  • UNEXPECTED_CREDENTIAL_STATE — generic credential stateName is not active or disabled; the credential is skipped

Phase 2 — Pair Dispatch Target generic credentials with App Attestation generic credentials

Steps:

  • Dispatch Target generic credentials without fidouaf_device_id are filtered out
  • Dispatch Target generic credentials whose fidouaf_device_id is shared by another Dispatch Target generic credential of the same user are filtered out (the device ID is ambiguous and pairing cannot be determined)
  • App Attestation generic credentials are indexed by ios_app_attestation_device_id
  • Each remaining Dispatch Target generic credential is paired with an App Attestation generic credential by matching device IDs

Possible errors:

  • MISSING_DEVICE_ID — Dispatch Target generic credential has no fidouaf_device_id
  • DUPLICATE_DEVICE_ID — two or more Dispatch Target generic credentials of the same user share the same fidouaf_device_id; all of them are skipped because pairing is ambiguous
  • UNPAIRED_APP_ATTESTATION — App Attestation generic credential has no matching Dispatch Target generic credential by device ID

Phase 3 — Pair FIDO UAF authenticators with Dispatch Target generic credentials

Steps:

  • FIDO UAF authenticators already linked to a DispatchTarget are skipped silently
  • Pairing strategy (in order):
    1. Explicit dispatchTargetExtId field on the FIDO UAF authenticator
    2. Fallback: matching deviceId between the FIDO UAF authenticator and the Dispatch Target generic credential

Possible errors:

  • FIDO_AUTHENTICATORS_LOAD_FAILED — HTTP/IO error fetching a user's FIDO UAF authenticators
  • MAPPING_VALIDATION_FAILED (cleanup mode only) — FIDO UAF authenticator already has a linked DispatchTarget but no matching Dispatch Target generic credential found by dispatchTargetExtId

Phase 4 — Report orphan Dispatch Target generic credentials

Steps:

  • Dispatch Target generic credentials not matched to any FIDO UAF authenticator are identified and reported

Possible errors:

  • ORPHAN_DISPATCH_TARGET — Dispatch Target generic credential has no matching FIDO UAF authenticator

Phase 5 — Create DispatchTargets and assign FIDO UAF authenticators

The behaviour of this phase is controlled by --gc-handling (default: retain-generic).

retain-generic (default — staged workflow)

For each group of FIDO UAF authenticators sharing a Dispatch Target generic credential:

Steps:

  1. Create one DispatchTarget via POST .../dispatch-targets; its state is forwarded from the source generic credential
  2. Validate the created entity by fetching it and comparing mandatory fields (identification, appId, name, signingKey, state) and optional fields if present (deviceId, target, dispatcher, encryptionKey, userAgent)
  3. If a paired App Attestation generic credential exists, create it via POST .../dispatch-targets/{id}/app-attestation and validate it (publicKey, receipt, identification, optionally environment)
  4. Assign the new DispatchTarget to each FIDO UAF authenticator via POST .../fido-authenticators/{id}/dispatch-target/{dtId}
  5. Disable the source Dispatch Target generic credential via PATCH .../generic-credentials/{id} (sets stateName to disabled)
  6. If the Dispatch Target generic credential was disabled successfully and a paired App Attestation generic credential exists, disable the App Attestation generic credential the same way

Possible errors:

  • DISPATCH_TARGET_CREATE_FAILED — one or more mandatory fields missing, or exception during creation; processing stops for this group
  • DISPATCH_TARGET_VALIDATION_FAILED — fetched entity fields do not match the source generic credential; processing stops (no assignment, no disabling)
  • APP_ATTESTATION_CREATE_FAILED — one or more mandatory fields missing, or exception during creation; processing stops for this group
  • APP_ATTESTATION_VALIDATION_FAILED — fetched App Attestation fields do not match the source generic credential; processing stops (no assignment, no disabling)
  • ASSIGNMENT_FAILED — exception during FIDO UAF authenticator assignment
  • GENERIC_CREDENTIAL_DISABLE_FAILED — exception disabling the source Dispatch Target generic credential; the App Attestation generic credential is not disabled if the Dispatch Target generic credential disable failed

delete-generic

Same as retain-generic (steps 1–4), but after a fully successful group:

Steps: 5. Delete the source Dispatch Target generic credential via DELETE .../generic-credentials/{id} 6. If the Dispatch Target generic credential was deleted successfully and a paired App Attestation generic credential exists, delete the App Attestation generic credential

Possible errors:

  • Same as retain-generic steps 1–4 (see above)
  • GENERIC_CREDENTIAL_DELETE_FAILED — exception deleting the source generic credential; the App Attestation generic credential is not deleted if the Dispatch Target generic credential deletion failed

delete-only-if-already-exists (cleanup mode)

Used after a previous retain-generic run to remove generic credentials that have already been migrated. Instead of creating new entities, the pipeline:

Steps:

  1. Find FIDO UAF authenticators that already have a linked DispatchTarget and pair them with Dispatch Target generic credentials by dispatchTargetExtId
  2. Validate that the existing DispatchTarget entity matches the Dispatch Target generic credential
  3. If an App Attestation generic credential is present, validate the existing AppAttestation entity
  4. Delete the source Dispatch Target generic credential
  5. If the Dispatch Target generic credential was deleted successfully and a paired App Attestation generic credential exists, delete the App Attestation generic credential

Possible errors:

  • MAPPING_VALIDATION_FAILED — unmatched Dispatch Target generic credential (no FIDO UAF authenticator with matching dispatchTargetExtId)
  • DISPATCH_TARGET_VALIDATION_FAILED — fetched entity fields do not match the source generic credential
  • APP_ATTESTATION_VALIDATION_FAILED — fetched App Attestation fields do not match the source generic credential
  • GENERIC_CREDENTIAL_DELETE_FAILED — exception deleting the source generic credential; the App Attestation generic credential is not deleted if the Dispatch Target generic credential deletion failed

In simulation mode (--dry-run) all steps — creation, validation, assignment, disabling, and deletion — are logged but not executed.


Error and failure categories

All skipped and failed entities are written to the CSV report.

CategoryPhaseEntity type referenced by entityExtIdTrigger
USER_CREDENTIALS_LOAD_FAILED1user ¹HTTP error fetching a user's generic credentials
UNEXPECTED_CREDENTIAL_STATE1generic credentialGeneric credential stateName (API response field) is not active or disabled
MISSING_DEVICE_ID2Dispatch Target generic credentialDispatch Target generic credential has no fidouaf_device_id
DUPLICATE_DEVICE_ID2Dispatch Target generic credentialTwo or more Dispatch Target generic credentials of the same user share the same fidouaf_device_id; all are skipped because pairing is ambiguous
UNPAIRED_APP_ATTESTATION2App Attestation generic credentialApp Attestation generic credential has no matching Dispatch Target generic credential by device ID
FIDO_AUTHENTICATORS_LOAD_FAILED3user ¹HTTP/IO error fetching a user's FIDO UAF authenticators
MAPPING_VALIDATION_FAILED3 ²Dispatch Target generic credentialCleanup mode only: FIDO UAF authenticator already has a linked DispatchTarget but no matching Dispatch Target generic credential found by dispatchTargetExtId
ORPHAN_DISPATCH_TARGET4Dispatch Target generic credentialDispatch Target generic credential has no matching FIDO UAF authenticator
DISPATCH_TARGET_CREATE_FAILED5Dispatch Target generic credentialOne or more mandatory fields missing (identification, signingKey, appId, name), or exception during POST .../dispatch-targets
DISPATCH_TARGET_VALIDATION_FAILED5Dispatch Target generic credentialFetched entity fields do not match the source generic credential; item processing stops (no assignment, no deletion)
APP_ATTESTATION_CREATE_FAILED5App Attestation generic credentialOne or more mandatory fields missing (receipt, publicKey, identification), or exception during POST .../app-attestation
APP_ATTESTATION_VALIDATION_FAILED5App Attestation generic credentialFetched App Attestation entity fields do not match the source generic credential; item processing stops (no assignment, no deletion)
ASSIGNMENT_FAILED5FIDO UAF authenticatorException during FIDO UAF authenticator assignment
GENERIC_CREDENTIAL_DISABLE_FAILED5generic credentialException disabling the source generic credential (retain-generic mode only); App Attestation generic credential is not disabled if Dispatch Target disable failed
GENERIC_CREDENTIAL_DELETE_FAILED5generic credentialException deleting the source generic credential (delete-generic or delete-only-if-already-exists mode only); App Attestation generic credential is not deleted if Dispatch Target deletion failed

¹ For user-level failures entityExtId duplicates userExtId — there is no finer-grained entity to reference. ² In cleanup mode (delete-only-if-already-exists) this check happens during Phase 3 pairing.

CLI options reference

OptionRequiredDefaultDescription
--base-url / -byesnevisIDM base URL, e.g. https://idm.example.com/nevisidm/api
--client-ext-id / -cyesClient extId(s) to migrate; comma-separated or repeated
--page-sizeno200Records per page for all paginated API calls
--dry-runnofalseSimulate without executing any API writes (creation, validation, assignment, deletion are all logged but not executed)
--report-filenoautoPath for the CSV report (auto-generated timestamp name if omitted)
--gc-handlingnoretain-genericGeneric credential handling after migration: retain-generic (disable source generic credential; App Attestation generic credential disabled only if Dispatch Target generic credential disable succeeded), delete-generic (delete source generic credential; same conditional applies), delete-only-if-already-exists (cleanup: validate existing mapping and delete generic credentials)
--keystorecert authPath to client PKCS12/JKS keystore
--keystore-passwordnoKeystore password. Env var: IDM_MIGRATION_KEYSTORE_PASSWORD. Prompted interactively if absent.
--keystore-typenoPKCS12PKCS12 or JKS
--truststorenoPath to truststore for server certificate verification
--truststore-passwordnoTruststore password. Env var: IDM_MIGRATION_TRUSTSTORE_PASSWORD. Prompted if truststore is configured and absent.
--truststore-typenoPKCS12PKCS12 or JKS
--username / -ubasic authUsername for Basic Auth
--password / -pnoBasic Auth password. Env var: IDM_MIGRATION_BASIC_AUTH_PASSWORD. Prompted interactively if absent.

Migration Steps

Preparation

  1. Download the migration tool from Nevis Portal and extract it to a directory.
  2. Create a trust- and keystore for the migration tool.
  3. Create database backup before starting the migration.

Migration

  1. Stop the nevisFIDO services.
  2. Run the migration tool in dry run configuration with appropriate CLI options (see above). For example:
java -jar dispatch-target-migration-tool-<<migration took version>>.jar --base-url https://yourdomain.com/nevisidm/api \
--keystore /home/youruser/nevis/nevisidm/dispatchtargetmigration/keystore.p12 \
--keystore-password=keystorepassword \
--truststore /home/youruser/nevis/nevisidm/dispatchtargetmigration/truststore.p12 \
--truststore-password=truststorepassword \
--client-ext-id 100,yourClient \
--report-file /home/youruser/nevis/nevisidm/dispatchtargetmigration/migration_report.csv \
--dry-run
  1. Review the generated report for any errors or unexpected results. If errors are found, investigate and resolve them before proceeding.
  2. Run the migration tool again without --dry-run to execute the migration.
  3. Review the generated report for any errors during execution. If errors are found, investigate and resolve them.
  4. Restart the nevisFIDO services.

Permissions

Generally we advise to have a ClientRoot or Root user for the migration, since they have permissions to access all required API endpoints and perform all necessary operations. If such a user is not available, here are the list of the endpoints that the user needs to have access to, and the required permissions for each endpoint (please check our API documentation for the exact permissions required for each endpoint):

MethodPathPurpose
GET/scim/v1/{client}/Users?filter=...Discover users with matching generic credential properties
GET/core/v1/{client}/users/{user}/generic-credentialsFetch all generic credentials for a user
GET/core/v1/{client}/users/{user}/fido-authenticatorsFetch all FIDO UAF authenticators for a user
POST/core/v1/{client}/users/{user}/dispatch-targetsCreate DispatchTarget from generic credential data
GET/core/v1/{client}/users/{user}/dispatch-targets/{dt}Validate created/existing DispatchTarget entity
POST/core/v1/{client}/users/{user}/dispatch-targets/{dt}/app-attestationCreate App Attestation
GET/core/v1/{client}/users/{user}/dispatch-targets/{dt}/app-attestationValidate created/existing App Attestation entity
POST/core/v1/{client}/users/{user}/fido-authenticators/{fido}/dispatch-target/{dt}Assign DispatchTarget to FIDO UAF authenticator
PATCH/core/v1/{client}/users/{user}/generic-credentials/{gc}Disable source generic credential (retain-generic mode: sets stateName to disabled)
DELETE/core/v1/{client}/users/{user}/generic-credentials/{gc}Delete source generic credential (delete-generic / delete-only-if-already-exists mode)