Keyri Banno QR Login Integration Guide
Incorporating Keyri QR login into a Banno Digital Banking environment entails
copying the ConsumerJwt
of an authenticated session in your mobile app,
sending it as the payload through the Keyri API once the user scans the login QR
code, and once the Keyri QR Widget on your web app's login page receives the
payload, saving the ConsumerJwt
as a cookie.
Usage notes:
-
Keyri QR login will not require users to complete separate MFA steps on the web platform, since it is extending the logged-in session already on the user's mobile app that has previously completed MFA
-
Your user needs to be already logged into the Banno platform in your app when the Keyri function is called once the QR code is scanned by your app
-
You must have a Keyri account with a service registered under the domain name on which you will be showing the login QR code. Register for a Keyri account here: https://app.keyri.com/sign-up (opens in a new tab)
Web
First, add the Keyri Widget iFrame to your login page in a div of your choosing:
-
Serve KeyriQR.html (available here (opens in a new tab)) from the same origin as your login page (e.g., a
/public
directory) -
RECOMMENDED: serve everything on your login's origin with the header X-Frame-Options: SAMEORIGIN (examples of how to do so here (opens in a new tab)) (opens in a new tab)
-
Embed an iframe in your login page in the desired DOM element with the path to your copy of KeyriQR.html as its
src
...
<iframe
src="./KeyriQR.html"
id="qr-frame"
frameborder="0"
height="300"
width="300"
scrolling="no"
style="border: solid 5px white;"
></iframe>
...
Now add handling for the ConsumerJwt
, triggered by an event listener tuned for
the Keyri Widget:
window.addEventListener('message', (evt) => {
// Ensure that the event is coming from the Keyri Widget
if (
evt.data.keyri &&
evt.data.data &&
document.location.origin == evt.origin
) {
const { data } = evt;
if (!data.error) {
// Extract the ConsumerJwt string from the payload
ConsumerJwt = data.data.ConsumerJwt;
// Set it as a cookie
document.cookie = `ConsumerJwt=${ConsumerJwt}; SameSite=None; path=/; Secure`;
// Reload to advance the user to the logged-in view
document.location.reload();
} else {
showErrorModal({ title: 'Uh Oh', body: 'Helpful Error Message' });
}
}
});
Mobile
Mobile integration entails loading the ConsumerJwt
as the payload when you
call the initiateQrSession()
Keyri method. Below is an example in Swift that
uses a QR scanner included in the Keyri mobile SDK. Note that
initiateQrSession
can be triggered either through the scanner provided by the
Keyri SDK, your own scanner, and/or through a deep link (user scanning the Keyri
QR code from their phone's camera app). All options for triggering the QR login
process can be found in Mobile SDK documentation
func handleDisplayingScanner() {
let scanner = Scanner()
scanner.completion = { str in
guard let url = URL(string: str) else { return nil }
process(url)
}
scanner.show()
}
func process(url: URL) {
let sessionId = URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems?.first(where: { $0.name == "sessionId" })?.value ?? ""
let payload = ConsumerJwt // Define ConsumerJwt before this function by extracting it from your app storage or environment
let appKey = "App key here" // Get this value from the Keyri Developer Portal
let publicApiKey = "Your key here" // Get this optional value from the Keyri Developer Portal
let serviceEncryptionKey = "Your key here" // Get this optional value from the Keyri Developer Portal
let keyri = KeyriInterface(appKey: appKey, publicApiKey: publicApiKey, serviceEncryptionKey: serviceEncryptionKey) // Be sure to import the SDK at the top of the file
keyri.initiateQrSession(sessionId: sessionId, publicUserId: "TestUser") { res in
switch res {
case .success(var session):
// You can optionally create a custom screen and pass the session ID there. We recommend this approach for large enterprises
// In a real world example you’d wait for user confirmation first
do {
try session.confirm(payload: payload, trustNewBrowser: true) // or session.deny()
} catch {
print(error)
}
case .failure(let error):
print(error)
}
}
}