このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

web帳

記事詳細

2017.05.20

Rails5 + devise token authで作る 認証API

どもです。

ちょっとRailsの認証周りについて少々。

サーバーサイドで認証が必要な場合、大きく2つの実装方法があるかと思います。

Cookieベースの認証

Tokenベースの認証

今回は、Tokenベースの認証について行っていきたいと思います。

Ruby oc Railsを基に構築し、Cookieベースの認証を行う場合、ログイン機能や、ユーザーデータ周りを Gemであるdeviseにお任せすることも多々あるかと思います。

そんな、deviseを使って、トークンベースの認証を行いたい場合「devise token auth 」というGemを使うと、何かと容易に実装が行えますので、行っていきます。

準備

それでは、早速作っていきましょう。

まず、rails new でアプリケーションを生成します。

$ rails new app_name --api -d mysql

続いて、Gemfileを編集しましょう。

新たに、「devise」と「devise_token_auth」を追加し、「jbuilder」と、「rack-cors」のコメントアウトを削除します。

Gemfile

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

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.2'
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.3.18', '< 0.5'
# Use Puma as the app server
gem 'puma', '~> 3.0'

# jsonを扱うためのgem
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# クロスドメイン対策
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'

# トークンベースの認証を行う
gem 'devise'
gem 'devise_token_auth'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platform: :mri
end

group :development do
gem 'listen', '~> 3.0.5'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

gemファイルの編集が終わりましたら、bundle installでgemをインストールします。

一応インストール先の pathは、「vendor/bundle」としておきます。

$ bundle install --path vendor/bundle

gemのインストールが完了しましたら、データベースの作成を行います。

$ bundle exec rake db:create

mysqlサーバーを起動していない場合は起動しておきましょう。

$ mysql.server start

ひとまず、準備は完了しましたので、続いては、「deveise token auth」を用いて データを作成していきます。

devise token auth

rails g コマンドで「deveise_token_auth」が使用できるようになっていますので、以下のコマンドで Userデータを生成します。

$ rails g devise_token_auth:install User auth

以下の様に表示すれば成功。

create  config/initializers/devise_token_auth.rb
create  db/migrate/20170520062742_devise_token_auth_create_users.rb
create  app/models/user.rb
insert  app/controllers/application_controller.rb
gsub  config/routes.rb

完了すると、マイグレーションファイルが生成されますので、そのファイルを編集していきます。

今回は、簡易版という事もあって、ユーザーが新規に登録する際、

・名前

・メールアドレス

・所属チーム

を、登録できるようにカラムの準備を行っておきます。

また、twitterやfacebookなどのソーシャルプラグインによるログインや、登録する際にメール送信して確認。などと言った機能に関して今回は行わないので、コメントアウトしておきます。

ユーザーモデルである、 user.rb を編集します。

app/models/user.rb

class User < ActiveRecord::Base
  # Include default devise modules.
  devise :database_authenticatable, :registerable,
  :recoverable, :rememberable, :trackable, :validatable
  #:confirmable, :omniauthable
  include DeviseTokenAuth::Concerns::User
end

次に、ユーザーカラム生成のマイグレーションファイルを修正します。

主な修正点として、「Confirmable」のコメントアウトと「confirmation_token」の add_indexのコメントアウト、ユーザーカラム(User Info)に「所属チーム」用のカラム 「team」の追加の記述となります。

db/migrate/xxxxxxx_devise_token_auth_create_users.rb

class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table(:users) do |t|
      ## Required
      t.string :provider, :null => false, :default => "email"
      t.string :uid, :null => false, :default => ""

      ## Database authenticatable
      t.string :encrypted_password, :null => false, :default => ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, :default => 0, :null => false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable コメントアウト
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      ## User Info
      t.string :name
      t.string :nickname
      t.string :image
      t.string :email
      t.string :team

      ## Tokens
      t.text :tokens

      t.timestamps
    end

    add_index :users, :email,                unique: true
    add_index :users, [:uid, :provider],     unique: true
    add_index :users, :reset_password_token, unique: true

    # コメントアウト
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,       unique: true
  end
end

ここまで、完了しましたら、マイグレーションコマンドを実行して、ユーザーテーブルの生成を行います。

$ bundle exec rake db:migrate

以下の様に、実行されれば成功。

== 20170520062742 DeviseTokenAuthCreateUsers: migrating =======================
-- create_table(:users)
-> 0.0164s
-- add_index(:users, :email, {:unique=>true})
-> 0.0158s
-- add_index(:users, [:uid, :provider], {:unique=>true})
-> 0.0126s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0140s
== 20170520062742 DeviseTokenAuthCreateUsers: migrated (0.0590s) ==============
Routes

現在のルートの確認を行ってみましょう。

