要点
Amazon Cognitoを使えば、deviseに依存せずに、ユーザ登録・ログイン・ログアウト・パスワードリセット・ソーシャルログイン・n段階認証・SAMLなど、様々な機能がアプリケーションコードから分離するかたちで追加可能です。Amazon Cognitoの他にも、Auth0やFirebase Authentication等があり、IDaaS(Identity as a Service)と呼ばれます。
本記事では、Railsアプリケーションで動作検証をする場合の手順を記載しています。
- Amazon Cognitoのユーザープールの作成とクライアントの設定の基本的な手順を解説(作業時間: 10分程度)
- 動作検証するためのサンプルアプリの実装方法を解決(作業時間: 10分程度)
なお、記事中のスクリーンショットに記載されたクライアントIDやシークレットは、既に削除されているため利用することはできません。ご自身で設定して動作検証してみてください。
環境
% ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
% bin/rails version
Rails 6.0.3.2
手順1: ユーザープールの作成とクライアントの設定
作業時間: 10分程度
Amazon Cognito のコンソールから「ユーザープールを作成する」を押下
プール名を入力
サンプルなのでデフォルトを確認するを押下
プールの作成
作成完了
プール ID
をメモする:
ドメイン名を設定
ナビゲーションの ドメイン名
をクリックし、認証フォームのURLを設定。設定した、 https://{your prefix}.auth.ap-northeast-1.amazoncognito.com
をメモする:
アプリクライアントの作成
ナビゲーションの アプリクライアント
をクリックし、 アプリクライアントの追加
をクリック:
アプリクライアントID
と アプリクライアントのシークレット
をメモ:
アプリクライアントの設定
ナビゲーションの アプリクライアントの設定を押下
し、コールバックURLとサインアウトURLを設定(それ以外の設定はスクリーンショットを参照):
これでcognito側の設定は完了です(なお、スクリーンショットの値は利用できません)。
手順2: アプリケーションの実装
作業時間: 10分程度
rails new
サンプルなので割愛
omniauth-cognito-idp を追加
gem 'omniauth-cognito-idp'
クレデンシャルの追加
今回はサンプルなので development
で、ユーザプール作成時に生成した諸々をセットする:
$ bin/rails credentials:edit -e development
aws:
access_key_id: 123
secret_access_key: 345
# 上記でメモした「ドメイン名」に含まれるリージョン
region: ap-northeast-1
# 上記でメモした「アプリクライアントID」
cognito_client_id: *****YOUR CLIENT ID*****
# 上記でメモした「アプリクライアントのシークレット」
cognito_client_secret: *****YOUR SECRET*****
# 上記でメモした「ドメイン名」
cognito_user_pool_site: https://*****YOUR PREFIX*****.auth.ap-northeast-1.amazoncognito.com
# 上記でメモした「プールID」
cognito_user_pool_id: *****YOUR POOL ID*****
イニシャライザの追加
# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider(
:cognito_idp,
Rails.application.credentials.aws[:cognito_client_id],
Rails.application.credentials.aws[:cognito_client_secret],
client_options: {
site: Rails.application.credentials.aws[:cognito_user_pool_site]
},
scope: 'email openid',
user_pool_id: Rails.application.credentials.aws[:cognito_user_pool_id],
aws_region: Rails.application.credentials.aws[:region],
)
end
HomeControllerの実装
root で表示する画面:
$ bin/rails g controller home show --skip-assets --skip-helper
<h1>Home#show</h1>
<p>Find me in app/views/home/show.html.erb</p>
<h1>RoR Cognito Sample</h1>
<p>Step 1 - Login.</p>
<%= button_to 'Login', 'auth/cognito-idp', method: :post %>
# config/routes.rb
Rails.application.routes.draw do
root 'home#show'
end
CognitoIdpControllerの実装
OmniAuthで認証後の処理をするコールバック:
$ bin/rails g controller cognito_idp --skip-template-engine --skip-assets --skip-helper
class CognitoIdpController < ApplicationController
def callback
# 実際は provider と uid を使って、Userレコードを作成/参照し、user_id をsessionにセットするが
# サンプルなのでauth hashをセット
session[:userinfo] = request.env['omniauth.auth']
redirect_to '/dashboard'
end
end
# config/routes.rb
Rails.application.routes.draw do
root 'home#show'
get 'auth/cognito-idp/callback' => 'cognito_idp#callback'
end
セッションストアでCookieを利用して session[:userinfo]
をセットしようとすると最大容量を超過するので、セッションストアをメモリに変更:
# config/initializers/session_store.rb
Rails.application.config.session_store :cache_store
config/environments/development.rb
に config.cache_store = :memory_store
を追加。
ApplicationControllerに認証に関する振る舞いの追加:
実際はcurrent_userなども実装するがサンプルなのでuserinfoがsessionにセットされているかどうかを確認:
class ApplicationController < ActionController::Base
private
def authenticate_user!
return redirect_to(root_path) unless signed_in?
end
def signed_in?
session[:userinfo].present?
end
helper_method :signed_in?
end
DashboardControllerの実装
ログイン後の画面を実装する:
$ bin/rails g controller dashboard show --skip-assets --skip-helper
class DashboardController < ApplicationController
before_action :authenticate_user!
def show
end
end
# config/routes.rb
Rails.application.routes.draw do
root 'home#show'
get 'auth/cognito-idp/callback' => 'cognito_idp#callback'
get 'dashboard' => 'dashboard#show'
end
<h1>Dashboard#show</h1>
<p>Find me in app/views/dashboard/show.html.erb</p>
<p><%= session[:userinfo].inspect %></p>
<p><%= link_to 'logout', logout_path, method: :delete %></p>
SessionsControllerの実装:
ログアウト処理を実装(cognito側もログアウトする必要がある):
$ bin/rails g controller sessions --skip-template-engine --skip-assets --skip-helper
class SessionsController < ApplicationController
before_action :authenticate_user!
def destroy
reset_session
redirect_to cognito_logout_url
end
end
direct ルーティングを使って、cognito側のログアウトを実装:
# config/routes.rb
Rails.application.routes.draw do
root 'home#show'
get 'auth/cognito-idp/callback' => 'cognito_idp#callback'
get 'dashboard' => 'dashboard#show'
delete 'logout' => 'sessions#destroy'
direct :cognito_logout do
query = {
client_id: Rails.application.credentials.aws[:cognito_client_id],
logout_uri: root_url,
}.to_param
"#{Rails.application.credentials.aws[:cognito_user_pool_site]}/logout?#{query}"
end
end
動作検証
rails server
を起動して localhost:3000
にアクセス:
ルートを表示
Login
を押下:
cognitoのログインフォームが表示されるのでサインアップする
メールアドレスを確認して認証コードを入力するとログインが完了します。
参考
- Amazon Cognito のドキュメント
- Sage/omniauth-cognito-idp: OmniAuth Strategy for AWS Cognito in Ruby
- Auth0 Ruby On Rails SDK Quickstarts: Login
以上