WebAuthnCredentialRepository
Overview
A critical component of the plugin is the credential repository, which stores and loads credential registrations. The default credential repository utilizes the Shibboleth Storage Service, but it's also possible to utilize other repository implementations by extending the WebAuthnCredentialRepository interface. Any storage service indicated by the IdP property shibboleth.StorageService will be used. However, you can override this by specifying a different bean in the idp.authn.webauthn.StorageService property in conf/authn/webauthn.properties.
In theory, any implementation of a storage service should be compatible, but it's important to consider its capabilities before using it. For example, for testing, you can use client-storage by referencing the bean shibboleth.ClientSessionStorageService. But that will store your credential registrations in the browser and is not portable across browsers—although the credentials will survive an IdP restart so it might be useful during initial testing.
Credential Lookup Caches(1.3.0)
Some WebAuthn assertion validation and attestation registration functions require credential lookups by either userHandle or credentialId. These operations can be costly when performed over the storage service by brute force since credential registrations are stored as serialized JSON documents. To enhance lookup efficiency, version 1.3.0 introduces two secondary index caches to the default credential repository implementation that stores mappings between IdP users, credential userHandles, and credentialIds. These caches are enabled by default, but can be toggled using the property idp.authn.webauthn.StorageService.cache.enable in conf/authn/webauthn.properties. Both caches evict entries after a certain time has passed since they were last accessed. The expiry can be configured by adjusting the property idp.authn.webauthn.StorageService.cache.expireAfterAccess in the conf/authn/webauthn.properties file.
JDBC Storage Option
In production, you may want to consider using a JDBC storage option. Assuming you do not already have a database suitable for use with the Shibboleth Storage Service (if you do, you can skip to step 3), then:
Install the JDBC storage plugin and create a new schema/database (e.g.
webauthn) and a new table (e.g.webauthn.StorageRecords):
Configure a suitable JDBC connection by following the JDBC storage plugin documentation.
Finally, set the storage service bean you want to use for WebAuthn using the property idp.authn.webauthn.StorageService in conf/authn/webauthn.properties
Accelerating JDBC Queries(1.3.0)
By default, queries to the storage service are not optimized for database lookups—this is a feature that makes all storage service implementations generic and interoperable. However, if you opt for a database storage solution, you may want or need to enable the JDBCAccelerator, especially as the number of credential registrations increases. The accelerator replaces some of the lookup features of the default credential repository with specialised, efficient lookups designed for databases that support SQL/JSON queries.
To enable the accelerator, you MUST ensure the following:
You are using a JDBC storage service for the credential repository (see the previous section).
You configure both the JDBCStorageService and JDBCAccelerator to use the same dataSource.
You are running a RDBMS that is compatible with SQL/JSON (SQL:2016) and, if you use the default queries, supports the JSON_TABLE function. At present, we are aware of support from:
MySQL >= v8 (2018)
Mariadb >= v10.6 (2021)
PostgreSQL >= 17 (2024)SQL:201SQL:2016
To enable, you need to define a shibboleth.authn.WebAuthn.JDBCAccelerator bean in the global context (conf/global.xml).
<bean id="WebAuthnJDBCAccelerator" parent="shibboleth.authn.WebAuthn.JDBCAccelerator"
p:dataSource-ref="JDBCDataSource"/> This will get picked up (by name) and injected into the credential repository by the credential repository factory. You can change the name of the bean by setting the property idp.authn.webauthn.StorageService.jdbcAccelerator in conf/authn/webauthn.properties.
The accelerator is designed to search within the JSON-serialised storage records of credential registrations to facilitate the lookup of credentials by credentialId and userHandle. The default queries use the JSON_TABLE function as shown in the table below:
Lookup | SQL |
|---|---|
userHandle | SELECT version, expires, value FROM StorageRecords, JSON_TABLE(value,'$[*]' COLUMNS (uid text PATH '$.userIdentity.id')) AS cred WHERE context=? AND cred.uid = ? |
credentialId | SELECT version, expires, value FROM StorageRecords, JSON_TABLE(value,'$[*]' COLUMNS (cid text PATH '$.credential.credentialId')) AS cred WHERE context=? AND cred.cid = ?; |
If you have a more efficient query or need adjustments for your RDBMS, you can define your own SQL in the accelerator:
<bean id="WebAuthnJDBCAccelerator" parent="shibboleth.authn.WebAuthn.JDBCAccelerator"
p:dataSource-ref="JDBCDataSource"
p:queryRecordsByUserHandleSQL="..."
p:queryRecordsByCredentialIdSQL="..."/>The SQL must SELECT the columns: version, expires, value, in that order. The results of the query should be credentials that match to the given userHandle, or the given credentialId. The correctness of the results returned are checked by the credential repository.
Credential Registration Data Model
The credential registration data model is captured by the CredentialRecord. This record is serialized into JSON for storage. An example record is shown below.