Skip to content

Fortified IDDocs

Explicit registration and clustering

Configure persistent explicit client registration and operate it safely on standalone or clustered Access nodes.

Explicit registration lets Access obtain OP-assigned client credentials from a verified federation_registration_endpoint. Access stores and renews the registration so that restarts and concurrent authentication requests do not create uncontrolled replacement credentials.

This page applies only when the RP uses federation_client_registration_type: explicit. Pre-registered and automatic clients do not use explicit_registration_store.

ModeIntended deploymentAuthorityStatus
file_localOne non-clustered Access nodeLocal fileSupported for standalone deployments
file_vertx_experimentalVert.x cluster with one local file copy per nodeCluster-wide runtime mapExplicit opt-in; experimental
jdbcAccess clusterShared databaseSupported cluster backend

Use the installed resources directory because it survives Access upgrades. Each registration file can contain an OP-issued client_secret and must be treated as credential material.

{
"explicit_registration_store": {
"type": "file",
"enable_experimental_vertx_cluster_store": false,
"file": {
"directory": "${system.customer_home}/resources/oidcf/registrations"
}
}
}

On POSIX file systems, Access creates the directory with mode 0700 and credential files with 0600. Writes require atomic rename support. A persistent directory lock file, a JVM-wide lock and an operating-system file lock serialize registration and renewal when two Access processes temporarily share the same node directory.

The file store removes expired readable records during startup, before writes and through periodic housekeeping. Corrupt records are not silently deleted; loading one produces a validation error so the operator can investigate.

Back up the registration directory with the rest of the protected Access configuration. Restrict backup access and preserve ownership and permissions when restoring it.

The experimental mode keeps the implementation behind an explicit switch:

{
"explicit_registration_store": {
"type": "file",
"enable_experimental_vertx_cluster_store": true,
"cluster_lock_timeout_ms": 30000,
"file": {
"directory": "${system.customer_home}/resources/oidcf/registrations"
}
}
}

Each node uses its own local directory; no shared file system is required. A cluster-wide Vert.x AsyncMap is the runtime authority, the event bus distributes updates to node-local replicas, and a distributed lock serializes registration and renewal for the same RP, OP and configuration fingerprint.

The mode reports store_mode: file_vertx_experimental and logs an EXPERIMENTAL warning when initialized. It remains covered by automated two-node tests but does not have the long-term support contract of the JDBC design.

During a rolling restart, a returning node reconciles its local copy with the current cluster record. The cluster value wins.

After a full cluster restart, Access does not seed a new empty runtime map from one node's file. A node that has not started might hold a newer credential, so selecting one local copy would be unsafe. Access creates a new runtime epoch and performs a new explicit registration before serving the flow. Old-epoch writes are fenced from the new namespace.

JDBC uses a shared database as the single authority. The named JdbcClient module owns the database connection, pool and Flyway lifecycle. The federation entity's explicit_registration_store references that JDBC destination and owns the OIDCF-specific database dialect, schema, lease coordination and record-encryption settings.

Deployable configuration accepts mssql, mysql and mariadb. MySQL and MariaDB use the same SQL dialect and migration, but remain separate configuration values so the selected database is explicit. HSQLDB exists only as an internal test dialect and is rejected by deployable configuration.

Database productMinimum verified versionMigration locationReal-database CI gate
mssqlSQL Server 2022classpath:db/migration/oidcf/mssqlEnabled
mysqlMySQL 8.4 LTSclasspath:db/migration/oidcf/mysqlEnabled
mariadbMariaDB 11.4 LTSclasspath:db/migration/oidcf/mysqlEnabled

Create a dedicated schema or database and let the named JDBC module run the OIDCF migration.

{
"name": "JdbcClient",
"enabled": true,
"config": {
"name": "oidcf-registration",
"jdbc": {
"url": "jdbc:sqlserver://db.example:1433;databaseName=access",
"username": "${secrets.oidcf_jdbc_username}",
"password": "${secrets.oidcf_jdbc_password}"
},
"migrations": {
"locations": ["classpath:db/migration/oidcf/mssql"],
"schemas": ["OIDCF"]
}
}
}