$ rake routes
Prefix Verb   URI Pattern                    Controller#Action
new_user_session GET    /auth/sign_in(.:format)        devise_token_auth/sessions#new
user_session POST   /auth/sign_in(.:format)        devise_token_auth/sessions#create
destroy_user_session DELETE /auth/sign_out(.:format)       devise_token_auth/sessions#destroy
new_user_password GET    /auth/password/new(.:format)   devise_token_auth/passwords#new
edit_user_password GET    /auth/password/edit(.:format)  devise_token_auth/passwords#edit
user_password PATCH  /auth/password(.:format)       devise_token_auth/passwords#update
PUT    /auth/password(.:format)       devise_token_auth/passwords#update
POST   /auth/password(.:format)       devise_token_auth/passwords#create
cancel_user_registration GET    /auth/cancel(.:format)         devise_token_auth/registrations#cancel
new_user_registration GET    /auth/sign_up(.:format)        devise_token_auth/registrations#new
edit_user_registration GET    /auth/edit(.:format)           devise_token_auth/registrations#edit
user_registration PATCH  /auth(.:format)                devise_token_auth/registrations#update
PUT    /auth(.:format)                devise_token_auth/registrations#update
DELETE /auth(.:format)                devise_token_auth/registrations#destroy
POST   /auth(.:format)                devise_token_auth/registrations#create
auth_validate_token GET    /auth/validate_token(.:format) devise_token_auth/token_validations#validate_token

devise_token_authのデフォルトのルート設定がされているのが確認できます。

今回は、userのteamが編集出来るように、コントローラーをオーバライドして「team」を追加していきます。

rails ジェネレータコマンドでコントローラーを生成します。

$ rails g controller auth/registrations

コントローラーが生成されましたら、編集を行っていきます。

app/controllers/auth/registrations_controller.rb

module Auth
  class RegistrationsController < DeviseTokenAuth::RegistrationsController

  private
  def sign_up_params
  params.permit(:name, :email, :team, :password, :password_confirmation)
  end

  def account_update_params
  params.permit(:name, :email, :team)
  end

  end
end

続いて、ルートを変更し、先程のコントローラーが使えるようにしていきます。

config/routes.rb

Rails.application.routes.draw do
  mount_devise_token_auth_for 'User', at: 'auth', controllers: {
    registrations: 'auth/registrations'
  }
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

rake routes コマンドで、先程のauth/registrationsにパスが作成されていたら成功です。

$ rake routes
Prefix Verb   URI Pattern                    Controller#Action
new_user_session GET    /auth/sign_in(.:format)        devise_token_auth/sessions#new
user_session POST   /auth/sign_in(.:format)        devise_token_auth/sessions#create
destroy_user_session DELETE /auth/sign_out(.:format)       devise_token_auth/sessions#destroy
new_user_password GET    /auth/password/new(.:format)   devise_token_auth/passwords#new
edit_user_password GET    /auth/password/edit(.:format)  devise_token_auth/passwords#edit
user_password PATCH  /auth/password(.:format)       devise_token_auth/passwords#update
PUT    /auth/password(.:format)       devise_token_auth/passwords#update
POST   /auth/password(.:format)       devise_token_auth/passwords#create
cancel_user_registration GET    /auth/cancel(.:format)         auth/registrations#cancel
new_user_registration GET    /auth/sign_up(.:format)        auth/registrations#new
edit_user_registration GET    /auth/edit(.:format)           auth/registrations#edit
user_registration PATCH  /auth(.:format)                auth/registrations#update
PUT    /auth(.:format)                auth/registrations#update
DELETE /auth(.:format)                auth/registrations#destroy
POST   /auth(.:format)                auth/registrations#create
auth_validate_token GET    /auth/validate_token(.:format) devise_token_auth/token_validations#validate_token

auth/registrations に、「#new」、「#update」などのアクションが付与されているのを確認。

ここまでで一旦、設定は完了です。お疲れ様でした!

initialize

毎回、毎回、トークンが変更されるのはちょっと面倒なので、以下の様に設定しておきます。

config.change_headers_on_each_request を true にしていると、リクエストごとに token を新しくする必要がある。 という設定になるので、コメントアウトを解除し「false」に変更します。

# config.change_headers_on_each_request = true

config.change_headers_on_each_request = false
# config.token_lifespan = 2.weeks

config.token_lifespan = 1.month

その他、config.headers_namesのコメントアウトを解除。

config/initializers/devise_token_auth.rb

DeviseTokenAuth.setup do |config|
# By default the authorization headers will change after each request. The
# client is responsible for keeping track of the changing tokens. Change
# this to false to prevent the Authorization header from changing after
# each request.
config.change_headers_on_each_request = false

# By default, users will need to re-authenticate after 2 weeks. This setting
# determines how long tokens will remain valid after they are issued.
config.token_lifespan = 1.month

# Sets the max number of concurrent devices per user, which is 10 by default.
# After this limit is reached, the oldest tokens will be removed.
# config.max_number_of_devices = 10

# Sometimes it's necessary to make several requests to the API at the same
# time. In this case, each request in the batch will need to share the same
# auth token. This setting determines how far apart the requests can be while
# still using the same auth token.
# config.batch_request_buffer_throttle = 5.seconds

# This route will be the prefix for all oauth2 redirect callbacks. For
# example, using the default '/omniauth', the github oauth2 provider will
# redirect successful authentications to '/omniauth/github/callback'
# config.omniauth_prefix = "/omniauth"

# By default sending current password is not needed for the password update.
# Uncomment to enforce current_password param to be checked before all
# attribute updates. Set it to :password if you want it to be checked only if
# password is updated.
# config.check_current_password_before_update = :attributes

# By default we will use callbacks for single omniauth.
# It depends on fields like email, provider and uid.
# config.default_callbacks = true

# Makes it possible to change the headers names
config.headers_names = {:'access-token' => 'access-token',
                        :'client' => 'client',
                        :'expiry' => 'expiry',
                        :'uid' => 'uid',
                        :'token-type' => 'token-type' }

# By default, only Bearer Token authentication is implemented out of the box.
# If, however, you wish to integrate with legacy Devise authentication, you can
# do so by enabling this flag. NOTE: This feature is highly experimental!
# config.enable_standard_devise_support = false
end

Restlet Client – REST API Testing

REST APIをテストするのに「Restlet Client – REST API Testing」をインストールして試していきます。

chromeの拡張機能として提供されているパッケージとなります。

Restlet Client – REST API Testing

APIテスト

Restlet Client – REST API Testing」をインストールしたら早速試していきましょう。

Rails s コマンドでRailsサーバーを起動します。

$ rails s
ユーザー登録

ユーザー登録は「localhost:3000/auth」のpostで作成されます。

sendデータ 例:

{
  "name": "daisuke.takayama",
  "email": "webcyou@example.com",
  "password": "password",
  "team": "ウェブ帳"
}

レスポンス

{
  "status": "success",
  "data":{
    "id": 1,
    "email": "webcyou@example.com",
    "provider": "email",
    "uid": "webcyou@example.com",
    "name": "daisuke.takayama",
    "nickname": null,
    "image": null,
    "team": "ウェブ帳",
    "created_at": "2017-05-20T07:41:15.000Z",
    "updated_at": "2017-05-20T07:41:16.000Z"
  }
}

成功すると、上記の様にレスポンスが返ってくるかと思います。

DBの方を確認すると、usersテーブルにユーザーデータが作成されているのが確認できます。

続いて、ログインしてみましょう。

ログイン

ログインは「localhost:3000/auth/sign_in」にPOSTでログインとなります。

sendデータ 例:

{
  "email": "webcyou@example.com",
  "password": "password"
}

レスポンス

{
  "data":{
    "id": 1,
    "email": "webcyou@example.com",
    "provider": "email",
    "uid": "webcyou@example.com",
    "name": "daisuke.takayama",
    "nickname": null,
    "image": null,
    "team": "ウェブ帳"
  }
}
パスワードの変更

続いて、パスワードの変更。

パスワードの変更は、 「localhost:3000/auth/password」 に PUT リクエストとなります。

いよいよなんですが、ユーザー情報や、パスワードの変更などを行う際、 認証情報が必要となってきます。

その為に必要なのは、以下の通りとなっております。

REQUEST HEADERSの

・ access-token

・ client

・ uid

の、3つとなります。

先程、ログインした際に、HEADERSに付与されている情報が必要となってきますので、そちらを利用していきます。

Restlet Client – REST API Testing」では、以下の画像の様に、REQUESTの部分で設定できますので、コピペを行って入力しましょう。

入力したら、パスワード変更のデータを PUTしてみましょう。

sendデータ 例:

{
  "password": "password2",
  "password_confirmation": "password2"
}

レスポンス

{
  "success": true,
  "data":{
    "id": 1,
    "provider": "email",
    "email": "webcyou@example.com",
    "uid": "webcyou@example.com",
    "name": "daisuke.takayama",
    "nickname": null,
    "image": null,
    "team": "ウェブ帳",
    "created_at": "2017-05-20T07:41:15.000Z",
    "updated_at": "2017-05-20T08:11:19.000Z"
  },
  "message": "Your password has been successfully updated."
}

パスワード変更が出来ました。

ユーザー情報の変更

最後に、ユーザー情報の変更をおこなっていきます。

ユーザー情報の変更は、 「localhost:3000/auth/」 に PUT リクエストとなります。

先程の

・ access-token

・ client

・ uid

の情報を「REQUEST HEADERS」に付与し、 「localhost:3000/auth/」 に PUT リクエストを送ります。

sendデータ 例:

{
  "team": "webcyou"
}

レスポンス

{
"status": "success",
  "data":{
    "id": 1,
    "team": "webcyou",
    "email": "webcyou@example.com",
    "provider": "email",
    "uid": "webcyou@example.com",
    "name": "daisuke.takayama",
    "nickname": null,
    "image": null,
    "created_at": "2017-05-20T07:41:15.000Z",
    "updated_at": "2017-05-20T08:16:48.000Z"
  }
}

所属する「チーム」の変更を行ってみました。

レスポンスデータで変更に成功したのが確認できました。

と、
今回、ザッとではありましたが、Tokenベースの認証を容易に実装できる Gem 「devise token auth」のご紹介でした。

今回のソースに関しては、githubの方にも追加しております。

github – devise_token_auth_demo

こちらを使って、Tokenベースの認証に切り替え、SPAサイトなど作ってみると良いかもしれませんね。

ではではぁ。

  • RSSを登録する

  • follow us in feedly

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、港区六本木で活動中。

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。