Integrations
Banno Digital Banking

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:

  1. 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

  2. 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

  3. 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:

  1. Serve KeyriQR.html (available here (opens in a new tab)) from the same origin as your login page (e.g., a /public directory)

  2. 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)

  3. 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)
        }
    }
}