Two-factor Authentication

  • UUID: d7fe4b24-7ee5-4c0d-8722-c088413706c2

When the year number is approaching 2024, two-factor authentication becomes mandatory for more and more information systems. Essentailly, two-factor authentication (2FA) enhances data security in the digital world.

As a preparation for better understanding, there comes some common names and basics which are related to two-factor authentication.

  • Two-factor authentication, by Microsoft, Apple
  • 2-Step Verification, by Google
  • 2FA
    • which standas for two-factor authentication
  • Multi-factor authentication
    • which is a enlarged concept for two-factor authentication
      • ~My Remark: in my opinion, also makes 2FA harder to understand as not really give certain expectation of just 2 steps and but in fact usually still just 2FA
  • Time-based one-time password (TOTP)
    • https://en.wikipedia.org/wiki/Time-based_one-time_password
      • Time-based one-time password (TOTP) is a computer algorithm that generates a one-time password (OTP) that uses the current time as a source of uniqueness.
      • As an extension of the HMAC-based one-time password algorithm (HOTP), it has been adopted as Internet Engineering Task Force (IETF) standard RFC 6238.
      • TOTP is the cornerstone of Initiative for Open Authentication (OATH), and is used in a number of two-factor authentication (2FA) systems.
  • HMAC-based one-time password (HOTP)
    • https://en.wikipedia.org/wiki/HMAC-based_one-time_password
      • HMAC-based one-time password (HOTP) is a one-time password (OTP) algorithm based on HMAC.
      • HOTP was published as an informational IETF RFC 4226 in December 2005, documenting the algorithm along with a Java implementation.
      • The HOTP algorithm is a freely available open standard.
  • One-time password (OTP)
    • https://en.wikipedia.org/wiki/One-time_password
      • A one-time password (OTP), also known as a one-time PIN, one-time authorization code (OTAC) or dynamic password, is a password that is valid for only one login session or transaction, on a computer system or other digital device.
      • OTPs avoid several shortcomings that are associated with traditional (static) password-based authentication; a number of implementations also incorporate two-factor authentication by ensuring that the one-time password requires access to something a person has (such as a small keyring fob device with the OTP calculator built into it, or a smartcard or specific cellphone) as well as something a person knows (such as a PIN).
  • More related reference links
    • https://en.wikipedia.org/wiki/Multi-factor_authentication
    • https://www.microsoft.com/en-us/security/business/security-101/what-is-two-factor-authentication-2fa
    • https://support.apple.com/en-us/HT204915
      • Two-factor authentication for Apple ID
    • https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication

The Trouble

Security comes with a cost. There also comes the problem of much more efforts in order to make logins.

As of 20231122, it still seems no well known open source applications available for managing 2FA secrets or tokens on macOS.

Why well known open source software is important in this case? 2FA secrets are secrets. Using safe software to keep the secrets is naturally critical, as using scam software to keep 2FA secrets endangers related accounts.

  • Apps in app stores like Google Play or Apple App Store are typically not open source.
    • As app stores commonly do not have an indicator whether an app is verified open source app.
    • As, to my knowledge, there is no transparent process which make open source code goes directly into the stores, therefore, all apps are the one made by developers.
      • therefore, even an app is told as open source, the version in the app store may still be different from what is given as open source on GitHub.
  • Making executable from the clean source code is best option, but there seems to be not so many open source projects for 2FA on GitHub being popular.
    • Being popular is important to this concept as popularity means number of people who have checked the code to avoid backdoors.
  • And there seems to be no big software vendors to make open usable solutions to the world.
    • Bing vendors like Microsoft, Google or Apple are more trustworthy, because they have more restriction than an indie developer to add backdoor on a future update.
      • Big vendors value their reputation much more than an indie developer.

