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 ユーザープール).
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.
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 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.
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
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
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.
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
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