Degoogling TOTP Authenticator Codes

Back to Articles

In the ongoing effort to extricate myself from Google's services, I've been paring down my usage of their apps on my (admittedly Android) phone. I'm now down to two Google apps I use regularly: Maps (for traffic data) and Authenticator (for TOTP[A]Time-based One Time Password codes).

Now, I spend most of my time in a terminal window on MacOS or connected to a Linux machine; it'd be nice if I could get TOTPs on the command-line, and it turns out there's a utility called oathtool that allows for TOTP generation on the CLI. However, that would mean switching my OTP provider, which usually involves:

Fortunately, Google's Authenticator provides a way to migrate codes between instances of the app based on scanning QR codes, and we can use this to migrate them away from Google into a TOTP handler of our choosing. It's another four-step process:

Note that the below steps are presented just as I went through them, you may be able to find efficiencies or you may run into troubles that I didn't (especially if you're trying this exclusively on Windows); "your mileage may vary" is apt here.

Going from Authenticator to a migration URL

The first step is getting the code out of Authenticator, through the Transfer Codes menu option in the app. Picking the services you'd like to extract leads you to a code like this:

QR code for The Rickroll Store's OTP
Figure 1: QR code exported from Google Authenticator
Unrivalled padding between the QR and Next button

You may have an app on your phone that decodes QRs, but I don't; instead, I transferred the file to my MacOS machine over Tailscale, and used a command-line tool called qrtool to get the QR content:

Decoding the migration QR

$ brew install qrtool
$ qrtool decode Screenshot_20250901_062719_Authenticator.jpg
otpauth-migration://offline?data=CjwKC2kqSJnNaAyKkw6jEhJUaGUgUmlja3JvbGwgU3RvcmUgASgBMAJCEzg4Yzg5ZTE3NTY3MDQzOTE0MzkQAhgBIAA%3D

Decoding the URL into secrets

So we have our migration URL, with a Base64-encoded data block. Unfortunately, if we were to simply decode the data, we'd end up with some binary gibberish:

Trying to decode the URL directly

$ php -r 'var_dump(base64_decode("CjwKC2kqSJnNaAyKkw6jEhJUaGUgUmlja3JvbGwgU3RvcmUgASgBMAJCEzg4Yzg5ZTE3NTY3MDQzOTE0MzkQAhgBIAA%3D"));'
string(69) "
<

i*H??h??ý

It turns out that this is a Protobuf-encoded data string, and we need to use Google's Protobuf library to get the data out. It turns out Tim Brooks has already done this with a short piece of Python at: https://github.com/brookst/otpauth_migrate

I decided to install this on a Linux machine I tend to be connected to (entirely unrelated to my Python installation being broken on Mac...):

Extracting the data via otpauth_migrate

$ git clone https://github.com/brookst/otpauth_migrate
$ cd otpauth_migrate
$ ./otpauth_migrate.py otpauth-migration://offline?data=CjwKC2kqSJnNaAyKkw6jEhJUaGUgUmlja3JvbGwgU3RvcmUgASgBMAJCEzg4Yzg5ZTE3NTY3MDQzOTE0MzkQAhgBIAA%3D
secret: "i*H\231\315h\014\212\223\016\243"
name: "The Rickroll Store"
algorithm: ALGORITHM_SHA1
digits: DIGIT_COUNT_SIX
type: OTP_TYPE_TOTP

Secret code = NEVERGONNAGIVEYOUM======

This tool is intelligent enough to extract any number of names and secrets from a migration URL, so you can export all your codes from Authenticator into one giant QR without needing to do each separately.

Using oathtool to generate OTPs

The final step is to use this secret code with oathtool, which takes the secret directly as a parameter. If you instead want to refer to the service by name, Michael Bushey[1]"CLI 2-Factor Authentication", Michael Bushey, 2023 has a quick wrapper script which extracts the secrets from a locally-stored file:

Wrapper script to generate OTPs: /usr/local/bin/otp

#!/bin/bash
OTPKEY="$(sed -n "s/${1}=//p" ~/.otpkeys)"
if [ -z "$OTPKEY" ]; then
   echo "$(basename $0): Bad Service Name '$1'"
   exit
fi
date
oathtool --totp -b "$OTPKEY"

OTP key store: ~/.otpkeys

rickroll=NEVERGONNAGIVEYOUM======

With this in place, you won't need to use your Authenticator app again. The tool outputs the current date and time, so you can double-check that your code won't expire (at :00 seconds) before you get a chance to type it in:

$ otp rickroll
Mon Sep  1 07:10:42 AM UTC 2025
200213

Future expansion

There's a security issue here, of course, which is the exposed secret key sitting in a file on-disk. I'm happy to sit with that and not require a password to generate OTPs every time, but if you're interested in adapting the wrapper script to use symmetric encryption to secure the keys, Vivek Gite[2]"Use oathtool Linux command line for 2 step verification (2FA)", Vivek Gite, updated Feb 2025 has a set of scripts which employ gpg for the job.

Now I just need to find a way to get traffic data into a maps App that doesn't involve Google's servers... Thoughts welcome.