Introduce two-factor authentication to your Rails application

Introduction

This is the 16th day article of Zeals Advent Calendar 2020.

I'm Takakuda, a backend engineer at Zeals. Today, I'm going to write an article about investigating how to introduce two-factor authentication for Rails applications. Since it is mainly the contents of the AWS cognito user pool, the main content is from [Amazon Cognito User Pool](## Amazon Cognito ユーザープール).

background

Is two-factor authentication installed in the services you usually use or develop? Most of them can be logged in only with email and password, and recently, most of them can be completed by SNS login using Google or Facebook. Recently, you may have heard of some services that have become a big problem due to unauthorized login. I think you often hear the term two-step verification, but what is the difference from the two-factor authentication described in the title this time?

First, let's talk about "authentication factors." "Authentication factor" is the type of authentication method required to perform authentication.

--Knowledge element

What you know and remember (passwords and secret questions)

--Possession element

Information sent to your device How to use Google Authenticator or use messages sent by SMS etc.

--Biological elements

Fingerprint authentication and face authentication

The one that uses two or more of these three authentication elements is called two-factor authentication or multi-factor authentication. Two-step verification is like completing the verification by answering a secret question after entering an email and password, and completing the verification in multiple steps.

It seems that the term two-step verification is not often used overseas, and what is needed when considering safety is not the number of steps, but the number of verification factors.

2FA

Now let's actually implement 2FA for Rails applications.

environment

devise

If you're creating a login feature in your Rails application, you probably use devise. If you are using devise, you can implement 2FA by introducing a gem that extends devise.

devise-two-factor You can easily introduce two-factor authentication rqrcode Generate QR code

Or google-authenticator Can be integrated with Google Authenticator

It can be implemented by using the existing gem.

Amazon Cognito User Pool

Amazon Cognito User Pool is a service that provides the authentication function required by the application.

There is no need to implement the authentication function on your own, and we will use the one provided in the cloud. sdk is also provided, so we will use it.

Client creation

def initialize
 cognito_client = Aws::CognitoIdentityProvider::Client.new(
   region: ENV['COGNITO_REGION'],
   access_key_id: ENV['COGNITO_ACCESS_KEY_ID'],
   secret_access_key: ENV['COGNITO_SECRET_ACCESS_KEY']
  )
end

Create user pool

The user pool is the Amazon Cognito user directory. User pools allow users to log in to web or mobile apps through Amazon Cognito. Users can also sign in to the user pool via social identity providers such as Google, Facebook, Amazon, Apple, and his SAML-based identity provider. All members of the user pool, whether users sign in directly or through a third party, have a directory profile that they can access through the Software Development Kit (SDK). https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html

def create_user_pool(user_pool_name)
  res = client.create_user_pool(
    pool_name: user_pool_name,
    policies: {
      password_policy: {
        minimum_length: 8,
        require_uppercase: false,
        require_lowercase: false,
        require_numbers: false,
        require_symbols: false,
        temporary_password_validity_days: 1
      }
    }
  )
  res.user_pool.id
end

Creating an app client

The application client ID and client secret are required to call API operations such as password registration and sign-in. Allow only authorized client apps to call these unauthenticated operations.

def create_cognito_app_client(user_pool_id)
  res = client.create_user_pool_client(
    user_pool_id: 'ap-northeast-1_XXXXXXXXX',
    client_name: 'cognito-app-client',
    explicit_auth_flows: ['ADMIN_NO_SRP_AUTH'],
    prevent_user_existence_errors: 'ENABLED'
  )
  res.user_pool_client.client_id
end

sign_up

def sign_up(email, password, app_client_id)
  res = client.sign_up(
    client_id: app_client_id,
    username: email,
    password: password
  )
  res.user_sub
end

sign_in

def sign_in(email, password, app_client_id)
  res = client.admin_initiate_auth(
    user_pool_id: 'ap-northeast-1_XXXXXXXXX',
    client_id: app_client_id,
    auth_flow: 'ADMIN_NO_SRP_AUTH',
    auth_parameters: {
      USERNAME: email,
      PASSWORD: password
    }
  )
  res.authentication_result.access_token
end

Here's some code that does most of the basic operations.

If you want to enable multi-factor authentication, you can enable it by changing the user pool settings.

Screen Shot 2020-12-16 at 18.59.10.png

If you check it as optional, you can set whether to apply MFA for each user. As a second factor, you can set an SMS text message or a one-time password (Google Authenticator). Now you can see which of the authentication factors you have. https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-mfa.html

After setting up two-factor authentication, you can authenticate using the verification code sent/registered with Google Authenticator.

resp = client.confirm_sign_up({
  client_id: app_client_id,
  username:  "user_name",
  confirmation_code: "*******",
  force_alias_creation: false,
})

Not only is it easy to set up MFA with cognito, but it also provides a login page itself and a mechanism to migrate from the existing authentication infrastructure to cognito. https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-migrate-user.html

Summary

When I usually touch Rails, I was always conscious of how to solve the problem in Rails, but by using Cognito etc., I cut out the authentication part etc. using an external service. I think I can go. When a service grows or multiple services are provided, it takes man-hours to create an authentication infrastructure for each service. I want to enjoy what I can enjoy at that time!

Recommended Posts

Introduce two-factor authentication to your Rails application
Introduce Vue.js to Rails
How to introduce Basic authentication
Deploy your application to WildFly
Rails6 I tried to introduce Docker to an existing application
Try to introduce OpenCV to Android application
Preparing to create a Rails application
Introducing full calendar to Rails application
Until you introduce fonts to Rails
How to introduce jQuery in Rails 6
I tried to introduce CircleCI 2.0 to Rails app
[Rails 6] Introducing Google Maps to your portfolio
Preparing to introduce jQuery to Ruby on Rails
[Rails 5.x] How to introduce free fonts
Introduce Kotlin to your existing Java Maven Project
[Rails] Introduce Carrierwave
Try deploying Rails application to EC2-Part 2 (Server construction)-
Introduce docker to the application you are creating
Introduce Rails Presenter
Introduce dotenv to Docker + Rails to manage environment variables
[Rails] How to easily introduce slick (slider function)
Introduce devise in Rails to implement user management functionality
How to write Rails
About rails application server
Introducing CircleCI to Rails
Introducing Bootstrap to Rails !!
How to uninstall Rails
[Rails] Create an application
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
What to do when you launch an application with rails
[With back tricks] How to introduce React to the simplest Rails
How to deploy jQuery in your Rails app using Webpacker
How to deploy a Rails application on AWS (article summary)