Skip to content

Instantly share code, notes, and snippets.

@shana
Forked from gboudreau/AuthyToOtherAuthenticator.md
Last active February 13, 2024 20:12
Show Gist options
  • Save shana/e4930f8f5608a796aa1e6716c8b3b1de to your computer and use it in GitHub Desktop.
Save shana/e4930f8f5608a796aa1e6716c8b3b1de to your computer and use it in GitHub Desktop.
Export TOTP tokens from Authy

Generating Authy passwords on other authenticators


Orignal: https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93

  1. Install Authy desktop app (version 2.2.3; versions after that won't work).

On Windows: Run the setup, kill it immediately once the login page appears. In %LOCALAPPDATA%\authy, delete Update.exe in the root folder and in the app-2.2.3 folder.

On Mac: mkdir -p ~/Library/Caches/com.authy.authy-mac.ShipIt ; rm -rf ~/Library/Caches/com.authy.authy-mac.ShipIt/* ; chmod 500 ~/Library/Caches/com.authy.authy-mac.ShipIt

  1. Open Authy and log in, so you can see the codes being generated for you.
    Note: Authy has a backup password feature that is enabled on some accounts; I think it's ones that have a padlock icon next to them. For those accounts, you might need to enter the backup password to be able to export them.
  2. Restart Authy desktop app, but add the --remote-debugging-port=5858 parameter to the command-line:
  • On Windows: right-click the Authy desktop shortcut, and in the Target field write --remote-debugging-port=5858 at the end. Then click OK. Double-click the Authy desktop shortcut.
  • On Mac: from Terminal.app: open -a "Authy Desktop" --args --remote-debugging-port=5858
  • On Linux, from a terminal: authy --remote-debugging-port=5858
  1. Open the following URL in a Chrome (or Chromium-based) web browser: http://localhost:5858
  2. Click the Twilio Authy link in that webpage.
  3. In Chrome Developer Tools top navigation bar, go in the Sources tab (if you don't see it, click >> to expand the full list), then select the Snippets sub-tab (tabs on the second line; again, click >> to expand the full list), and finally choose + New snippet.
  4. If you'd like to ensure the code below doesn't send anything to a remote server, you can disconnect from the internet now.
  5. In the snippet editor window that appears on the right, paste the following code:
// 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; }

// from https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid#answer-2117523
function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }

// from https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93
function saveToFile(data, filename) { if (!data) { console.error('Console.save: No data'); return; } if (typeof data === "object") { data = JSON.stringify(data, undefined, 4) } const blob = new Blob([data], { type: 'text/json' }); const e = document.createEvent('MouseEvents'); const a = document.createElement('a'); a.download = filename; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ['text/json', a.download, a.href].join(':'); e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); a.dispatchEvent(e); }

function deEncrypt({ log = false, save = false }) {
const folder = {
    id: uuidv4(),
    name: 'Imported from Authy'
};

const bw = {
    "encrypted": false,
    "folders": [
        folder
    ],
    "items": appManager.getModel().map((i) => {
        let secretSeed = i.secretSeed;
        if (typeof secretSeed == "undefined") {
            secretSeed = i.encryptedSeed;
        }
        const secret = (i.markedForDeletion === false ? i.decryptedSeed : hex_to_b32(secretSeed));
        const period = (i.digits === 7 ? 10 : 30);

        const [issuer, rawName] = (i.name.includes(":"))
            ? i.name.split(":")
            : ["", i.name];
        const name = [issuer, rawName].filter(Boolean).join(": ");
        const totp = `otpauth://totp/${name}?secret=${secret}&digits=${i.digits}&period=${period}${issuer ? '&issuer=' + issuer : ''}`;

        return ({
            id: uuidv4(),
            folderId: folder.id,
            rawName: i.name,
            name: name,
            secret: secret,
            digits: i.digits,
            period: period,
            issuer: issuer,
            totp: totp,
            kind: "TOTP",
            algorithm: "SHA1",
            
        });
    }),
};

if (log) console.log(JSON.stringify(bw));
if (save) saveToFile(bw, 'authy.json');
}

deEncrypt({
log: true,
save: true,
});
  1. Right-click the snippet name on the navigator pane on the left (eg. Script snippet #1) , and choose Run.
  2. In the authy app, it will prompt to save the json file. Check that the secret entries in the json are all different. If they're not, it's because you need to login to authy with your backup password.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment