Virtual Keyboard Login with PingOne Advanced Identity Cloud
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.
- The user enters their username.
- 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 hiddenPasswordCallback.
b. The journey verifies the username/PIN and continues as usual (MFA, device checks, etc.). - 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.
- 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.
- Connect to your platform UI
https://<YOUR TENANT>/platformas an administrator. - Browse to Native Consoles > Identity Management. This opens a new window.
- In this new window, browse to Configure > Managed Objects.
- Click on Alpha_user.
- Click on Add a Property and configure it with the following values:
Property Name:custom_PINLabel:PIN (Virtual-Keyboard)Type:String - Click Save.
- 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. - Click Save.
The figure below shows how it should be configure in Native Console.
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.
- In the platform UI, navigate to Developers > Custom Endpoints.
- Click on Create Custom Endpoint.
- Name your endpoint
matchesand paste your routing script (script below). This script will intercept the HTTP GET request, fetch the user'scustom_PINhash, 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
Utilitiesand Tags toPIN. - 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
Utilitiesand Tags toPIN. - In the Settings step, define two outcomes:
trueandfalse. - 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, addPINas 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.
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.
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.









