IBM Verify

IBM Verify

Join this online user group to communicate across Security product users and IBM experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only

Using FIDO Java APIs from Infomap on ISVA

By Shane Weeden posted Fri June 23, 2023 01:54 AM

  

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:

Note that the way in which the page initially loads follows a very similar pattern to the registrations page. Take a look at the fido_infomap_login.js for startup details. 
There are two different ways in which WebAuthn login is exposed on this page. The first is the Autofill Login, and this will only be displayed if the feature is supported on your OS/browser combination. For example, at the time of writing this article, the feature is not supported in Firefox.

Autofill passkey login

When it is available, then autocomplete can be used to sign in. Here's an example of what that looks like on Safari:
If the autofill capability is supported, during page startup a background call is made to navigator.credentials.get with mediation: "conditional". This requires that the page contain an input field annotated with autocomplete="webauthn" (or "username webauthn"). The login.html page template has this set up. Note that you wouldn't normally just display the field for only the purpose of passkey login. Typically this field would be displayed as part of a broader "identifier first login" approach, with passkey login being one option. Manually entering a username and offering a "Continue" button would be more common, and then deciding how you want the user to login based on some server-side discovery of the username. Many sites now behave this way and ISVA offers a scenario wizard to set up exactly this type of example flow (under AAC -> Authentication -> Scenarios):
This offers a much more advanced set of capabilities than the simple passkey-based examples shown in this blog, and I would encourage you to explore this authentication pattern for your own website.

Modal passkey login

The other way to invoke WebAuthn is via the modal dialog, which is triggered on this demonstration policy by pressing the Login with a passkey button. Note that this requires the autofill call to navigator.credentials.get to be aborted first (if it was started), then the modal call to navigator.credentials.get can be issued. There is extensive commenting in the fido_infomap_login.js describing how this is done. Look for the modalLogin() function.
Regardless of the method of invoking WebAuthn, one thing that is interesting to note is that because the login flow results in an EAI login to the WRP, its best to perform a complete FORM POST to the Infomap with the WebAuthn response parameters (rather than an AJAX call) so that the browser can be redirected to the originally requested protected resource after login completes. This is why you'll see a hidden form at the bottom of login.html that is programmatically submitted once WebAuthn completes successfully. Look at the processAssertionResponse function to see how this is done.
Errors during login are displayed in a similar way to the registrations page - in a div at the bottom of the screen. Sometimes the errors are client-side, and are directly retrieved and displayed as part of JS within the page. Server-side errors might also occur (eg. attempting to login with a passkey when the registration has deleted from the server), and in this case the way the demonstration policy handles those errors is to return them in the page template for display on page load. You can see how this is done at the end of the loginStartup function call.

The wrap

The intention of this blog post and the supporting assets was to show you how to utilise the capabilites of the FIDO Java APIs offered within ISVA. In many cases you'll never need to do this, and can make use of built-in registration and authentication mechanism features. For those implementing very custom logic however, including building out your own identifier first authentication flow, I hope that you find these assets useful and as always I look forward to your feedback.

0 comments
67 views

Permalink