Project

sms_auth

0.0
No commit activity in last 3 years
No release in over 3 years
A Rails engine for quickly adding SMS authentication to a Rails API
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

SMS Auth Build Status Code Climate Test Coverage Gem Version

A Rails engine for quickly adding SMS authentication to a Rails API.

What does this do?

It will let a user on your app authenticate with only their phone number. It'll provide them an authentication token that will be enforced on protected routes (described below). It will also provide user context on the protected routes.

This gem currently only supports PostgreSQL and creates 3 tables: phone_verifications, authentication_tokens, and users (it will skip this one if the table already exists)

Why use this?

This was originally built to support SMS authentication for an API used for a mobile application. If you are interested in adding this type of authentication to your API, this should get you up and running in a few minutes without the need to use a Warden based solution. It will let you accomplish authentication on a mobile app with a flow similar to below

blend authentication runthrough

Installation

Add the gem to you gemfile

gem 'sms_auth'

Run Bundle Install in the console

bundle install

Copy over the migrations

rake sms_auth:install:migrations

Setup the DB

rake db:create db:migrate

Configure the gem

Create an initializer called config/initializers/sms_auth.rb and set it up with your Twilio account information. You will need to purchase a phone number that can send text messages to use as your from.

require 'sms_auth'

SmsAuth::Engine.setup do |config|
  config.twilio_auth_token = 'AUTH TOKEN HERE'
  config.twilio_account_sid = 'ACCOUNT SID HERE'
  config.twilio_from_number = 'FROM_PHONE_NUMBER'
end

Mount the Engine in your config/routes, add it at the top

Rails.application.routes.draw do
  mount SmsAuth::Engine => '/', as: 'auth'
end

Update your controllers/application_controller to use the Auth Controller Helper provided

class ApplicationController < ActionController::Base
  include SmsAuth::Engine::AuthControllerHelper
  protect_from_forgery with: :null_session
end

If you have a specific controller that you want to be protected with authentication, use a before_filter

The authenticate_with_token! filter can be used on all controller methods or can be excluded to only specific ones.

class ExampleController < ApplicationController
  before_action :authenticate_with_token!
  respond_to :json
end

For authenticated controllers, the current_user and current_token objects are defined to allow you to use that information in your endpoints.

Configurable Options

Additional configuration options can be added within the initializer (as mentioned above). Here are the additional arguements allowed and what they do.

require 'sms_auth'

SmsAuth::Engine.setup do |config|
  config.twilio_auth_token = 'AUTH TOKEN HERE'
  config.twilio_account_sid = 'ACCOUNT SID HERE'
  config.twilio_from_number = 'FROM_PHONE_NUMBER'
  config.max_login_attempts = 5
  config.max_login_attempt_within_minutes = 60
end

message_prefix (string)

It will append a prefix to SMS texts being sent out (e.g. if it is set to "Onboarding App", then the user would receive "Onboarding App: Your verification code is 123456"

token_length (int, defaults to 6)

It will set the token length that is sent to user to be a code that is N length.

max_login_attempts (int, defaults to 3)

Number of attempts allowed in the allotted time frame before the account is temporarily locked.

max_login_attempt_within_minutes (int, defaults to 15)

Is used to determine how many login attempts to consider when looking at recent tries. If a user tried to login 3 times in the last 15 minutes, then their account would be locked. If you set this number to 60, then it would be locked if they tried 3 unsuccessful times in the last 60 minutes.

verification_token_time_limit_minutes (int, defaults to 5)

If a token is sent out to a user and they wait longer than N minutes to verify it, then it will make them request another

lock_min_duration (int, defaults to 60)

If a user unsuccessfully verifies their token the maximum number of times within the allotted max_login_attempt_within_minutes then it will lock their account for N minutes.

default_token_expiration_days (int, defaults to 90)

When creating a new authentication_token, it will expire in 90 days. The protected endpoints will return a 401 if the token expired, so that you can then try to request a new one.

limited_recipients (array of strings, defaults to [])

If you are testing in an environment and would like to limit phone numbers that can authenticate to only specific phone numbers then set this as an array of numbers (e.g. ['3125552333','3123332255']) and only ones that are within this will be allowed to authenticate and receive the text verification code.

Routes

This will mount 3 routes to your application: /auth/login, /auth/verify, and /auth/logout

POST /auth/login?phone_number=312-555-2333

This will cleanup the phone number and initiate the sending of the verification token to the SMS device

POST /auth/verify?phone_number=312-555-2333&verification_token=680587

After the device receives the verification_token, you will post to this endpoint with the phone_number and verification_token received via SMS. If successful, it will return this body:

{
  "message": "",
  "success": true,
  "authentication_token": "mr-B9xBaHyJABkyR9jgq1FRs7zdZFE4VsFWR_J7y",
  "user_id": 1,
  "new_user": false
}

Store the authentication_token on the device and pass it in as Authorization within the header. If not provided on protected routes, it will not allow you to continue. new_user is true only if the user was just created on his/her first verification. At this point, you can redirect to pages that would enrich other parts of the User model.

DELETE /auth/logout

All authenticated routes (including logout) will require the Authorization added to the header.

Error Handling

All endpoints will return a HTTP status code of 400 if there is an issue and return a message and success key within the json body to then be displayed to the Client.

{
  "message": "The verification token used is expired. Please request a new one and try again.",
  "success": false
}

If it succeeds, it will return a HTTP status code of 200 and both keys

{
  "message": "",
  "success": true
}