If you’re like me you’ve got a lot of time-based one-time passwords (aka “TOTP”) in a smartphone app like Google Authenticator. This system works pretty well: The service presents you with a QR code, you scan it with Google Authenticator, and then every 30 seconds you get a fresh 6-digit code to use as your second factor when logging in to the service. It’s more secure than using SMS for reasons I won’t spell out here.

As you might have intuited, this QR code contains a secret code – a string of random alphanumeric characters unique to your account. Google Authenticator stores this secret code, but doesn’t allow the user to read them (I assume for security reasons). Google Authenticator then uses an algorithm to mix the secret with the current time and generate a new 6-digit code every 30 seconds. Thus we can think of these secret codes as “seeds”, but for the remainder of this post I’ll refer to them as “secrets” or “text secrets”.

If you indeed use Google Authenticator to store and display these codes, you should know that Google Authenticator does NOT offer a method for backing up the stored secrets. (Authy does offer back-ups, but they live on their servers.)

So what if you lose or break your phone? Well, that’s what the “back-up codes” that the service gives you are for. Store them somewhere secure, and then when you break/lose your phone, use one of the back-up codes instead of the 6-digit code.

But I’ve found some services either give you only one back-up code, and reset it every time you ask to view it (Twitter), or don’t even give you any back-up codes at all (ConEdison). In these cases, I think I’d like a way to store the QR codes, or better yet: store the actual TOTP secrets (the random text strings) and make QR codes as needed.

But Isn’t This Less Secure Than Not Retrieving and Storing the Secrets?

Short answer: yeah, it can make your system less secure. But my logic is that if I store these secrets in the same location and/or with the same care and procedure as I store back-up codes, I’m not really losing much security.

I will say you probably do NOT want to store these secrets in the same KeePass database as your account passwords.

My Goal

I wanted to create a reliable procedure to extract these secrets from a given QR code, and store these secrets somewhere off of my phone. (Note: this is distinct from storing the back-up codes, which I was already doing). My hope is to make my inevitable transition from a lost or destroyed phone to a new phone as easy as possible. (Though if my phone was stolen, I’d probably want to fully reset all of my TOTPs just to be safe…)

To accomplish this, I could have simply screenshotted the QR codes when they’re presented to me and store the image files securely, ready to be rescanned on my new phone. However I wanted to instead store the secrets themselves (a string of random characters) for two reasons: (1) my password manager of choice, KeePassXC, can store these secrets and present TOTPs on command, but you have to input the secret as text, as opposed to a QR code, and (2) I was curious how the QR codes worked.

A Bit More About QR Codes

One of the things that a Quick Response code (QR code) can contain is a URL or URI (Uniform Resource Identifier). The QR codes we’re dealing with here contain an otpauth URI, which look like otpauth://totp/hereisthelabel?secret=hereisthesecret&issuer=hereistheissuer. This particular URI format is defined further in the Google Authenticator GitHub repo. The secret parameter is the most important for us, but we’ll use the label and issuer too.

There are a few online tools to make your own QR codes containing otpauth URIs: I found Authenticator Test and 2FA QR code generator. I would NOT recommend entering your actual account secrets into these online generators cuz they are on the internet. However you can use them to test my procedure I outline below if you don’t want to use your real account secrets.

The System I Ended Up With

I ended up installing two separate command line tools to accomplish my goal, one to convert screenshots of QR code into their base URIs, and another to take URIs and create new QR codes. If you’re not comfortable using the command line, sorry, this procedure is going to be hard for you. However there may be GUI options for this for your OS.

Setup

MacOS

  1. Install Homebrew
  2. Run brew install zbar qrencode

Ubuntu-based Linux

  1. Run sudo apt install zbar-tools qrencode

Steps For Accessing TOTP Secret for Twitter (QR Code to Text Secret)

