Abra-QR-dabra: Watch Passwordless Mobile Auth Magically Appear

Abra-QR-dabra: Watch Passwordless Mobile Auth Magically Appear

·

4 min read

A few days ago, a post on hacker news caught my eye, where folks were playing around with and linking to various QR Code designers. This got me to thinking, how quickly could I add a QR flow into our authentication processes?

Starting Out

For ease and speed, I wanted to work with a preexisting application to see how quickly I could slot this feature in, without needing to spend time with setup. To that end, I started up our React/Express Starter Application as a base. Reminder, this is how the application looks at the end of our starter guide:

In terms of flow for the application, I thought about a kind of product that requires some version of mobile interaction after setting up an account on the browser. Think of a product like PagerDuty, which is used to send alerts when issues arise. Most customers that are configuring PagerDuty are going to use their desktop to set up their on-call schedule, generate API keys, etc.

However, an important step in the process is setting up your phone, for use cases like adding PagerDuty’s phone numbers to the exceptions to your DND settings. So one way to move customers from their desktop to their phones is to send them a magic link, which we can put in a QR code.

So the expected flow for this use case would be:

  • The user is already logged in on their laptop/desktop

  • The user needs to be logged in on their mobile device

  • We generate a magic link on the backend and return it to the browser

  • We render that as a QR code so the user can easily sign in on their mobile device

  • (Optional) We can direct them to a specific page so they are dropped in to the product exactly where they need to be

To begin, I set up another route for the page that would display the QR code, and built that component, called qr.jsx. Next, I installed a React friendly QR code generator

npm install qrcode.react

In the new component, I wrote a fetching function that would reach out to a new endpoint, ‘/generate-mobile-login-link’. Next, the component calls this function, and renders the response as a QR code generated from the library we imported.

import { withRequiredAuthInfo} from "@propelauth/react";
import { QRCodeSVG } from 'qrcode.react';
import { useEffect, useState } from "react";

function fecthNewLink(accessToken) {
    return fetch(`/generate-mobile-login-link`, {
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${accessToken}`
        }
    }).then(response => {
        console.log('response in fetch: ', response)
        if (response.ok) {
            return response.json()
        } else {
            return {status: response.status}
        }
    })
}

function QR({ accessToken }) {
    const [response, setResponse] = useState(null)

    useEffect(() => {
        fecthNewLink(accessToken).then(setResponse)
    }, [accessToken])

    if (response) {
        return <div>
            <QRCodeSVG value={JSON.stringify(response.newLink)} />
        </div>
    } else {
        return <div>Loading...</div>
    }
}

export default withRequiredAuthInfo(QR);

We have an endpoint in our backend client libraries that will take in a user’s email, along with a few other optional parameters, and produce a Magic Link for passwordless login.

So in order to produce this link, I added the new /generate-mobile-login-link route that I’m pointing to on my frontend, and used the createMagicLink function to pass this new URL back as a successful response. requireUser checks the token in the request to make sure it was made by a valid user, and then the new magic link is sent back in JSON.

app.get('/generate-mobile-login-link', requireUser, async (req, res) => {
    let newLink = await createMagicLink({
        email: req.user.email
    })

    res.json({ newLink: newLink})
})

How Does It Work?

Pretty well! You can see the new page below making successful calls to the server.

Granted, this is the MVP version of this kind of authentication flow. For a real product you might want to put this in a modal or as part of your onboarding flow.

That said, one optional parameter for the createMagicLink function is another URL to redirect to after the user is logged in, so in a production ready application I could be forwarding my users to additional setup or permissions pages.

I could also get much fancier with it! The mini project was inspired by QR code designers, so I could work in libraries like the ones mentioned in the hacker news post.

Or, go even wilder, using Halftone QR Codes like you can see here:

All in all, this was a fun exercise in working with our client libraries in ways that let me customize an authentication experience even more. We already offer easy passwordless login options via our hosted login pages, but it was fun to use the building blocks to make things more my own.