Without trustworthy 2FA secrets management app on macOS, the awkward situation is typically like the following. When I am about the login in a website with my saved passwords in the browser in use, it is the one-click task if there is no 2FA but it is very-many-steps task if there is a 2FA in place. I need to find my mobile phone, unlock the screen, find the secrets management app such as Microsoft Authenticatior, click to open the app, find the corresponding account token in the app, manually type the token into the website on macOS, and only then I can successfully login on the website. It is not a easy process as it is also quite easy to mistype the token because copying from mobile phone to macOS is not possible or not reliable.

My Solution

Make one for myself! Yes, I don't want to do so. But I have no better choice.

I use Microsoft Authenticator on my iPhone as my primary 2FA secrets management application. It is not open source but the most trustworthy solution I can find. Microsoft is relatively the most trustworthy brand in this domain. It also requires the unnecessary login with a Microsoft account, but others also do so and I think Microsoft is more capable to make my data secure and have the better cross-platform support. (This comes to another aspect of security, when an app requires login, it is much lower security level than the ones with no login. With login, there is a very dangerous possibility that data gets uploaded to the cloud which means user data leave the device onto a computer owned by others with Internet accessibility.) However, there is no better opition and the Microsoft Authenticator doesn't work on macOS.

I have no choice other than to make one by my own. Of course, on shoulders of gaints which means the many open source projects on GitHub. I cannot find one on GitHub which simply works. And even I can find one, I still need to go through the code to be assure there is no backdoor in the code.

What do I have made? Let's walk through how I have made my own 2FA token management application.

My target platform is my main OS where I perform my daily digital work which is macOS. However, making macOS for myself is not economical as macOS technologies are typically Apple-locked technology and things made for macOS only work on this one platform. Therefore, I decided to make a web application.

Then the "shoulders" I need to find is JavaScript or TypeScript or Web Browser Programming libraries to do 2FA TOTP computation. (For 2FA, TOTP is there.) A bit more than necessary, a bit about JavaScript or TypeScript or Web Browser Programming in the last sentence. Logically, the libraries I was looking for should not be hooked with a specific runtime because it is mainly about pure computation around 2FA secrets or tokens. However, the reality is a bit more complicated. For TOTP calculations, some base data structures or base algorithms are not implemented in the language itself. Web browsers now support more runtime than just JavaScript, i.e., also web assembly. Web assembly is great for pure computation. But the web assembly community is still not big enough and as a result, JavaScript is still the only runtime chioce because of the number and qualitiy of existing libraries for TOTP on GitHub. TypeScript does not run on web browsers as of today, and it needs to be compiled into JavaScript. But TypeScript is still meaningful to talk separately from JavaScript because a TypeScript library is easier to use than a JavaScript library from a developer's perspective. JavaScript libraries nowadays are also a bit complicated because of the two popular runtime environments, Node.js and web browsers. Some JavaScript librareis only work on Node.js and some only on web browsers, because of the dependency on runtime APIs. Why matters here? Because I am targetting at making most potable app. So I need to find libraries which work in web browsers, not just on Node.js. Unfortunitely, for TOTP, Node.js have more APIs built-in than web browsers. This means, for making an web application without the need of an backend, JavaScript libraries without the dependency on Node.js API are necessary. What more specific? The crypto and Buffer. For crypto, when in Node.js, it is https://nodejs.org/api/crypto.html (require('node:crypto')/await import('node:crypto')); when on web browsers, it is https://developer.mozilla.org/en-US/docs/Web/API/crypto_property (globalThis.crypto which is window.crypto or self.crypto). (Partically, also related to the fact that global context is different - on the web you can use window, self, or frames - but in Web Workers only self will work; in Node.js none of these work, and you must instead use global.) For Buffer, when in Node.js, it is https://nodejs.org/api/buffer.html (const { Buffer } = require('node:buffer') or import { Buffer } from 'node:buffer'); when on web browsers, it is none, therefore, added such as https://github.com/feross/buffer should be adopted.

What is the option I have found?

  1. otplib
    • which supports both Node.js and web browsers
    • 🔑 One Time Password (OTP) / 2FA for Node.js and Browser - Supports HOTP, TOTP and Google Authenticator
    • https://github.com/yeojz/otplib
    • https://github.com/yeojz/otplib/blob/master/packages/otplib-preset-browser/README.md
  2. otpauth
    • One Time Password (HOTP/TOTP) library for Node.js, Deno, Bun and browsers.
    • https://github.com/hectorm/otpauth

Before the too dull code shows up, a screenshot of what the final view could be interesting.

Qia Authenticator GIF

In order to make a useful web app, there are also several following points worth consindering.

  • The UI is "dynamic" instead of "static" therefore, using React.js alike reactiving programming tools should be of great help
  • The applications data shouldn't be locked in the web app, therefore using an data API is necessary, such as fetch data.json
  • Considering the TOTP update period, there is no need to compute tokens on every UI update for performance (CPU heat, Fan sound, ...) consideration
  • Thinking about hosting on a protected intranet, therefore, authentication is optional (which requires backend)

And then there comes the dull code.

import React from './react.js'

const qia = {
    // @ts-expect-error
    authenticator: qiaAuthenticator.authenticator
}

class App extends React.Component {
    constructor(props: any) {
        super(props)
        this.state = {
            now: (new Date).toLocaleString(),
            numberOfRemainingSeconds: qia.authenticator.timeRemaining(),
            applications: []
        }

        // NOTE: 20231122, the path is relative to index.html
        const jsonDataUrl = './assets/data.json'
        window.fetch(jsonDataUrl)
            .then(response => response.json())
            .then((data) => {
                if (data && data.applications) {
                    console.debug(`[app] Generating tokens at ${(new Date).toLocaleString()}`)
                    const applications = data.applications.map((application: any) => Object.assign(application, {
                        token: application.secret ? qia.authenticator.generate(application.secret) : ''
                    }))
                    this.setState({ applications })
                }
            })
        let tokenGenerationTimestamp = 0
        window.setInterval(() => {
            const now = (new Date).toLocaleString()
            const numberOfRemainingSeconds = qia.authenticator.timeRemaining()
            // NOTE: 20231122, remaining seconds have 30 as max and 1 as min (does not have value 0)
            //  + hopefully, 5 seconds should be enough for a calculation, the setInterval is approximately 1s which may be longer or shorter
            if (numberOfRemainingSeconds > 25) {
                const nowTimestamp = Date.now()
                if (nowTimestamp - tokenGenerationTimestamp > 25 * 1000) {
                    console.debug(`[app] Regenerating tokens at ${(new Date).toLocaleString()}`)
                    tokenGenerationTimestamp = nowTimestamp
                    const applications = this.state.applications.map((application: any) => Object.assign(application, {
                        token: application.secret ? qia.authenticator.generate(application.secret) : ''
                    }))

                    this.setState({
                        applications,
                        numberOfRemainingSeconds,
                        now: now
                    })
                    return
                }
            }

            this.setState({
                numberOfRemainingSeconds,
                now: now
            })
        }, 1000)
    }

    render() {
        return (
            <div style={{ margin: '1em' }}>
                <h1>Qia Authenticator</h1>
                <div className="now" style={{ textAlign: 'right' }}>{this.state.now}</div>
                <div className="applications">
                    {this.state.applications.map((application: any) => application.secret ? (
                        <div key={application.name} className="application-token-card" style={{ margin: '.7em', border: '1px solid gray', padding: '.5em' }}>
                            <div className="name">{application.name}</div>
                            <div className="token-container" style={{ textAlign: 'right', fontSize: '2em' }}>
                                <code className="otp-token">{application.token}</code>
                            </div>
                            <div className="countdown-container" style={{ textAlign: 'right' }}>
                                (<span className="otp-countdown">Next token in: {this.state.numberOfRemainingSeconds}s</span>)
                            </div>
                        </div>
                    ) : null)}
                </div>
            </div>
        )
    }
}

export default App

* cached version, generated at 2024-01-11 10:58:22 UTC.

Subscribe by RSS