The value in migrations.schemas must match the federation store's database_schema. Use a dedicated schema so the Flyway history and tables do not collide with other users of the same database.

Flyway support for MySQL and MariaDB is included with the OIDC module. The database-specific JDBC driver must also be available on the Access server classpath, as for other uses of the JdbcClient module. The MySQL/MariaDB migration creates InnoDB tables.

The following example selects SQL Server. For MySQL, set database_product to mysql and database_schema to the database name used in the JDBC URL and migrations.schemas. For MariaDB, use mariadb in the same way.

{
"explicit_registration_store": {
"type": "jdbc",
"jdbc_client": "oidcf-registration",
"database_product": "mssql",
"database_schema": "OIDCF",
"lease_duration_ms": 30000,
"lease_acquire_timeout_ms": 30000,
"retention_cleanup_interval_ms": 3600000,
"retention_cleanup_batch_size": 100,
"credential_encryption_keystore": {
"type": "PKCS12",
"path": "${system.customer_home}/resources/oidcf/registration-credentials.p12",
"password": "${secrets.oidcf_registration_keystore_password}"
},
"credential_encryption_alias": "oidcf-registration",
"credential_encryption_key_password": "${secrets.oidcf_registration_key_password}"
}
}
NameDescriptionDefaultMandatory
typeSet to jdbc."file"
jdbc_clientDestination name exposed by the named JdbcClient module.""
database_productSQL dialect: mssql, mysql or mariadb.""
database_schemaDedicated unquoted SQL identifier beginning with a letter.""
lease_duration_msLifetime of the database lease. Minimum 3000 ms.30000
lease_acquire_timeout_msPositive maximum time to acquire the registration lease. Must be less than 86400000 ms (one day).30000
retention_cleanup_interval_msInterval between bounded cleanup runs. Minimum 1000 ms.3600000
retention_cleanup_batch_sizeMaximum number of candidates processed in each cleanup phase and run. Valid range 1–1000.100
credential_encryption_keystoreAccess-compatible keystore containing the record-encryption key.{}
credential_encryption_aliasAlias of a 256-bit AES key.""
credential_encryption_key_passwordPassword for the AES key. The value may be empty but the field must be present.None

The full registration record is encrypted with AES-256-GCM before it is sent to JDBC. Every Access node must have the same AES key and retain it across restart, restore and rolling upgrade. Losing the key makes existing records unreadable; using different keys makes cluster behavior fail closed.

The database stores the current encrypted registration, its generation, expiry and a lease row with a monotonically increasing fencing token. A node acquires a conditional lease, performs the OP call without holding a database connection or transaction, and may persist the result only if the same lease and fencing token are still current.

This prevents a delayed node from overwriting a newer registration after its lease has expired or been taken over. Access performs a final authoritative database read under coordination before credentials are returned to the authenticator.

SQL Server uses update and range-lock hints for its lease and fence checks. MySQL and MariaDB lock the lease row with FOR UPDATE while a record write verifies the current fence, and use an atomic ON DUPLICATE KEY UPDATE path when nodes race to create the first lease row.

On module stop, new registration calls are rejected, ongoing lease acquisitions are drained and active leases are revoked. A failed revocation blocks a clean restart until a later stop attempt succeeds.

The JDBC store starts together with the federation entity, before its HTTP listener is published. Startup fails closed if the named JDBC destination, schema or encryption key cannot be opened. Retention therefore runs even on a node that has not yet received an explicit-registration request.

An expired registration remains as a generation floor for seven days. It can then be retired only when its lease is inactive. Retirement increments the fencing token and marks the lease before the record is removed. An inactive lease without a record becomes a tombstone, including a lease left by an interrupted first registration. The tombstone remains for one day and is removed only when there is no record and no active lease.

