Passkey Setup on Main Web App
This guide will walk you through the setup and installation process for the
keyri-auth-core
package. It is responsible for everything from creating your
QR code to listening to events from the QR code to handling front-end passkey
activities.
Installation
To install the keyri-auth-core
package, run the following command:
npm i keyri-auth-core
This command installs the package and its dependencies into your project.
Demonstration
If you want to see a live example of how the keyri-auth-core
package works all
together, you can clone the following repository and install the required
dependencies:
git clone https://github.com/Keyri-Co/keyri-hello-world
After cloning the repository, navigate to the project folder and run:
npm install
This command installs the necessary dependencies for the demonstration project. Once the installation is complete, you can explore the project to see how the keyri-auth-core package is utilized.
QR Code Widget Setup and Styling
The following outlines how to set up an iframe to handle QR code creation for user authentication. We will discuss event handling in the following section titled "Event Handling."
Create an HTML file and add the following boilerplate code:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
crossorigin="anonymous"
media="all"
rel="stylesheet"
href="./css/qr.css"
/>
</head>
<body>
<div class="pre-blurry" id="qr-target"></div>
<div id="qr-lay-over"></div>
</body>
</html>
Add the script tag to import the library and set up the iframe:
<script src="./dist/index.min.js"></script>
<script type="module">
// Note: If you're using a build tool like webpack, you can
// import `IFrameManager` from 'keyri-auth-core'. If you're
// not using a modular approach, the global `KeyriFrontEnd`
// is exposed by the library.
const { IFrameManager } = KeyriFrontEnd;
const iFrameManager = new IFrameManager();
await iFrameManager.start();
</script>
This code initializes the iframe manager for QR code authentication. The IFrameManager is either imported from the keyri-auth-core library or accessed through the global KeyriFrontEnd object, depending on your project setup. The iframe manager is started using the start() method.
QR Code Styling (Optional)
Below is an example of CSS styles that work well with the QR code iframe. Please note that these styles are not mandatory and can be customized to fit your preferences.
/*
* ***************************************************************************
* CSS SUGGESTIONS THAT WE HAVE FOUND TO WORK WELL
* BUT ARE BY NO MEANS MANDATORY
* ***************************************************************************
*/
/* Set the dimensions and position of the QR overlay */
#qr-lay-over {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
/* Set the dimensions and background size for the QR target */
#qr-target {
height: 100%;
width: 100%;
z-index: -1;
background-size: 33%;
}
/* Define the blurOn animation for applying blur effect */
@keyframes blurOn {
0% {
filter: opacity(1);
}
100% {
filter: opacity(0.3) blur(7px);
}
}
/* Define the blurOff animation for removing blur effect */
@keyframes blurOff {
0% {
filter: opacity(0.3) blur(7px);
}
100% {
filter: opacity(1);
}
}
/* Apply initial blur effect to the QR target */
.pre-blurry {
filter: opacity(0.3) blur(7px);
}
/* Apply the blurOn animation on the QR target when it becomes blurry */
.blurry {
animation: blurOn 0.15s;
filter: opacity(0.3) blur(7px);
}
/* Apply the blurOff animation on the QR target when it becomes un-blurred */
.off.blurry {
animation: blurOff 0.15s;
filter: opacity(1);
}
/* Set the reload background image and its properties */
.reload {
background: url('https://static.keyri.com/library-keyri-connect/reload.svg');
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
background-size: contain;
}
/* Set the check background image and its properties */
.check {
background: url('https://static.keyri.com/library-keyri-connect/check.svg');
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
background-size: contain;
}
/* Define the color animation for the QR code elements */
rect:not(:first-child) {
animation: col 5s linear infinite;
transform-origin: center;
transform-box: fill-box;
}
/* Set the color animation keyframes */
@keyframes col {
0% {
fill: black;
}
100% {
fill: blue;
}
}
API Event Handling
The EventHandler
module simplifies the handling of events coming from the QR
iframe. This guide demonstrates how to listen for each event.
Usage
Import the EventManager
class:
const { EventManager } = KeyriFrontEnd;
window.eventManager = new EventManager(window);
Handle Keyri App-Based Validation
Set a handler for standard Keyri App-Based Validation. The detail.data
attribute of the event object contains the decrypted payload from mobile. In a
production environment, you would send this to your server for verification and
session management.
window.addEventListener('qr_event_session_validate', async (evt) => {
// Notify the user of the event, e.g., through a modal or by reloading the page
alert('SESSION VALIDATE!!');
// Log the session validation event
console.log('SESSION VALIDATE', evt.detail.data);
});
Handle Risk Metrics
If you are signed up for risk metrics, a base64 encoded risk detail object is made available to the browser, signed by the API, and twice by the browser. This can be sent to the server for analysis or decision-making.
window.addEventListener('qr_event_risk_data', async (evt) => {
// Extract the risk data
let data = evt.detail.data.information.data;
// Log the risk data
console.log('RISK DATA', JSON.parse(atob(data)));
});
Handle Errors from the QR
Set a handler for any errors coming out of the QR.
window.addEventListener('qr_event_socket_error', async (evt) => {
// Display the error to the user
alert(evt?.detail?.data);
});
Listen for Passkey Framework Events
User Logins
Listen for events coming from the passkey framework for user logins.
window.addEventListener('qr_event_appless_login', async (evt) => {
// Parse Data from the Event
let data = JSON.parse(evt.detail.data);
// Hit the Relying Party's API with the provided information
let output = await localAppless.rp_validate_data(data);
// TODO: ERROR HANDLING!
// Let the user know that everything went to plan
alert('OK!');
});
Registrations
window.addEventListener('qr_event_appless_registration', async (evt) => {
// Log the event
console.log(evt);
// Notify the user
alert('PASSKEY LOG IN');
});
Export
Export the module as default:
export default true;
Passkey Configuration
This guide demonstrates how to use the ApplessLocal
class to handle
user-triggered events in your login page. The ApplessLocal
class manages the
following actions:
- Local Passkey Creation
- Local Passkey Verification
- Mobile Web Passkey Creation
- Mobile Web Passkey Verification
Usage
Import the ApplessLocal
class:
If you're using WebKit:
import { ApplessLocal } from 'keyri-auth-core';
Otherwise, access it through the global KeyriFrontEnd
object:
const { ApplessLocal } = KeyriFrontEnd;
Define the URL for mobile app authentication:
const MobileAppUrl = 'http://localhost/mobile.html';
Instantiate the ApplessLocal
class with the API Routes:
let localAppless = new ApplessLocal(
'./api/passkey/register',
'./api/passkey/login'
);
Register a Logged-in User
Create an async function to register a logged-in user:
async function makeLocalPassKey(e) {
// Prevent the default behavior of the event
e.preventDefault();
// Replace {USER-SESSION-TOKEN} with the actual session token
let idToken = '{USER-SESSION-TOKEN}';
// Register the user using the ApplessLocal instance
await localAppless.register(idToken);
// In a production environment, you might reload the page or show a modal
// to inform the user about the successful registration
alert('LOCAL PASS KEY CREATED');
}
Authenticate an Existing User
Create an async function to authenticate an existing user:
async function authenticateLocalPasskey(e) {
// Prevent the default behavior of the event
e.preventDefault();
// Authenticate the user using the ApplessLocal instance
let authData = await localAppless.authenticate(true);
// In a production environment, you might reload the page
alert(authData.message);
}
Generate a URL for the iframe (Mobile Passkey)
Create an async function to generate a URL for the iframe to register a mobile device:
async function makeMobilePassKey(e) {
// Prevent the default behavior of the event
e.preventDefault();
// Replace {SESSION_TOKEN} with the actual session token
const SESSION_TOKEN = '{SESSION_TOKEN}';
// Get the iframe element to update for user registration
const QR_IFRAME = document.getElementById('qr-iframe');
// Prompt the user for a one-time PIN to prevent shoulder surfing
const ONE_TIME_PIN = prompt('Pick a number between 1 and 100');
// Build the query string for the iframe using the ApplessLocal instance
let QUERY_STRING = localAppless.registerMobile(SESSION_TOKEN, ONE_TIME_PIN);
let TMP_SRC = `./qr.html?mobile=${MobileAppUrl}&qsd=false&${QUERY_STRING}`;
// Set the 'src' attribute of the iframe, which takes the user to the mobile page
// where they can enter their passcode, submit biometrics, and register their device
QR_IFRAME.src = TMP_SRC;
}
Export Functions
export { makeLocalPassKey, authenticateLocalPasskey, makeMobilePassKey };
By following this guide, you can handle user-triggered events on your login page
using the ApplessLocal
class.
Login Setup for QR Passkeys
This example demonstrates how to create a login page with KeyriQR. It provides various authentication methods, including local passkeys, mobile registration, and app-based authentication.
HTML Structure
The HTML structure includes an iframe for displaying the QR code, and a series of buttons for the available authentication methods.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>QR - Demo</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
crossorigin="anonymous"
media="all"
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
/>
</head>
<body>
<main class="text-center">
<div>
<iframe
scrolling="no"
frameborder="0"
height="500"
width="500"
id="qr-iframe"
></iframe>
</div>
<a
class="btn btn-lg btn-primary"
style="width: 100%; margin-bottom: 20px;"
id="makeLocalPassKey"
>CREATE LOCAL PASSKEY</a
>
<a
class="btn btn-lg btn-primary"
style="width: 100%; margin-bottom: 20px;"
id="authenticateLocalPasskey"
>SIGN-IN WITH LOCAL PASSKEY</a
>
<a
class="btn btn-lg btn-primary"
style="width: 100%; margin-bottom: 20px;"
id="registerAPhone"
>REGISTER A PHONE</a
>
<a
class="btn btn-lg btn-primary"
style="width: 100%; margin-bottom: 20px;"
id="signInWithPhone"
>SIGN IN WITH PHONE</a
>
<a
class="btn btn-lg btn-primary"
style="width: 100%; margin-bottom: 20px;"
id="signInWithApp"
>SIGN IN WITH APP</a
>
</main>
</body>
</html>
JavaScript
The script handles the iframe source, imports the required modules, and assigns event listeners to the buttons.
// Import event manager for handling events from the iframe
import * as EventManager from './mjs/eventManager.mjs';
// Import environment-specific variables, such as host, app key, and mobile URL
import { host, appKey, mobile } from './mjs/keys.mjs';
// Import local and appless authentication methods
import {
makeLocalPassKey,
authenticateLocalPasskey,
makeMobilePassKey,
} from './mjs/localAppless.mjs';
// Build the iframe source URL with the necessary query parameters
const iframeSrc = `./qr.html?qsd=false&Host=${host}&appKey=${appKey}&mobile=${mobile}`;
// Set the source of the iframe to display the QR code
const iframe = document.querySelector('iframe');
iframe.src = iframeSrc;
// Assign event listeners to the buttons for the various authentication methods
document.getElementById('makeLocalPassKey').onclick = async (e) => {
await makeLocalPassKey(e);
};
document.getElementById('authenticateLocalPasskey').onclick = async (e) => {
await authenticateLocalPasskey(e);
};
document.getElementById('registerAPhone').onclick = async (e) => {
console.log('ding!');
await makeMobilePassKey(e);
};
document.getElementById('signInWithPhone').onclick = async (e) => {
alert('scan qr with a REGISTERED Phone');
};
document.getElementById('signInWithApp').onclick = async (e) => {
alert('scan qr with your APP!');
};
This portion of the JavaScript code assigns event listeners to the buttons for:
- Registering a Passkey locally
- Using a Passkey locally
- Registering a Passkey on your phone via QR
- Scanning a QR with a Passkey registered phone
- Scanning a QR with an app