Authy is a simple REST API that does all the heavy lifting, so you can add two-factor authentication to your website or app
in just a few hours.
Full source code for this app can be found here
You will find everything you need in Github
There are 2 main parts to implement the Authy API
First we are going to create a page in our users control panel (well call it /enableauthy). In this page we'll create a simple form that takes the persons phone number
and area code.
%label Cellphone =text_field "cellphone", :placeholder => "Enter your cellphone", :id => "authy-cellphone" %label Country =text_field "country_code", :id => "authy-countries", :placeholder => "Enter your country" %button.btn(type="submit") Submit
def register_authy @authy_user = Authy::API.register_user(:email => current_user.email, :cellphone => params[:user][:cellphone], :country_code => params[:user][:country_code]) if @authy_user.ok? current_user.authy_id = @authy_user.id current_user.save else @errors = @authy_user.errors render 'enable_authy' end end
If everything goes well:
Authy will return you the Authy ID of the users. This is an integer > 0. You need to store this id your users database.
If not a hash with errors in plain English is sent back. You should display this errors in the form so that the user can correct the inputs.
After the user has register to Authy in your website you get back an Authy ID. Next time user wants to log-in
you can use the Authy ID to verify the two-factor token.
There are many ways of doing this which depends on your application and the user experience you wish to create.
In this example we plan to support users that have two-factor authentication on, and some which don't. The easiest way is to do this is separating the authentication in 2 screens.
You can quickly check if a user has Authy enabled by checking your database. Assuming you called the Authy ID record in your database authy_id, you can check if authy_id field for the user is > 0. If so you can assume Authy is enabled.
Here's the function that validates username and password and redirects to the two-factor auth if authy is enabled:
def create @user = User.find_by_email(params[:user][:email]) if @user && @user.authenticate(params[:user][:password]) #username and password is correct if(@user.authy_id != 0) #user is using two-factor Authy::API.request_sms(:id => @user.authy_id) #request the API to send and sms. #Rails sessions are tamper proof. We can store the ID and that the password was already validated session[:password_validated] = true session[:id] = @user.id redirect_to url_for(:controller => "sessions", :action => "two_factor_auth") else sign_in(@user) flash[:notice] = "Successfully authenticated without two-factor" redirect_to @user end else flash[:error] = "Wrong username, password." @user = User.new render 'new' end end
Basically, when the user wants to log-in, you use his e-mail to locate the record in the database. Then you try to authenticate him. The authenticate function will check his username and password and also if the user has Authy enabled. If so you will redirect the user to the second page were he can enter his Token and complete his authentication. Here's the function that does the second factor authentication:
def create_two_factor_auth @user = User.find(session[:id]) token = params[:token] if @user && session[:password_validated] && @user.verify_token(token) sign_in(@user) @user.authy_used = true @user.save(:validate => false) session[:password_validated] = nil session[:id] = nil flash[:success] = "Securely signed in using Authy" redirect_to @user else flash[:error] = "Wrong token" redirect_to new_session_path end end
Token verification involves sending the token and the id. Authy will respond HTTP status 200 if the token is correct.
def verify_token(token) token = Authy::API.verify(:id => self.id, :token => token) token.ok? end
The function simply passes the authy_id and the token the user entered. Then it check if the response is HTTP status 200, if so it returns true.
To prevent users account getting locked down, Authy won't check the tokens until we are certain the user has completed the registration process. If you want to verify token's anyway, you can pass ?force=true to verify.