The one-day tombstone period is why lease_acquire_timeout_ms must be strictly shorter than one day. A fresh database acquisition after retirement can reclaim a recordless tombstone with a higher fencing token, so a legitimate retry does not have to wait for cleanup. An older acquisition or writer that crossed retirement remains fenced out.

The seven-day record-retention period and one-day tombstone period are fixed safety properties shared by every federation entity using the schema. They are not deployable configuration values. Do not try to add expired_record_retention_ms or tombstone_retention_ms; Access rejects them.

Every active JDBC store instance may run the same conditional, idempotent cleanup against the authoritative database. No leader election is required. By default, cleanup runs hourly and processes at most 100 candidates in each of its phases. Size the interval and batch so that the following daily capacity exceeds the number of new RP–OP configuration fingerprints that expire per day:

retention_cleanup_batch_size * 86400000 / retention_cleanup_interval_ms

One active store with the defaults can process at least 2400 candidates per phase and day. Multiple nodes are safe, but should not be required to meet the capacity target. Stopping the store cancels its timer and waits for any cleanup already in progress.

explicit_registration_renewal_lead_seconds controls how early Access attempts renewal. The default is 60 seconds. The effective expiry never extends beyond the verified OP trust resolution or an earlier secret expiry for a secret-based authentication method.

If an early renewal fails after a successful OP refresh, Access may use the previous registration only while it remains valid and compatible with the refreshed OP metadata. If trust resolution fails, authentication stops even when stored credentials still exist. Credentials never replace current trust information.

If the database, named JDBC destination, schema, encryption key or selected store is unavailable, explicit registration fails closed. Ordinary OIDC, pre-registered federation clients and automatic registration do not use this store.

Federation logs identify the phase, federation entity ID, OP entity ID and resolver strategy when available. Validation errors retain a bounded root cause such as endpoint, HTTP status, media type, issuer, audience, expiry, kid, algorithm, signature, lease or storage failure.

JDBC retention logs the number of retired records, marked orphan leases and purged tombstones when a run changes data. A failed run identifies the JDBC destination, database product and schema without exposing stored credentials.

The federation entity status includes resolution timestamps and expiry, last_error, and explicit-registration details such as:

  • last_success_at
  • expires_at
  • renew_after
  • assigned client_id
  • whether a client secret is present
  • store_mode
  • a sanitized last_error

Logs and status do not include Entity Statements, Request Objects, tokens, client assertions, client secrets, private keys, full JWKS content or HTTP response bodies.

When troubleshooting, first verify the named HTTP and JDBC destinations independently, then check the federation entity's status and the log entry for the same entity and OP.

For file_local, back up the protected registration directory and preserve file ownership and permissions. Restore it only to the standalone node and configuration identity that created it.

For JDBC, back up the complete dedicated schema, including both OIDCF tables and Flyway history, and the AES keystore as separate protected assets. A database restore without the matching key is unusable. A key restore without the matching database does not recover the registrations. Install the same key on every node before starting the federation entity.

Stop the affected Access nodes while restoring. Never delete or restore the registration-record and lease tables independently; generation and fencing state must remain consistent. Do not manually delete live rows as routine housekeeping because bounded retention owns their safe lifecycle.

After disaster recovery, verify clock synchronization, database migration state, key identity and a complete explicit-registration flow before admitting user traffic.

  • The experimental Vert.x file-cluster mode is not the supported long-term cluster backend.
  • JDBC deployable configuration and real-database CI gates cover SQL Server 2022, MySQL 8.4 LTS and MariaDB 11.4 LTS.
  • The seven-day record-retention and one-day tombstone windows are fixed schema-wide safety values and cannot be changed per federation entity.
  • Cleanup is intentionally bounded. A deployment that creates more expiring configuration fingerprints than its configured cleanup capacity can accumulate eligible rows until capacity is increased.
  • Real-database integration tests are release gates; HSQLDB and SQL-expression tests are not substitutes.

See OpenID Federation RP for initial setup and Configuration reference for all federation-specific fields.