As mentioned above, I dislike Twitter’s handling of back-up codes. First, because Twitter only gives me one back-up code (as opposed to 8 or 10 like other services), but also because it’s my understanding that every time I ask to view the back-up code, it gives me a new one, implying that the previous back-up codes will no longer work.

So I went about getting a new QR code from Twitter, getting the secret out of the QR code and finally storing it in a KeePass database using KeePassXC.

Here are the steps I took to do this.

If you already have any login verification setup, you’ll need to turn it off first then turn it back on. Note: To turn it back on via any method, you’ll need to receive an SMS message to your set phone number.

Once you do this, you’ll get a new back up code – store it some place safe.

  1. (Re)Setup “Mobile security app” (aka TOTP) 2nd factor for your Twitter account.
  2. Take a screenshot of the QR code you’re presented with. Save it to your desktop.
  3. Add this QR code to your Google Authenticator just in case the rest of this procedure fails.
  4. Run zbarimg --raw <path to screenshot>. The output should contain a otpauth URI; something like otpauth://totp/Twitter?secret=hereisthesecret&issuer=Twitter. We’ll be using the “secret” soon.
  5. Now either write down this secret, next to “Twitter” and your username, and store it somewhere safe and away from your password – wherever you store your back-up codes for example. (Alternatively you can store the secret in a KeePass database. To do this, create a new entry in a KeePass database called “Twitter TOTP code”. Right-click the entry and select “Setup TOTP”. In the “Key” text field, enter the secret from the URI we got before from the zbarimg command. Select the radio button for “Default RFC 6238 token settings”. Click “OK”.)
  6. As a test, let’s generate a fresh QR code from this secret/key. Run qrencode -s 10 -o ~/Pictures/generated_qr_code.png 'otpauth://totp/Twitter:@twitter_username?secret=hereisthesecret&issuer=Twitter'
  7. Open ~/Pictures/generated_twitter_qr_code.png on your computer. You should see a QR code! Next open Google Authenticator on your phone and point it at the newly generated QR code.
  8. Compare the two 6-digit codes in Google Authenticator – they should be the same! If they are the same, feel free to remove one of them from your Google Authenticator app.

Moderately Secure Clean Up on Linux

On Linux:

shred -ufv --iterations=20 ~/Pictures/generated_qr_code.png
shred -ufv --iterations=20 ~/Pictures/<original screenshot of QR code>

Then open ~/.bash_history in your text editor of choice and delete the lines the contain your secret. This isn’t a perfect way to cover your tracks, but it doesn’t hurt.

Notes on Other Methods of Twitter Login Verification

The above procedure will leave SMS (text message) login verification enabled. If you don’t want this enabled (for example if you’re afraid of a SIM-swapping attack), you’ll have to disable it yourself.

Also, if you had enabled a security key (like a YubiKey) for Twitter login verification, you’ll need to set that up again. (Here’s a guide from Vice.)

Using a Secret to Create a QR Code (Text Secret to QR Code)

Now we have the secret stored securely. So let’s say we want to use this secret to make a QR code to scan with our new phone.

For my Twitter account (@sts10), I’d run:

qrencode -s 10 -o generated_twitter_qr_code.png 'otpauth://totp/Twitter:@sts10?secret=hereismysecret&issuer=Twitter'

Alternatively, Google Authenticator has an option to manually enter an account name and its key (secret).

To get your secret out of a KeePassXC entry, Edit the entry > Click the Advanced icon > Highlight “TOTP Seed” under Additional Attributes > click the “Reveal” button on the right.

A KeePassXC Feature Request

It’d be super handy if users of KeePassXC could simply drag a screenshot (or image) of a QR code containing a otpauth URI and drop it into a KeePass database entry to setup TOTP in one go. Likewise, it’d be nice if, once users set up TOTP for a given entry, KeePassXC had an option to show or create the corresponding for QR code.

Appendix

Here’s a shell script that takes an otpauth URI and presents the current 6-digit code, among other things (h/t @shello@octodon.social).