DEV Community

Cover image for How I Built My Own Offline 2FA Authenticator App (A Weekend Android Project)
H.M.Ashis Rahman
H.M.Ashis Rahman

Posted on

How I Built My Own Offline 2FA Authenticator App (A Weekend Android Project)

Introduction: The Offline Mystery of 2FA

We all rely on 2-Factor Authentication (2FA) for enhanced security. Apps like Google Authenticator, Authy, or Microsoft Authenticator are staples on our phones. But have you ever stopped to wonder how they generate those constantly changing, time-sensitive codes without an internet connection?

That question sparked my latest weekend project. I decided to pull back the curtain and build my very own 2FA authenticator app for Android, focused specifically on implementing the Time-based One-Time Password (TOTP) algorithm. It was a fascinating dive into cryptography, time synchronization, and mobile development.

What is TOTP and How Does It Work?

Before we jump into the code, let's quickly demystify TOTP. At its core, TOTP (Time-based One-Time Password) is a method that uses a shared secret key and the current time to generate a unique, temporary verification code.

The magic relies on both the authentication server and your authenticator app having:

A Shared Secret Key:

A unique, random string generated during the account setup (when you scan a QR code).

A Common Understanding of Time:

While not perfectly synchronized to milliseconds, both need to agree on roughly the same "time window" (e.g., a 30-second interval).

Using these two inputs, they can independently generate the same OTP at any given 30-second interval. When you enter the code from your app, the server performs the same calculation. If the codes match, you're authenticated!

My Project: Architecture Overview

Here's a breakdown of the simple architecture I implemented for this project:

Authentication Server (Conceptual/Simulated):

Generates a unique secret key for a user.

Stores this secret key securely in a database.

Generates a QR Code that encodes this secret key (often using a otpauth:// URI format). This QR code is what the user scans to "enroll" their device.

Android Authenticator App (The Star of the Show):

QR Code Scanner: Captures the otpauth:// URI
containing the secret key.

Secret Key Storage: Securely stores the captured secret key on the device.

TOTP Generator: Uses the stored secret key and the device's current time to continuously calculate and display the current 6-digit OTP, refreshing every 30 seconds. Here's a breakdown of the simple architecture I implemented for this project:

Authentication Server (Conceptual/Simulated):

Generates a unique secret key for a user.

Stores this secret key securely in a database.

Generates a QR Code that encodes this secret key (often using a otpauth:// URI format). This QR code is what the user scans to "enroll" their device.

Android Authenticator App (The Star of the Show):

1.QR Code Scanner: Captures the

otpauth:// URI
containing the secret key.

Secret Key Storage: Securely stores the captured secret key on the device.

TOTP Generator: Uses the stored secret key and the device's current time to continuously calculate and display the current 6-digit OTP, refreshing every 30 seconds.

Key Implementations

I leveraged a popular library for QR code scanning (e.g., ZXing or ML Kit's Barcode Scanning API). The critical part here is parsing the otpauth:// URI, which typically looks something like this:
otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&digits=6&period=30
From this, the app extracts the secret parameter, which is our vital shared key.

2. Secure Secret Key Storage

Storing the secret key directly in plain text is a huge security risk. For this project, I opted for Android's EncryptedSharedPreferences (from the AndroidX Security library), which stores data securely using the Android Keystore system. This encrypts the secret key at rest.

3. TOTP Generation Logic

This is where the core algorithm comes into play. I implemented the TOTP algorithm using a standard Java cryptography library (or a custom implementation if you prefer to go deeper).

  1. The generateTOTP function typically takes:

secretKey (the base32 decoded secret)

time (current Unix timestamp divided by the time step, e.g., 30 seconds)

digits (number of digits for the OTP, usually 6 or 8)

The algorithm involves:

  1. HMAC-SHA1 Calculation: Computing an HMAC-SHA1 hash using the secret key as the HMAC key and the current time step as the message.

  2. Dynamic Truncation: Taking a specific byte from the HMAC result and using it to dynamically select a 4-byte sequence.

  3. Modulo Operation: Converting this 4-byte sequence into an integer and taking its modulo by 10^digits to get the final OTP.

To make the app dynamic, I used a Handler or a Flow/LiveData to periodically trigger the TOTP calculation and update the UI every second, visually showing the 30-second countdown before a new code appears.

Top comments (0)