Each release of IBM Security Verify Access (ISVA) we continue to iterate on high value capabilities both in our exposed user interfaces and also in our underlying APIs. One of several innovations since my original 4-part blog series on FIDO in ISAM, is that we have introduced Java APIs for FIDO that allow you to develop customized registraiton and authentication experiences in Infomap-based AAC authentication policies. This article presents two such custom Infomap-based policies - one for registration and the other for authentication that can be used as references for writing alternatives to the built in registration and authentication mechanisms. They are bare-bones implementations though - designed to show just enough to get you started with building your own experience.
A fair amount of existing experience is assumed for this advanced article - if you need help getting started then I suggest you first read:
ISVA System setup
In my ISVA installation environment, I have set up a Web Reverse Proxy (WRP, aka WebSEAL) with the advanced access component configured using the WRP wizard. I have also the advanced custom parameter: sps.authService.policyKickoffMethod = path
I have blogged about this parameter in the past - basically this parameter allows URL-based access to individual authentication service policies which makes it easier to use ISVA ACL and POP attachments to control access to individual policies. This is particularly advantageous in our FIDO policies use case as one of our policies (the registration policy) is going to require authenticated access (so that you are logged in before registering FIDO credentials), and the other is going to allow unauthenticated access (for the purposes of logging in).
Assets
Various template pages and Javascript mapping rules are used in this article for the Infomaps, and are available at:
https://github.com/sbweeden/blog_assets/tree/master/fido_infomap
At the time of writing, this is the complete list of file assets for this article:
./template_pages/C/static/fido_infomap_login.js
./template_pages/C/static/fido_infomap_helper.js
./template_pages/C/static/fido_infomap_registrations.js
./template_pages/C/authsvc/authenticator/fido_infomap/login.html
./template_pages/C/authsvc/authenticator/fido_infomap/registrations.html
./template_pages/C/authsvc/authenticator/fido_infomap/jsonresponse.html
./template_pages/C/authsvc/authenticator/fido_infomap/jsonresponse.json
./template_pages/C/authsvc/authenticator/fido_infomap/error.html
./mapping_rules/FIDOInfomapRegistrations.js
./mapping_rules/FIDOInfomapLogin.js
./mapping_rules/FIDOInfomapConfig.js
./mapping_rules/FIDOInfomapUtils.js
Configuration
Pre-requisite configuration
You should have a FIDO2 Relying Party configured in your advanced access control, and be able to perform basic registraiton and sign-in functions using the built in ISVA capabilities, as described in the first article on FIDO in my series:
That is the minimum pre-requisite for following along with the rest of this blog article.
Configuration of the assets provided in this article
The way I've laid out this article is that this section will describe all of the necessary configuration for both the registration and login Infomap policies, then I'll explain in a bit more detail how each works.
Update the FIDOInfomapConfig.js
mapping rule to contain the relying-party ID used for your FIDO2 Configuration.
Install all of the template page files and mapping rules into your ISVA instance, using the LMI. For each mapping rule, be sure to use the name of the filename (less the .js extension), as some mapping rules include the others, and this is the only way the imports will work properly.
Create the following AAC Infomap authentication mechanisms. No page template need be specified in the configuration of each mechanism - just the mapping rule, since the Infomaps themselves set the page template to use in code.
AAC Authentication Mechanism Name |
Mechanism URI |
Mapping Rule |
fido_infomap_registrations |
urn:ibm:security:authentication:asf:mechanism:fido_infomap_registrations |
FIDOInfomapRegistrations |
fido_infomap_login |
urn:ibm:security:authentication:asf:mechanism:fido_infomap_login |
FIDOInfomapLogin |
Create the following AAC Infomap authentication policies. Each policy has just one mechanism (the associated infomap).
AAC Authentication Mechanism Name |
Mechanism URI |
Mechanims List |
fido_infomap_registrations |
urn:ibm:security:authentication:asf:fido_infomap_registrations |
fido_infomap_registrations |
fido_infomap_login |
urn:ibm:security:authentication:asf:fido_infomap_login |
fido_infomap_login |
Here's a screenshot of each policy from my system:
Use pdadmin to attach ACLs to each policy - note that we explicitly require authentication for the registrations policy, and allow unauthenticated access to the login policy. The authsvc
and apiauthsvc
paths are required. Adjust the objectspace name for your WRP as needed:
pdadmin sec_master> object list /WebSEAL
/WebSEAL/localhost-default
pdadmin sec_master> acl attach /WebSEAL/localhost-default/mga/sps/authsvc/policy/fido_infomap_registrations isam_authsvc_anyauth
pdadmin sec_master> acl attach /WebSEAL/localhost-default/mga/sps/apiauthsvc/policy/fido_infomap_registrations isam_authsvc_rest
pdadmin sec_master> acl attach /WebSEAL/localhost-default/mga/sps/authsvc/policy/fido_infomap_login isam_authsvc_unauth
pdadmin sec_master> acl attach /WebSEAL/localhost-default/mga/sps/apiauthsvc/policy/fido_infomap_login isam_authsvc_rest_unauth
Of course you can refine this policy as needed. The point here is that you should be authenticated to view and create new passkey registrations, and the login policy should allow unauthenticated access because indeed you use it to login!
About the FIDO APIs being used
The Infomap policies in this article make use of the FIDO2RegistrationHelper, FIDO2Registration, FIDOClientManager and LocalFIDOClient classes.
The registration infomap makes use of the FIDO2RegistrationHelper
and the FIDO2Registration
classes to retrieve the list of current registrations for a user. Take a look at the FIDOInfomapRegistration.js
source for example usage, particularly in the getRegistrations()
function.
Both the registration and login infomaps are provided with a global instance variable of the FIDOClientManager
called fido2ClientManager
.
You can use the fido2ClientManager
to instantiate an instance of the LocalFIDOClient
class for a particular relying-party id, and you can see that being done in the FIDOInfomapUtils.js
code:
var lfc = fido2ClientManager.getClient(RPID);
The LocalFIDOClient instance then has access to the four major operational APIs for FIDO, plus a utility to resolve RPID to an internal UUID which is used for the FIDO server endpoints. You can read more about the FIDO server endpoints in my article: ISAM FIDO2 – Using the FIDO2 server endpoints (Part 4 of 4)
The string input to each of the APIs should be stringified JSON in the same format as can be accepted by the FIDO2 server endpoints. You can see how these are constructed in the example source provided with this article.
The string output will also be stringified JSON - just the same kinds of responses you would get from the FIDO2 server endpoints.
The Registrations Policy
To access the registrations policy use the URL (update for your hostname): https://www.iamlab.ibm.com/mga/sps/authsvc/policy/fido_infomap_registrations
You should see a screen similar to this:
Depending on your browser and operating system, the WebAuthn Feature Discovery table may show different values, and of course you may or may not have any existing registrations.
Try out the Register passkey
button. This will result in an AJAX call to the server to retrieve a fresh challenge and timeout, then invocation of the WebAuthn navigator.credentials.get
API to prompt for passkey registration. Any existing registrations will be included in the allowCredentials
list, and if you try and register the same authenticator again, you will get a WebAuthn erorr. Errors are displayed in an otherwise-hidden div at the bottom of the page.
How the registrations policy works
The registrations policy will initially send the registrations.html
page to the browser, with a macro populating any existing registrations. The html page and its associated script file are written to be compatible with modern content security policy which disallows inline script. That is why all the Javacsript supporting code is kept outside the HTML files. Take a look at the fido_infomap_registrations.js
page template file to see what happens when this page loads.
The start of this file contains:
// read existing registrations JSON from the macro embedded in the registrations.html page
var registrationsPageJSON = JSON.parse(htmlDecode(document.getElementById('fido_registrations_tags').textContent));
var fidoRegistrations = registrationsPageJSON.fidoRegistrations;
console.log("fidoRegistrations: " + JSON.stringify(fidoRegistrations));
// set up an onload function for this page
window.addEventListener("load", registrationsStartup);
After parsing any inital registrations provided by macro substitution into a local variable, then on page load the registrationsStartup
function is called. This function is as follows:
function registrationsStartup() {
// perform discovery before we do anything else
performWebAuthnFeatureDiscovery()
.then((x) => {
// render feature table
renderFeatureTable();
// set up a handler for the register button
$('#registerButton').click(() => { register(); });
// populate registrations table
renderRegistrations();
});
}
As the WebAuthn discovery APIs are promise-based, we first perform discovery, then setup the rest of the page.
The "feature table" is the table at the top of the page (the same table exists in the login.html
page template) and is included for informational purposes only.
An onclick handler is setup for the registration button, then the registrations table is populated with any registrations sent from the server.
You can read the rest of the code yourself to discover what happens when the registration button is invoked (look for the register()
function). Essentially the following events take place:
- The Infomap policy is called via an AJAX function to retrieve a fresh server challenge and timeout. In the
FIDOInfomapRegistrations.js
Infomap mapping rule (look for action.equals("getAttestationOptions"
)
), the LocalFIDOClient
is used to call the FIDO APIs directly to obtain required options, and these are sent via a JSON response using the page template jsonresponse.json
.
- The attestation options are then massaged into the format required for the WebAuthn call to
navigator.credentials.create
, and that API is invoked.
- On successful completion, the registration data is uploaded to the Infomap again (see
processAttestationResponse
), where the Infomap then uses the LocalFIDOClient
to call attestationResult
. Look for action.equals("processAttestationResponse")
in the FIDOInfomapRegistrations.js
for this.
- If the registration succeeds, a new list of registrations is sent back to the browser, and this is rendered in the table.
- If the registration fails, an error response is sent back to the browser and is displayed to the user. A registration might fail at the server, for example, if the FIDO2 relying party configuration is configured to require metadata (and the registration doesn't match), or if some other condition in the mediator returns an error.
The Login Policy
To access the login policy use the URL (update for your hostname): https://www.iamlab.ibm.com/mga/sps/authsvc/policy/fido_infomap_login
You should see a screen similar to this: