Sitemap
Stéphane Orluc

Stéphane Orluc

Principal Sales Engineer at Ping Identity (h-ForgeRock) www.linkedin.com/in/sorluc (posts are my own and do not necessarily reflect the views of my company)

Virtual Keyboard Login with PingOne Advanced Identity Cloud

5 min readMay 19, 2026

--

If you’ve ever had to log in from a public kiosk or an untrusted device, you’ve probably worried about keyloggers. In a previous post, I showed how to implement an Unlock Pattern with PingOne Advanced Identity Cloud (now PingOne Advanced Identity Cloud) to address alternative authentication methods. In the same spirit, a virtual keyboard can reduce risk by avoiding physical keystrokes altogether. This feature is also highly frequent in French financial services, and I’m regularly asked how to implement it. I won’t debate here how secure or user-friendly this pattern is — that’s a separate discussion. Instead, in this post, we’ll build a clean, virtual keyboard login using PingOne Advanced Identity Cloud. We’ll do it with a minimal, reusable pattern that works with hosted pages.

How it works

User journey

We’ll keep the journey simple, allowing the user to log in or to register and set their PIN.

  1. The user enters their username.
  2. If the user exists and their PIN is defined : Instead of a normal password field, we display a virtual keyboard UI.
    a. The virtual keyboard collects the PIN client-side and posts it back through a hidden PasswordCallback.
    b. The journey verifies the username/PIN and continues as usual (MFA, device checks, etc.).
  3. If the user exists but no PIN has been defined : We display a virtual keyboard UI to collect and store the new PIN in the user profile.
  4. If the user doesn’t exist : Another form is displayed to register and set a PIN using the virtual keyboard.

User profile & Endpoint

In this integration, the PIN code is decoupled from the main user password. We will extend the user schema with a custom attribute custom_PIN stored as a hashed String (SHA-512).

To validate the PIN without exposing hashes to the client, we will create a Custom Endpoint. This endpoint extends the REST API, allowing us to check if the PIN entered by the user matches the hashed PIN stored in their profile via a simple HTTP GET request.

Step-by-Step Implementation

Step 1: Extend the user schema

We need to add the custom_PIN attribute to the User object schema using the native Identity Management console.

  1. Connect to your platform UI https://<YOUR TENANT>/platform as an administrator.
  2. Browse to Native Consoles > Identity Management. This opens a new window.
  3. In this new window, browse to Configure > Managed Objects.
  4. Click on Alpha_user.
  5. Click on Add a Property and configure it with the following values:
    Property Name: custom_PIN
    Label: PIN (Virtual-Keyboard)
    Type: String
  6. Click Save.
  7. Click on your newly created property custom_PIN to edit its advanced settings:
    Leave the Detail, Validation, and Scripts tabs as they are.
    Go to the Privacy & Encryption tab.
    Toggle Hashed to active.
    Select SHA-512 in the encryption algorithm dropdown.
  8. Click Save.

The figure below shows how it should be configure in Native Console.

Press enter or click to view image in full size
Native Console — custom Pin attribute definition

Step 2: Create a custom endpoint to check the PIN

We will expose a custom REST endpoint named /matches that accepts a userId and a pin, and returns a boolean response.

  1. In the platform UI, navigate to Developers > Custom Endpoints.
  2. Click on Create Custom Endpoint.
  3. Name your endpoint matches and paste your routing script (script below). This script will intercept the HTTP GET request, fetch the user's custom_PIN hash, hash the incoming query parameter, and compare them.

Here is an example of how you will be able to test your endpoint once saved:

curl --location \
--request GET \
--header "accept: application/json" \
--header "authorization: Bearer <YOUR_TOKEN>" \
"https://<YOUR TENANT>/platform/endpoint/matches?pin=123456&userId=4511427c-67a7–40bb-bdcb-590ebf76a8bd"

Expected JSON response:

{
"_id": "4511427c-67a7-40bb-bdcb-590ebf76a8bd",
"res": true
}

Step 3: Create the Custom Nodes

To keep our journey modular and reusable, we will build two Custom Nodes.

Node 1: DisplayVirtualKeyboard

Get Stéphane Orluc’s stories in your inbox

Join Medium for free to get updates from this writer.

This node triggers the frontend rendering of our keypad. It bundles a PasswordCallback (which we will hide via CSS/JS) and a ScriptTextOutputCallback to inject our keyboard logic.

  • Browse to Journeys > Custom Nodes and click on Add New Custom Node.
  • In the Details step, name it DisplayVirtualKeyBoard.
  • Set Category to Utilities and Tags to PIN.
  • In the Settings step, define a single outcome: true.
  • In the Script step, paste your UI injection script (script below) and click Save.

Node 2: CheckPINHash

This node consumes the submitted PIN value and calls our custom endpoint to validate it against the profile.

  • Click again on Add New Custom Node.
  • Name it CheckPINHash.
  • Set Category to Utilities and Tags to PIN.
  • In the Settings step, define two outcomes: true and false.
  • Paste your validation script (script below) and click Save.

Step 4: Build the Journey

Now, let’s assemble these blocks into a visual authentication tree.

  • Navigate to Journeys > Journeys and click on New Journey.
  • Name it virtualKeyboard, select Alpha realms — Users as the Identity Object, add PIN as a tag, and click Save.
  • Drag a Page Node onto the canvas. Name it Login Form.
  • In Page Header, enter Login Page.
  • In Page Description, enter Enter your login and type your PIN.
  • Inside this Page Node, drop a Platform Username (or Username Collector) node.
  • Next, drag your custom DisplayVirtualKeyboard node into the same Page Node right below the username. This bundling ensures both elements render on the same screen.
  • Route the exit of the Page Node to your custom CheckPINHash node.
  • Connect the outputs of CheckPINHash:
  • Connect the true outcome to the green Success exit node.
  • Connect the false outcome to the red Failure exit node.

Now, your journey should look like the following figure.

Press enter or click to view image in full size
User Journey — demo user journey using a virtual keyboard

Important Security Note: Don’t forget to update your platform’s Content Security Policy (CSP) settings if your frontend virtual keyboard script relies on external CDNs or specific inline scripts. Documentation here: https://docs.pingidentity.com/pingoneaic/tenants/content-security-policy.html

Demonstration

The video below shows the result from the end-user’s perspective.

Press enter or click to view image in full size
Virtual keyboard login — user experience

Conclusion

By leveraging a Custom Endpoint, two modular Custom Nodes, and a single Page Node, you can deliver a secure virtual-keyboard login experience tailored for untrusted environments. This architecture keeps your backend logic decoupled from your frontend UI while providing a seamless user experience.

Further reading

--

--

Stéphane Orluc
Stéphane Orluc

Written by Stéphane Orluc

Principal Sales Engineer at Ping Identity (h-ForgeRock) www.linkedin.com/in/sorluc (posts are my own and do not necessarily reflect the views of my company)