Choosing Two Factor Authentication to Secure our Login Page

Securing our users has always been a challenge on the web. For years, we’ve found that the traditional usernames and passwords provided a secure method for authenticating users. They enter their credentials, submit the form, and in no time, they gain access to the providers services. Better known as a Single Factor of Authentication, SFA is still used across majority of the sites and has been a reliable security method. But, over time, attackers have been able to expose user credentials applying certain techniques. As a way of protection our users, we started to implement password checks and used well known hashing and salting techniques at the database level. But in most cases, not all password checks are equal and not all hashing techniques are reliable enough to prevent attacks.This is where Two Factor Authentication comes in.
So what is Two Factor Authentication? TFA a method that is based on users providing two factors of information rather than a single credential. Now, why is this a better option? For one, it adds an extra layer of security over the traditional username and password by removing the need to create highly secure password checks and two, introducing a second factor such as a device the user owns, increases the difficultly for hackers to obtain a successful attack. Here at Osprey, TFA was one of the strategies we wanted to integrate for our login portal. With multiple 2F strategies available, we found a new strategy created by the Facebook team called Account kit.
Introducing Facebook’s Account Kit
Introduced at the F8 conference this April, Account kit was a new product that Facebook launched for all developers to secure their mobile and web applications. In short, Account kit is a TFA and Single Sign On method that allows users to login and register using their phone number or email. With Account kit, developers don’t have to create a separate workflow to handle new registrations because their infrastructure handles authentication and managing user accounts. To get a better picture on how it works, let’s take a look at what happens during a simple login using Account kit:
1.Your application makes a call using Account Kit’s API’s with either a phone number or email address to initiate a login or registration.
2. Account Kit servers send an SMS with a confirmation code to continue the login, or a confirmation email to the email address.
3.The SDK will verify the SMS confirmation code or monitor the status of the confirmation email.
4. If your app has Client Access Token Flow turned on, your app will receive an access token containing an additional account information in response to a successful login. If your app has Client Access Token Flow turned off, your client app will receive an authorization code that your application’s server may use to securely request an access token.
Below is a diagram of the exchanges that occur during a login or registration process.

Token based Authentication Flow
Now that we’ve established some context on what Account Kit is and how it works, let’s take a closer look at how it handles Authentication. The primary strategy Account kit takes on Authentication is based on two types of access tokens, user access tokens and app access tokens. These tokens provide secure access to Account Kit APIs. User access tokens validate the identity of a user when making calls to the server API’s. App access tokens on the other hand, are used to make server calls on behalf of your application and works for account management operations. There are two ways to handle this type of authentication flow.
The first is turning the Client Access Token Flow feature on in which your client application (after a successful login) receives a long-lived access token. Then, you can pass this token securely to your server to be used in API calls.
The second method is turning the Client Access Token Flow off where your client application will instead, (after a successful login) receive a short-lived authorization code. Then, with this token, you can pass to your server and use the code to securely retrieve an access token, which may be used for API calls.
Although both methods may be used, enabling the client access token flow and exposing the access token at the browser level will increase the chances of attack. Disabling it offers as a threat mitigation for cases where an attacker might attempt to impersonate your client application, or otherwise intercept the long-lived API access token. It is highly recommended to disable the feature to ensure an extra layer of security and enabling all the exchanges of sensitive data at your application’s servers rather at the browser level.
Integrating Account Kit
In our example, we are going to show you how we integrated Account kit into our current technology stack. Since this will be an example based on a web based application, we’ll be using the JavaScript SDK provided by Account kit as our main integration piece. Go will drive the back-end and AngularJS for our front-end.
Getting Started
The first step was to create a Facebook application and add Account Kit as an additional feature. Once configured, we disabled the Client Access Token Flow so that the token exchanges occur at our application’s server.

Building our Back-end
Here’s a snippet of the Go code we’ve got setup to authenticate our users via Account kit.
type AuthCode struct {
Code string
State string
Status string
}
type AccessToken struct {
Id string
Access_token string
Token_refresh_interval_sec int16
}
type UserCredentials struct {
Email string
Phone string
}
type UserValidatedResponse struct {
Email string
Phone string
Access_token string
Token_refresh_interval_sec int16
}
func sendAuthCode(w http.ResponseWriter, r *http.Request) {
// We are using Account Kit which is version 1.0
// Facebook Graph API is version 2.6 and will be displayed in your
// Facebook app dashboard, but setting 2.6 for the api_version will not
var (
api_version = "v1.0"
app_id = "{{Facebook App Id}}"
app_secret = "{{Account Kit App Secret}}"
token_exchange_url = "https://graph.accountkit.com/v1.0/access_token?grant_type=authorization_code&"
user_endpoint_url = "https://graph.accountkit.com/v1.0/me/?"
authCode AuthCode
authAccessToken Access_token
userCredentials UserCredentials
userValidatedResponse UserValidatedResponse
)
//Read body of initial request
reqBody, _ := ioutil.ReadAll(r.Body)
if err := json.Unmarshal(reqBody, &authCode); err != nil {
log.Fatal(err)
}
//send request to token exchange url
//to exchange for access token
respExchange, err := http.Get(token_exchange_url + "code=" + authCode.Code + "&access_token=AA|" + app_id + "|" + app_secret)
defer respExchange.Body.Close()
if err != nil {
log.Fatal(err)
}
//Read body of request
respExchangeBody, _ := ioutil.ReadAll(respExchange.Body)
//store access token in model
if err := json.Unmarshal(respExchangeBody, &authAccessToken); err != nil {
log.Fatal(err)
}
//send request to user endpoint url to
//validate user credentials with facebook's db
respValidation, err := http.Get(user_endpoint_url + "access_token=" + authAccessToken.Access_token)
defer respValidation.Body.Close()
if err != nil {
log.Fatal(err)
}
//store user credentials from facebook response
if err := json.Unmarshal(respValidation, &userCredentials); err != nil {
log.Fatal(err)
}
//create user credentials to send to client
//which includes access token, refresh token, and
//user informaton from facebook's db
if userCredentials.Email !== "" {
userValidatedResponse := &UserValidatedResponse{
Email: userCredentials.Email,
Acccess_token: authAccessToken.Access_token,
Token_refresh_interval_sec: authAccessToken.Token_refresh_interval_sec,
}
} else if userCredentials.Phone == "" {
userValidatedResponse := &UserValidatedResponse{
Phone: userCredentials.Phone,
Acccess_token: authAccessToken.Access_token,
Token_refresh_interval_sec: authAccessToken.Token_refresh_interval_sec,
}
} else {
userValidatedResponse := &UserValidatedResponse{
Acccess_token: authAccessToken.Access_token,
Token_refresh_interval_sec: authAccessToken.Token_refresh_interval_sec,
}
}
output, err := json.Marshal(userValidatedResponse)
if err != nil {
log.Fatal(err)
}
//write back to client with proper user credentials
//as JSON Type
w.Header().Set("Content-Type", "application/json")
w.Write(output)
}
The server handles a specific route at /sendAuthCode. This post request/response is responsible for communicating with Facebook’s Infrastructure and exchanging tokens to authenticate and validate the users credentials. Once verified, we send the available credentials along with the access token and refresh token to the user.
Building the Front-end
First step was to add Account Kit’s JavaScript SDK to our initial login page located at index.html.
<!DOCTYPE html>
<html lang="en”>
<head>
<!-- Account Kit Facebook JavaScript SDK —->
<script src="https://sdk.accountkit.com/en_US/sdk.js"> </script>
</head>
</html>
Setting up Angular
Now that we’ve made the SDK available throughout our application, let’s look at how we integrated Account Kit into Angular. In this example, we’ll go through three different layers (MVC) at which we used Angular to work with Account Kit’s SDK.
1). Model ( Account Kit SDK )
In this example, we have an Angular Service that initiates Account Kit’s SDK and makes it available across our application.
(function(angular, window) {
'use strict';
angular
.module('OspreyAccountKitExample')
.service('AccountKitService', AccountKitService);
AccountKitService.$inject = ['$http'];
function AccountKitService(http) {
var country_code = "+1",
authUrl = "/sendAuthCode"
//Initialize Account Kit once detected
AccountKit_OnInteractive = function(){
AccountKit
.init(
{
appId: "{{Facebook App Id}}",
state: "{{CSRF TOKEN}}",
version:"v1.0"
}
);
};
function loginViaPhone(phoneNumber) {
return AccountKit.login('PHONE', {countryCode: country_code, phoneNumber: phoneNum}, loginCallback);
}
function loginViaEmail(email) {
return AccountKit.login('EMAIL', {emailAddress: email}, loginCallback);
}
function loginCallback(response) {
if (response.status === "PARTIALLY_AUTHENTICATED") {
return $http
.post(authUrl, response)
.then(handleSuccessLoginCallback)
.catch(handleErrorLoginCallback)
}
else if (response.status === "NOT_AUTHENTICATED") {
// handle authentication failure
}
else if (response.status === "BAD_PARAMS") {
// handle bad parameters
} else {
//handle Invalid request
}
}//End of loginCallBack
function handleSuccessLoginCallback(response) {
if( response.status == 200 ) {
//handle response
//User credentials, Access token. token refresh ttl
return response;
} else {
//throw error
}
}//End of handleSuccessLoginCallback
function handleErrorLoginCallback(error) {
//handle Error
}/End of handleErrorLoginCallback
//exposed methods to service
return {
loginViaPhone: loginViaPhone,
loginViaEmail: loginViaEmail
};
}//End of AccountKitService
}(angular, window));
2).View ( Login Template )
Here, we have our basic html login form with Angular directives to manage the DOM elements.
<form role="form">
<div class="accountkit-form">
<div class="input-group">
<span class="input-group-addon">
US+1
</span>
<input
type="text"
ng-model="accountkitctrl.phoneNum"
placeholder="e.g. 444555666" />
<span
class="input-group-addon btn-default"
ng-click="accountkitctrl.submitPhone(accountkitctrl.phoneNum)">
Login via SMS
</span>
</div>//end of input group
<div class="accountkit-middle-split">
<h6>
OR
</h6>
</div>//end of accountkit middle split
<div class="input-group">
<span id="sizing-addon2">
Email
</span>
<input
type="text"
ng-model="accountkitctrl.email"
placeholder="[email protected]"
style="border-radius: 0px; font-weight: bold; width: 168px" />
<span
class="input-group-addon btn-default"
ng-click="accountkitctrl.submitEmail(accountkitctrl.email)">
Login via Email
</span>
</div>//end of input group
<div class="accountkit-bottom-section">
Already have an account?
<a ng-click="mobileToLoginTransition()">
Sign in
</a>
</div>// end of account kit bottom section
</div>//end of Account Kit Form
</form>//end of Form
3). Controller (Login Logic)
Lastly, tying everything together using Angular’s controller to handle the communication between Account Kit’s SDK and our login template.
(function(angular, window) {
'use strict';
angular
.module('OspreyAccountKitExample')
.controller('AccountKitCtrl', AccountKitCtrl);
AccountKitCtrl.$inject = ["AccountKitService"];
function AccountKitCtrl(AccountKitService) {
var accountkitctrl = this;
//account kit form user credentials
accountkitctrl.phoneNum = "";
accountkitctrl.email = "";
//submit via phone
accountkitctrl.submitPhone = function( phoneNum ) {
AccountKitService
.loginViaPhone(phoneNum)
.then(handleResponseSuccess)
.catch(handleErrorResponse);
};
//submit via email
accountkitctrl.submitEmail = function( email ) {
AccountKitService
.loginViaEmail(email)
.then(handleResponseSuccess)
.catch(handleErrorResponse);
};
//handle success and error responses
function handleResponseSuccess( response ) {
//handle response success
}
function handleErrorResponse( error ) {
//handle error
}
}//end of AccountKitCtrl
}(angular, window));
Now that we’ve seen how we intregated Account Kit into our front-end, let’s see it in action.
Account Kit in Action
In this example, we’ll showcase our login process via mobile number. Shown below is a typical user login process using account kit.
Here’s a quick walkthrough of all the events that takes place
1). User enters their mobile phone number.
2). User is prompted a window that allows them to validate their credentials.
3). User receives a one time code on their phone
4). User submits the code and is successfully redirected to our dashboard.
Conclusion
Implementing account kit not only added a layer of of security but also significantly raised the barrier and limited the effectiveness for possible attacks. Even if hackers are able to compromise our passwords via a phishing email, malware, or other attack, they still won’t have access to the second factor, i.e., the associated smartphone, landline, or token. Even in the worst case scenario we are still only vulnerable for a single transaction decreasing the number of possible attacks. For securing our users, Increasing the difficulty for attackers by a significant factor is always worth it. Although 2FA isn’t guaranteed to prevent all attacks, we knew that enabling it lessened the chances of being in the position of one. That said, even though there is no perfect security, there are plenty of best standards and practices out there that help add security to our infrastructures. With so many corporations adopting TFA, we have no doubt that choosing it to secure our login portal would be a great option.