この記事では、「Dockerを使ったRailsアプリ開発環境の構築」、「Rails5アプリでOAuth2認証をするためのセットアップ」をしてみます。
ざっとした全体像
ざっとした全体像です。
- 1.RailsアプリをDocker上に構築と下準備
- 1.1 Dockerfileの準備
- 1.2 docker-compose.ymlの準備
- 1.3 Gemfileの準備
- 1.4 Dockerイメージの作成
- 1.5 Railsのセットアップ
- 1.6 Railsの各種設定、微調整
- 1.1 Dockerfileの準備
- 2.OAuth2認証のための土台作り
- 2.1 doorkeeperのセットアップ
- 2.2 RailsのOAuth2まわりのコードを追加
- 2.3 doorkeeperの調整
- 2.4 Railsの調整
- 2.1 doorkeeperのセットアップ
- 3.動作確認
- 3.1 OAuth2アプリの作成
- 3.2 Userの作成
- 3.3 トークン発行
- 3.4 アクセストークンでリソースの取得
- 3.5 トークンの無効化
- 3.1 OAuth2アプリの作成
doorkeeper という Rails や Grape アプリに OAuth2 認証を導入することを容易にしてくれる gem を使います。
1. RailsアプリをDocker上に構築と下準備
最初は、Dockerの基本的な準備をしていきます。
1.1 Dockerfileの準備
まずは、Dockerイメージを作成するための Dockerfile というファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | FROM ruby:2.3.1 ENV APP_ROOT /usr/src/project_name WORKDIR $APP_ROOT RUN apt-get update && \ apt-get install -y nodejs \ mysql-client \ postgresql-client \ sqlite3 \ --no-install-recommends && \ rm -rf /var/lib/apt/lists/* COPY Gemfile $APP_ROOT COPY Gemfile.lock $APP_ROOT RUN \ echo 'gem: --no-document' >> ~/.gemrc && \ cp ~/.gemrc /etc/gemrc && \ chmod uog+r /etc/gemrc && \ bundle config --global build.nokogiri --use-system-libraries && \ bundle config --global jobs 4 && \ bundle install && \ rm -rf ~/.gem COPY . $APP_ROOT EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"] |
Ruby2.3.1 のイメージをレポジトリから取得し、それを元にした Dockerイメージ を使っています。これにより、rbenv 等を使用することも不要になります。
1.2 docker-compose.ymlの準備
次は docker-compose.yml の準備です。このファイルは複数のコンテナを管理することを容易にするためのものです。docker-compose
というコマンドを使い操作を行います。今回はRailsアプリのコンテナ、データベース用のコンテナの2つを作成する予定です。このように複数のコンテナがある場合、別々に docker
コマンドを打つのは大変なので docker-compose.yml
にまとめて記述しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | version: '2' services: app: build: . environment: RAILS_ENV: development DATABASE_URL: mysql2://root:pass@mysql:3306 MYSQL_ROOT_PASSWORD: 'pass' ports: - '3000:3000' volumes: - .:/usr/src/project_name links: - mysql mysql: image: mysql:5.7.10 environment: MYSQL_ROOT_PASSWORD: 'pass' ports: - '3306:3306' volumes: - mysql-data:/var/lib/mysql volumes: mysql-data: driver: local |
上で述べた通り、1コンテナ1サービスの原則に従いDBは別コンテナにしました。app
と mysql
というコンテナが定義されています。app
は 1.1 で作成したイメージです。そこに mysql
の v5.7.10 をレポジトリから取得して、作成しています。
1.3 Gemfileの準備
これはよくある Gemfile なので問題ないと思います。Rails と OAuth2 認証に関連した gem が追加で記述されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end gem 'devise' gem 'doorkeeper' gem 'omniauth' gem 'oauth2' gem 'rails', '~> 5.1.5' gem 'mysql2' gem 'puma', '~> 3.7' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.2' gem 'turbolinks', '~> 5' gem 'jbuilder', '~> 2.5' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] |
また、Gemfile.lock も空で良いので作成しておきます。
1.4 Dockerイメージの作成
基本的な準備が整ったので、イメージを作成します。docker-compose が定義されているため、下記の1つのコマンドを実行すれば2つのイメージが作成されるはずです。
1 | $ docker-compose build
|
作成に成功すると、下記のようにイメージが作成されていると思います。
1 2 3 4 | $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
project_name latest 49a371092eeb 8 seconds ago 888MB
ruby 2.3.1 ffe8239a147c 16 months ago 730MB
|
上記の docker images
コマンドはイメージのリストを表示します。
1.5 Railsのセットアップ
遂にコンテナを起動して、そのコンテナの中に Rails アプリをセットアップします。まずは、下記のコマンドを実行します。
1 | $ docker-compose run -v "$PWD":/usr/src/project_name --rm app rails new . -BT |
Rails5 の APIモード も使いたかったのですが、doorkeeper がまだ対応していないようなので通常の Rails アプリです。今後の対応予定はあるようです。
実行後に、ローカルの作業ディレクトリに Rails のファイルが一式セットアップされているか確認してください。この際に Rails 側で Gemfile がオーバーライドさせてしまった場合は、sqlite3
を mysql2
に変更。また下記の gem も Gemfile に追加することを忘れないでください。
1 2 3 4 | gem 'devise' gem 'doorkeeper' gem 'omniauth' gem 'oauth2' |
ここら辺が OAuth2 に関連した gem 達です。もし、Gemfile をアップデートして、 bundle install
が必要になった場合は、下記のコマンドを実行します。
1 2 3 4 5 | # 起動中のコンテナーを停止 $ docker-compose down # イメージを新しく作成 $ docker-compose build |
Dockerfile の中で gem をインストールするコマンドが入っており、イメージに gem 等の情報も入れるために再度コンテナをビルドしなおす必要があります。
1.6 Railsの各種設定、微調整
ここからは Rails のちょっとした設定です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # database.yml default: &default adapter: mysql2 encoding: utf8 port: 3306 pool: 5 timeout: 5000 url: <%= ENV['DATABASE_URL'] %> development: <<: *default database: db_development test: <<: *default database: db_test production: <<: *default database: db_production |
sqlite3 から mysql に変えたので修正。
下記のコマンドで devise の初期設定をします。今回は user
モデルを作成しています。
1 2 3 4 | $ docker-compose run --rm app rails g devise:install $ docker-compose run --rm app rails g devise user $ docker-compose run --rm app rake db:create $ docker-compose run --rm app rake db:migrate |
さらに、doorkeeper の初期設定をして OAuth2認証に必要のテーブル等を作成します。
1 2 3 | $ docker-compose run --rm app rails generate doorkeeper:install $ docker-compose run --rm app rails generate doorkeeper:migration $ docker-compose run --rm app rake db:migrate |
既に mysql とかは起動してるかもしませんが、docker-compose
で各コンテナが起動しているか確認してください。
1 | $ docker-compose up -d |
各コンテナを起動します。
1 2 3 4 | $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 01b530297ff8 my_project_name_app "rails server -b 0.0…" Less than a second ago Up 3 seconds 0.0.0.0:3000->3000/tcp project_name_app_1 396398aaaea3 mysql:5.7.10 "/entrypoint.sh mysq…" 3 minutes ago Up 3 minutes 0.0.0.0:3306->3306/tcp project_name_mysql_1 |
上記のように2つのコンテナが立ち上がっていれば大丈夫です。 http://0.0.0.0:3000/ にアクセスして Rails のホームが表示されていれば成功です。 これで、Rails が Dockerコンテナの中で動いています。
2. OAuth2認証のための土台作り
Dockerのセットアップが終わったので、次は OAuth2 認証周りの作成をします。
2.1 doorkeeperのセットアップ
doorkeeper.rb に下記を追加します。
1 2 3 4 5 6 7 | # doorkeeper.rb resource_owner_from_credentials do |routes| user = User.find_by_email(params[:username].downcase) if user && user.valid_password?(params[:password]) user end end |
今回は Resource Owner Password Credentials
というグラントタイプで実装をします。この実装方法は、必ずクライアントが信頼できる場合に使用することができるタイプです。
1 | Doorkeeper.configuration.token_grant_types << "password" |
最後に、同じファイルに下記の1行を追加します。
また、適宜このファイルでリフレッシュトークンの設定やトークンの期限を定めることができます。
2.2 RailsのOAuth2まわりのコードを追加
そして、実際に OAuth2認証ができているのか確認するため、簡単なコードを追加します。
1 2 3 4 5 6 7 8 | # routes.rb namespace :api do namespace :v1 do get '/me' => 'users#me' end root to: 'home#show' end |
よくあるルーティングです。
1 2 3 4 5 6 7 | # api/v1/api_controller.rb class Api::V1::ApiController < ApplicationController private def current_resource_owner User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token end end |
1 2 3 4 5 6 7 8 9 | # api/v1/users_controller.rb class Api::V1::UsersController < Api::V1::ApiController before_action :doorkeeper_authorize! respond_to :json def me respond_with current_resource_owner end end |
ベースとなる APIコントローラー と Userコントローラー を追加します。
また、ローカルでの変更がすぐにロードされるように下記の記述も追加します。
1 2 | # development.rb config.reload_classes_only_on_change = false |
さらに、CSRF対策でJSONリクエスト時にはエラーが出ないように下記のように記述。
1 2 3 4 | # application_controller.rb class ApplicationController < ActionController::Base protect_from_forgery unless: -> { request.format.json? } end |
app
コンテナの再起動をします。
1 2 | # このハッシュ値は自分の値を入れる docker restart 01b530297ff8 |
これで大方の準備が整ったので、ここからは実際にOAuth2認証ができているかを試していきます。
3. 動作確認
これまででだいたいのセットアップは完了です。
3.1 OAuth2アプリの作成
まずは、OAuth2のアプリ作成します。http://localhost:3000/oauth/applications にアクセスしてアプリを作成します。
例えば下記のように作成します。
1 2 3 | name: test1 callback: http://localhost:3001/users/auth/doorkeeper/callback scope: empty |
作成後は、client_id
と client_secret
が作成されるのでこれをメモします。 忘れた場合は、 oauth_applications
というテーブルにも登録されているので参照してください。
3.2 Userの作成
POST: http://0.0.0.0:3000/users
1 2 3 | user[email]: user's email user[password]: user's password user[password_confirmation]: user's password |
deviseのセットアップしていれば上記のように3つの値とPOSTリクエストを投げるとユーザーが新規に作成されるはずです。
3.3 トークン発行
POST: http://localhost:3000/oauth/token
1 2 3 4 5 | client_id client_secret username: registered email password: registered password grant_type: password |
これでアクセストークンが発行され、このトークンを使いリソースにアクセスします。
3.4 アクセストークンでリソースの取得
GET: http://0.0.0.0:3000/api/v1/me
上記のURLにGETリクエストのヘッダーに、
1 | Authorization Bearer #token# |
といった感じでアクセスし、ユーザーのメールアドレスやIDが見ることができれば成功です。
3.5 トークンの無効化
POST: http://0.0.0.0:3000/oauth/revoke
1 2 3 | client_id client_secret token: 破棄しようとしているアクセストークン |
破棄をした後は同じように情報を取得しようとしても401エラーが発生するはずです。
参考
RailsアプリをDockerで開発するための手順
Using Resource Owner Password Credentials flow
Protocol OAuth2: let’s play with Doorkeeper & Omniauth/OAuth2. Part 1.
Rails5 + Doorkeeper + DeviseでOAuthサーバーを実装する
Rails 5 API protect_from_forgery