Menu

Category

Archive

logo


Rails5 / Docker / OAuth2 認証のセットアップ

2018-03-25 22:00:00 +0900
  • このエントリーをはてなブックマークに追加

この記事では、「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の各種設定、微調整
  • 2.OAuth2認証のための土台作り  
    • 2.1 doorkeeperのセットアップ  
    • 2.2 RailsのOAuth2まわりのコードを追加  
    • 2.3 doorkeeperの調整  
    • 2.4 Railsの調整
  • 3.動作確認  
    • 3.1 OAuth2アプリの作成
    • 3.2 Userの作成
    • 3.3 トークン発行
    • 3.4 アクセストークンでリソースの取得
    • 3.5 トークンの無効化

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は別コンテナにしました。appmysql というコンテナが定義されています。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 がオーバーライドさせてしまった場合は、sqlite3mysql2に変更。また下記の 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_idclient_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