‹ gerlach.coffee

From Authy to Alfred for Time-based One-time Passwords

Mar 15, 2021

I’ve been annoyed by my company’s sign-in workflow for a while. A few times a day (depending on what computers I use) I have to open Authy, wait until it loads (thanks Electron), click on Okta, click on the copy icon, and then paste it into the screen. A few days ago I finally got annoyed enough to do something about it.

Extracting the Password Seed

You may not know that time-based one time passwords are basically just a simple algorithm based on an initial seed: https://en.wikipedia.org/wiki/Time-based_One-Time_Password

You can extract these seeds from Authy: https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93

tl;dr:

  • Open Authy like so: open -a "Authy Desktop" --args --remote-debugging-port=5858
  • Open the following URL in a Chrome (or Chromium-bases) web browser: http://localhost:5858
  • Click Twilio Authy, in the dev tools Application, select the top frame, right click main.html and select Open in containing folder
  • paste the following code into the dev console:
// Based on https://github.com/LinusU/base32-encode/blob/master/index.js
function hex_to_b32(hex) { let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; let bytes = []; for (let i = 0; i < hex.length; i += 2) { bytes.push(parseInt(hex.substr(i, 2), 16)); } let bits = 0; let value = 0; let output = ''; for (let i = 0; i < bytes.length; i++) { value = (value << 8) | bytes[i]; bits += 8; while (bits >= 5) { output += alphabet[(value >>> (bits - 5)) & 31]; bits -= 5; } } if (bits > 0) { output += alphabet[(value << (5 - bits)) & 31]; } return output; }

var data = appManager.getModel().map(function(i) {
  var secret = (i.markedForDeletion === false ? i.decryptedSeed : hex_to_b32(i.secretSeed));
  var period = (i.digits === 7 ? 10 : 30);
  var totp_uri = `otpauth://totp/${encodeURIComponent(i.name)}?secret=${secret}&digits=${i.digits}&period=${period}`;
  return {name: i.name, secret: secret, uri: totp_uri};
});
console.dir(data)

You can now extract the seed password(s) that you are interested in:

Extracting the password from Authy

Then you need to install oath-toolkit, which includes an implementation of and command line interface to the TOTP algorithm - on mac you can do this through brew install oath-toolkit .

You can now generate a Time-based One-Time token through oathtool -b --totp 'SECRET' where SECRET is the secret token you’ve extracted from Authy.

Using Alfred to Fill in the Token

I quite like Alfred for creating workflows so, this is what I used for pasting the token into the browser:

Alfred Workflow

These threw pieces are predefined Alfred workflow primitives; the keyword building block lets me invoke this workflow by typing okta:

Okta in Alfred

The copy to clipboard block not only copies the result of the shellscript to the clipboard, it also pastes it into the frontmost application - which is the browser which prompts me yet again for the TOTP token.

In the middle the shell script simply calls oathtool -b --totp 'SECRET'. Because this shell script doesn’t inherit your $PATH you will have to specify the full path to it, such as /opt/homebrew/bin/oathtool -b --totp 'SECRET'.

Using the MacOS Keychain

As simple and straightforward as this is, we probably don’t want to keep the seed in plaintext in your Alfred workflow. The MacOS keychain is fairly straightforward to use for this kind of thing.

First, open your Keychain Access App, select your Login keychain and create a new password item:

Keychain Access.app

Choose a reasonable Keychain Item Name, use your login user as the Account name, and save the TOTP seed as the password.

You can now retrieve this password through the command security find-generic-password -s "adaokta" -w

Which means your Alfred shellscript can look like this:

PASSWORD=$(security find-generic-password -s "adaokta" -w)
/opt/homebrew/bin/oathtool -b --totp $PASSWORD

Shell Script in Alfred

This will let you choose to only let Alfred create your TOTP tokens:

Permission