Setting up Google Two-Factor Authentication
By Levi Clancy for לוי on
updated
Two-factor authentication is a login system that requires the user to have a password they enter, plus a secret code from a device that they own. This means that even if a user's password is stolen or guessed, a hacker cannot log in without also having the user's device that has that secret code. This also works for DUO!
Google Authenticator is an app available for iOS and Android to support two-factor authentication. DUO also supports the same protocol in the same way.
The authenticator secret is a secret code stored on the server that is specific to the user and is also stored on their device.
The authenticator code is a time-sensitive six-digit code from the user's device that they use to verify their login elsewhere.
Base 32 encoding is a type of encoding used with the protocol.
The protocol used with Google Authenticator (and compatible with DUO) uses a custom algorithm that generates codes from the authenticator secret that are only valid for thirty seconds at a time.
The user experiences ...
Entering the authenticator secret into Google Authenticator or DUO once per device. The entry is done by scanning a QR code or clicking an app-launch link.
Entering an authenticator code from Google Authenticator or DUO into your app or site with each log in.
The developer has to make ...
An authenticator secret for the user that is securely, privately stored.
A function to generate thirty-second authenticator codes to validate user input.
A QR code for the user to add their authenticator secret to their device.
A check for whether the user entered a valid thirty-second authenticator code.
Base 32 encoding
Base 32 encoding is a well-established standard that is great for web because it is all upper-case without confusing characters. It uses the 26 upper-case letters of the English alphabet, plus the numbers 2 - 7 (avoiding 1 that looks like I, and 0 that looks like O). This results in 32 possible characters. This is particularly important, because each binary number between 0 (0 in binary) to 31 (11111 in binary) can be mapped to a character. For example: the 0 (0 in binary) maps to the letter A, 1 (1 in binary) maps to the letter B, 2 (10 in binary) maps to C, and so forth. You get an alphabet map like this,
Decimal | Binary | Character |
---|---|---|
0 | 0 | A |
1 | 1 | B |
2 | 10 | C |
3 | 11 | D |
4 | 100 | E |
5 | 101 | F |
6 | 110 | G |
7 | 111 | H |
8 | 1000 | I |
9 | 1001 | J |
10 | 1010 | K |
11 | 1011 | L |
12 | 1100 | M |
13 | 1101 | N |
14 | 1110 | O |
15 | 1111 | P |
16 | 10000 | Q |
17 | 10001 | R |
18 | 10010 | S |
19 | 10011 | T |
20 | 10100 | U |
21 | 10101 | V |
22 | 10110 | W |
23 | 10111 | X |
24 | 11000 | Y |
25 | 11001 | Z |
26 | 11010 | 2 |
27 | 11011 | 3 |
28 | 11100 | 4 |
29 | 11101 | 5 |
30 | 11110 | 6 |
31 | 11111 | 7 |
Writing a function that can convert any string to base 32 encoding works like this,
Split the string into individual characters: LOVE is split into 'L', 'O', 'V', 'E'.
Convert each character into its eight-digit ASCII binary equivalent. Languages will generally have this functionality built-in. LOVE becomes '01001100', '01001111', '01010110', '01000101'.
Merge the binary equivalents into one long string of numbers. LOVE becomes '01001100010011110101011001000101' with leading zeroes preserved.
Split the long binary string into chunks of five digits each, and add zeroes to the last chunk if it is not long enough. LOVE becomes '01001', '10001', '00111', '10101', '01100', '10001', '01000'.
Now convert each chunk use the base 32 character map from its five-digit binary number to a character equivalent. LOVE thus becomes 'J', 'R', 'H', 'V', 'M', 'R', 'I'.
PHP code sample,
Authenticator secret
Write an authenticator secret that uses the same character map as the base 32 set. Although you can take any secret and encode it into base 32, my recommendation is to stick to the protocol. Frankly, the user should never see this code.
Authenticator code
Google designed an excellent algorithm to generate six-digit codes valid for thirty seconds each, for use in two-factor authentication.
Take the current UTC timestamp and divide it by thirty.
Pack it into a binary string.
SHA1 encode the time together with the authenticator secret.
Use last bit as index/offset.
Grab four bytes using that index/offset.
Unpack the binary value.
You want only a thirty-bit result
Make sure that it is six characters.
PHP code sample,