Authentication with login and password is known and common approach for user identification in the internet and accessing resources in the web. However, nowadays, with existing computing powers, cyber attackers have facilities for testing billions of password combinations in a second. More than that, statistics says that 65% of people use the same password, usually simple password, everywhere. This means that stealing your credentials or picking them up via brute-force attack is not a complicated task anymore.
Two-factor authentication (also known as 2FA) is a method of confirming a user’s identity in which user is granted access only after successfully presenting two pieces of evidence (or factors) to an authentication mechanism: knowledge (something they and only they know – login&password, PIN code, etc), and possession (something they and only they have). The possession factors may be – ID card, security token, smartphone, etc – something that is not a logical thing you know but a physical entity.
In this post I would like to show how to implement two-factor authentication for web application using Google Authenticator as a possession security factor.
General assumptions and conditions
You should not by any conditions consider the described sample application as production ready solution. It shows only basic principles of establishing two-factor authentication for web applications. Moreover:
- Limited subset of Spring Security features is used – only for restricting access to some pages by anonymous users and for keeping user authentication in a session. All other authentications flows are implemented explicitly to show the concept.
- Using Spring Boot as a platform for web application
- Using QR code gereator provided by https://zxing.org
- Using TOTP reference implementation provided by IETF
- All services are implemented as REST end-points
- Web UI is implemented via Bootstrap and JQuery libraries
TOTP (Time-based One-time Password Algorithm)
Google Authenticator application is basen TOTP algorithm. Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. This algorithm is described and standardized in RFC 6238. And here, in sample application, I used TOTP generation function implemented as a part of this specification – reference implementation.
As a basis for 2FA example I created a very simple Java Web application via Spring Boot. The structure of this application is very simple. It consists of:
- Static 4 HTML pages
- Home page
- User registration page
- Login page
- Secured page – unavailable for anonymous user, but accessible for user who passed through two steps of authentication
- Spring REST controllers for user login and registration on a server side.
- And underlying services to support REST controllers implementation
You can look at implemented and working application version in a GitHub repository:
For simplicity, the users and their credentials are stored in the in-memory storage.
User registration flow consists of two steps:
- Specify login and password
- Register generated secret key within Google Authenticator application
When application creates a new user with given login and password, it also generates a unique secret key. This key then will be used to generate time-based codes. So it’s obvious, that this secret key (token) should not be shared with anyone, same as password.
This is how user registration flow looks on the UI:
The token on this screenshot is a mentioned user secret key encoded in Base32 format. This is a requirement to make it possible to be used in Google Authenticator or other OTP applications.
QR code represents a link with specific format, which can be scanned and understood by Google Authenicator. The format of this link is the following
To generate a QR image itself I used zxing.org service by just using the following link:
https://zxing.org/w/chart?cht=qr&chs=250x250&chld=M&choe=UTF-8&chl=otpauth://totp/2FaExample.com?secret=" + data + "&issuer=2FaExample
where ‘data’ is a user secret key string.
After clicking ‘Done’ on a ‘Configure your token’ screen the registered user will be ready for login attempts. Please note, that the secret key is shown for the user only once. So its important to save or remember it. Otherwise, it will not be possible to login.
The authentication process consists of two steps:
- Login/Password verification
- One-time TOTP token check.
Look at the diagram below for more details.
TOTP implementation and verification
As was said above, in the described sample application I used TOTP algorithm reference implementation, provided as a part of RFC 6238. I copied-and-pasted that implementation to the project “as is” wihout any modifications, so I will not describe anything regarding it. You can go here and review it.
What application does is:
- Get user login and password
- Verify that user with given login and password exists.
- Get token from the user (screenshot above)
- Get the user’s secret key from database.
- By this secret key and current time generate TOTP using mentioned algorithm.
- Compare generated TOTP (step 5) with the token provided by user (step 3).
It is important to say that there a requirement to make this flow working correctly. As the TOTP algorithm uses time for token calculation, the current time on the application server and on the device with installed Google Authenticator must be set to the same. Otherwise the tokens will never match on step 6.
Worth to know further
It is important to understand that Google Authenicator application is just one of implementations of TOTP generation algorithms. It is absolutely possible to use any application which implements the same specification, for example FreeOTP, Authy or even your own. It just has to implement Time-based One-time Password Algorithm to generate one-time passwords.
In this post I described how to implement two-factor authentication with Google Authenticator and TOTP algorithm for token generation. To make the 2FA even more secured there is possible to use any other token generator approaches, for example: SMS, a phone call, a separate hardware token, etc.