This is a demonstration Node.js application that utilises IBM Cloud Identity (CI) for authentication and FIDO2 services to support a signed photograph mobile application.
- Update your local hosts file to have www.fidophoto.com as a hostname alias (I do this for the loopback address 127.0.0.1) where your Node application is going to listen.
- Make sure you have Node.js installed.
- clone the repo into a directory and cd to that directory
- npm install
- cp .env.example to .env
- edit the .env file and update SECRET, CI_TENANT_ENDPOINT, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET.
- npm run start_local
- Create an API client_id and client_secret (these go into the .env file as OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET) with the following entitlements:
- Authenticate any user
- Manage second-factor authentication enrollment for all users
- Manage second-factor authentication method configuration
- Manage users and standard groups
- Configure an identity source (I used Google SSO) if you want to allow "any user" to self-register into your app. Note this could get noisy, so you may instead want to manually provision users and manage entitlements via groups.
- Configure an "application", using custom OIDC connector.
- Use the authorization code flow WITHOUT PKCE.
- Use application URL https://www.fidophoto.com:9443, and redirect URL https://www.fidophoto.com:9443/callback.
- I would suggest "Do not ask for consent". This provides a more seamless SSO experience.
- Token settings are not important as this application uses an admin-based API client rather than the user access token provided in OIDC. This is done to allow JIT-P of the FIDO definition, custom SCIM attribute creation for the long-lived user access token, automatic provisioning of an application group and adding of any new user to this group, and other admin-only CI APIs.
- No specific attributes need to be added to the id_token.
- Under Access Policies, I customized the identity sources for this application to just "Google". This provides a seamless SSO experience from the fidophoto app through Cloud Identity to Google and back, without the user having to stop at a Cloud Identity selection page for identity sources.
- No API Access is needed as we do not make use of the OIDC-provided delegated access token in this application.
- Under Entitlments, I selected "All users are entitled to this application". Of course you could be more restrictive, particularly if using the Cloud Directory identity source.
- If you want to customize the "department" that the user belongs to as seen when verifying a photo, see ciservices.js, and search for "result.reg.department". You could use any attribute from the user profile (to trace what is avaialable: console.log(JSON.stringify(userSCIMRecord));
- In the CI application definition (under Sign-on -> Access Policy) you could enforce conditional 2FA for SSO to the application if desired.
- In Postman, create globals call fidotools and fidoutils from the corresponding JS files in the phototools/postman/globals directory.
- Import the environment and update the access_token to be your user access token from the Account Settings page in the app.
- Also in the environment, validate the origin in fidoutilsConfig, and the values for hostport and rpId. These vary depending on the hostname used for exposing the application.
- Import the collection, and walk through the APIs from top to bottom, i.e.
- WhoAmI - this uses your access_token, and verifies you can authenticate with it to the application. Make sure this works and that a user profile is returned.
- FetchAttestationOptions - obtains challenge used for credential registration. Again, make sure this works and you get an options object back.
- PostAttestationResult - creates a FIDO credential keypair, and registers the public key. After this has run you should be able to see your registration in the FIDO2 Registrations page of the web app.
- FetchAssertionOptions - obtains an authentication challenge, and the allowedCredentials list which should include the credential just registered.
- PostAssertionResult - completes authentication with the registered credential.
- AFTER all the APIs in the Postman collection have been run successfully, inspect the environment object, and pull out the "Current value" of these two working variables:
last_CredentialId
andlast_privKeyHex
. - Update the command-line application phototools/sign_image.js with:
- The value of
var credentialId
should be replaced with the value fromlast_CredentialId
collected in the previous step. - The value of
var privateKeyHex
should be replaced with the value fromlast_privKeyHex
collected in the previous step. - Verify that
rpId
matches your rpId from the Postman environment. - Set
filename
to point to the jpg file you wish to sign. This can be any existing JPG file you have, but one which contains GPS exif data looks better in the verifier page later. - Set
fileout
to point to the destination filename you wish the signed image to be saved as.
- The value of
- Run the signing app with
node sign_image.js
. On success it completes silently, and yourfileout
should be populated. - In the web app, go to the "Verify a photo" page, and upload the signed image. It should validate successfully and show the user details of the owner of the credential registration.
- A couple of the FIDO searches in
ciservers.js
use inefficient search criteria. This is due to temporary limitations in the search APIs in CI, that are being addressed. When these issues are fixed in CI, the search functions will be updated to more optomised search expressions. - The application has not been tested on large user/data sets, or large numbers of registered credentials. It will need modification to work with searches that return multiple pages of items (i.e. more items than can be returned in a single request/response pair). The CI APIs have support for pagination, however this simple example web application does not have logic in place to leverage that and makes the assumption that all system records can be returned in a single search.
This application makes use of:
- A number of Node.js open source packages, documented in package.json.
- Javascript QRCode generator from node-qrcode. This is used in the account settings page of the web application.