Ruby on Rails アプリケーションから、OAuth 2.0 を使って、kintone API を利用する

著者名:Mamoru Fujinoki(Fuji Business International)

目次

はじめに

共通管理メニュー内の[外部連携]で、 OAuth クライアントの作成 ができるようになりました。
今回は、Ruby on Rails(以下、Rails)で作成した顧客お問い合わせサイトから入力したデータを、 OAuth 2.0 の承認を経て、kintone API でデータを追加するサンプルを紹介します。

事前に必要なもの

開発手順

  1. kintone アプリの作成
  2. OAuth クライアントの設定
  3. Rails クライアントアプリケーションの開発
  4. Heroku へのデプロイ
  5. 動作確認

1. kintone アプリの作成

kintone アプリストアより、「お客様の声管理」で検索し、表示されたアプリを追加します。

このとき作成した kintone アプリのアプリ ID をメモしてください。Rails アプリケーションの作成時に利用します。
アプリ ID は、kintone アプリを開いたときの URL で確認できます。
https://{subdomain}.cybozu.com/k/{アプリ ID}

次のテーブルを参考にフィールドコードを設定します。

フィールドの種類 フィールド名 フィールドコード
ドロップダウン 収集媒体 received_via
ドロップダウン カテゴリ category
ドロップダウン テナント名 tenant_name
文字列(複数行) ご意見内容 opinion

kintone アプリの設定は以上です。

2. OAuth Clientの設定

kintone アプリの cybozu.com 共通管理ページより、外部連携の設定画面にて、OAuth クライアントの追加をクリックします。
操作方法の詳細は、 OAuth クライアントを追加する を参照してください。

「クライアント名」と「リダイレクトエンドポイント」を入力します。

  • 「リダイレクトエンドポイント」は、サイトを Heroku へデプロイした際に決定します。
    今の段階では、次の値を仮設定しておき、のちほど変更します。
    仮のリダイレクトエンドポイント: https://localhost:3000/authorize(必ず https を指定してください)

「保存」すると、以下の情報が自動生成されます。
「クライアントID」と「クライアントシークレット」をメモしてください。Rails アプリケーションの作成時に利用します。

  • クライアント ID
  • クライアントシークレット
  • 認可エンドポイント
  • トークンエンドポイント

連携利用ユーザーの設定をクリックします。

API 利用を許可するユーザーを選択し、設定を保存します。

以上で OAuth クライアントの設定は終了です。

3. Rails クライアントアプリケーションの開発

Ruby on Rails でクライアントアプリケーションを開発していきます。

ステップ 1

ターミナルを開き、次のコマンドを実行して、Rails のアプリケーションを作成します。

1
rails new customer-feedback

これにより、customer-feedback というプロジェクト名で Rails のアプリケーションのひな型が生成されます。

以下のコマンドでローカルサーバーを起動し、動作するか確認します。
サーバーが起動したら、ブラウザーにて http://localhost:3000 を開き、以下のように表示されたら成功です。

1
2
cd customer-feedback 
rails server

注意事項
(Windows 環境)
SQLite3 で dlopen の関数が存在しないというエラーになった場合、以下コマンドを実施してください。

1
ridk exec pacman -S mingw-w64-x86_64-dlfcn
ステップ 2

今回は、OAuth2.0 を使うので、以下のように Gemfile に 2 つの gem を追加してください。
oauth2 は、OAuth リクエストを実行するために使用し、activerecord-session_store はセッションの情報をデータベースへ記録するために使用します。

1
2
gem 'oauth2'
gem 'activerecord-session_store'

ファイルを保存後、以下のコマンドを実行します。

1
bundle install

次に以下のコマンドを実行し、セッションの保存用のデータベースを作成します。

1
2
rails generate active_record:session_migration
rails db:migrate
ステップ 3

続いて、以下のコマンドで、「home」、「auth」と「feedbacks」というコントローラーを作成します。

1
2
3
rails generate controller home
rails generate controller auth
rails generate controller feedbacks
ステップ 4

次に「app」-「helpers」フォルダー内の「auth_helper.rb」を開き、以下を参考にコーディングします。

次の値は、利用環境に合わせて修正してください。

  • 9 行目 CLIENT_ID: 2. OAuth Clientの設定 で作成した OAuth クライアントのクライアント ID
  • 11 行目 CLIENT_SECRET: 2. OAuth Clientの設定 で作成した OAuth クライアントのクライアントシークレット
  • 13 行目 SITE:kintone 環境のドメイン
 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Rails x kintone OAuth client sample program
# Copyright (c) 201x Cybozu
#
# Licensed under the MIT License
# https://opensource.org/license/mit/

module AuthHelper
  # kintone側のOAuth設定のクライアントID
  CLIENT_ID = '{クライアントID}'
  # kintone側のOAuth設定のクライアントシークレット
  CLIENT_SECRET = '{クライアントシークレット}'
  # kintoneのURL
  SITE = 'https://{サブドメイン名}.cybozu.com'
  # 認可エンドポイント
  AUTHORIZE_URL = '/oauth2/authorization'
  # トークンエンドポイント
  TOKEN_URL = '/oauth2/token'

  # APIアクセスのスコープ設定 read/write権限
  SCOPES = ['k:app_record:read','k:app_record:write']

  # CSRF対策のランダムな値
  STATE = SecureRandom.alphanumeric

  # ログインURLの生成
  def get_login_url
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    login_url = client.auth_code.authorize_url(:redirect_uri => authorize_url, # Railsの認可ページへのルートパス
                                                :scope => SCOPES.join(' '),
                                                :state => STATE)
  end

  # アクセストークン取得のための認可コードを送信
  def get_token_from_code(auth_code)
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    token = client.auth_code.get_token(auth_code,
                                        :redirect_uri => authorize_url,
                                        :scope => SCOPES.join(' '))
  end

  # アクセストークンの取得
  def get_access_token
    # セッションから現在のアクセストークンハッシュを取得
    token_hash = session[:kintone_token]

    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    token = OAuth2::AccessToken.from_hash(client, token_hash)

    # アクセストークンが期限切れの場合、リフレッシュトークンからアクセストークンを取得
    if token.expired?
      new_token = token.refresh!
      # 新アクセストークンをセッションへ保存
      session[:kintone_token] = new_token.to_hash
      access_token = new_token
    else
      access_token = token
    end
  end
end
ステップ 5

今度は、「app」>「controllers」フォルダー内の「home_controller.rb」を以下のように編集します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Rails x kintone OAuth client sample program
# home_controller.rb
# Copyright (c) 201x Cybozu
#
# Licensed under the MIT License
# https://opensource.org/license/mit/

class HomeController < ApplicationController
  include AuthHelper

  def index
    # 承認リンクの表示
    @login_url = get_login_url
  end
end

また、同フォルダー内の「auth_controller.rb」を以下を参考にコードを追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Rails x kintone OAuth client sample program
# auth_controller.rb
# Copyright (c) 201x Cybozu
#
# Licensed under the MIT License
# https://opensource.org/license/mit/

class AuthController < ApplicationController
  include AuthHelper

  # 認可コードからアクセストークンを取得
  def gettoken
    token = get_token_from_code params[:code]
    session[:kintone_token] = token.to_hash
    redirect_to feedbacks_url
  end
end

最後に「feedbacks_controller.rb」を以下を参考に編集します。

次の値は、利用環境に合わせて修正してください。

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Rails x kintone OAuth client sample program
# feedback_controller.rb
# Copyright (c) 201x Cybozu
#
# Licensed under the MIT License
# https://opensource.org/license/mit/

class FeedbacksController < ApplicationController
  include AuthHelper

  # kintoenのアプリケーションID
  APP_ID = '{アプリケーションID}'
  DOMAIN = 'https://{サブドメイン名}.cybozu.com'

  def new
  # 初期ページロード状態では何もしない
  end

  def create
    @feedback = Feedback.new(feedback_params)
    if @feedback.save
      token = get_access_token

      if token
        # アクセストークンがすでに取得されている場合、データをkintoneへポスト

        # ハッシュ形式でレコードを設定
        #
        record = {
          "app" => APP_ID,
          "record" => {
            "received_via" => {
              "value" => feedback_params[:received_via]
            },
            "category" => {
              "value" => feedback_params[:category]
            },
            "tenant_name" => {
              "value" => feedback_params[:tenant_name]
            },
            "opinion" => {
              "value" => feedback_params[:opinion]
            }
          }
        }

        response = token.post("#{DOMAIN}/k/v1/record.json", {:body => record.to_json, :headers => {'Authorization' => "Bearer #{token.token}", 'Content-Type' => 'application/json'}})

        redirect_to feedbacks_url
      else
        # アクセストークンが存在しない場合、ホームページへ戻り、承認のやり直し
        #
        redirect_to root_url
      end
    else
      render 'new'
    end
  end

  private
    def feedback_params
      # フォームからデータを取得
      params.require(:feedback).permit(:received_via, :category, :tenant_name, :opinion)
    end
end
ステップ 6

「app」>「views」>「home」フォルダー内に「index.html.erb」ファイルを作成し、以下を参考にしてホームページを作成します。
「kinrtone へ接続」するためのリンクを設置しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!--
 * Rails x kintone OAuth client sample program
 * home_controller.rb
 * Copyright (c) 201x Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
-->

<h1>kintone OAuth 2.0 クライアントサンプル</h1>
  <p>
    <a href="<%= @login_url %>" role="button" id="connect-button">kintoneに接続</a>
</p>

次のようなページが生成されます。

次に、「app」>「views」>「feedbacks」フォルダー内に「new.html.erb」ファイルを作成し、以下を参考にして、お客様の声フォームを作成します。

 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
34
35
36
37
38
39
40
41
<!--
 * Rails x kintone OAuth client sample program
 * home_controller.rb
 * Copyright (c) 201x Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
-->

<h1>お客様の声</h1>
<%= form_for :feedback, url: feedbacks_path do |form| %>
  <p>
    <%= form.hidden_field :received_via, value: "WEBサイトから" %>
    <%= form.label :category, "カテゴリ:" %><br>
    <%= form.select :category,
      [['---','---'],
        ['お褒め','お褒め'],
        ['お叱り','お叱り'],
        ['ご要望','ご要望'],
        ['その他','その他'],] 
    %>
  </p>
  <p>
    <%= form.label :tenant_name, "テナント名:" %><br>
    <%= form.select :tenant_name,
      [['---',''],
        ['坊主食品','坊主食品'],
        ['サイボシューズ','サイボシューズ'],
        ['Cミュージックストア','Cミュージックストア'],
        ['施設','施設'],]
    %>
  </p>
  <p>
    <%= form.label :opinion, "ご意見内容:" %><br>
    <%= form.text_area :opinion, size: "50x6" %>
  </p>

  <p>
    <%= form.submit "送信" %>
  </p>
<% end %>

次のようなページが生成されます。

ステップ 7

次に「config」フォルダー内の「routes.rb」ファイルを以下のように編集します。

1
2
3
4
5
6
7
Rails.application.routes.draw do
  root 'home#index'
  get '/authorize' => 'auth#gettoken'
  get '/feedbacks', to: 'feedbacks#new'
  post '/feedbacks', to: 'feedbacks#create'
  resources :feedbacks
end
HTTP 動詞 パス フィールドコード 目的
GET /authorize auth#gettoken OAuth で kintone からのコールバック先
GET /feedbacks feedbacks#new お客様の声を1つ作成するための HTML フォームを返す
POST /feedbacks feedbacks#create お客様の声を1つ作成する
ステップ 8

次にさきほど指定した resource 名のモデルを生成します。

次のコマンドを実行し、「feedback」モデルを生成します。ここでは「feedbacks」ではなく、単数形の「feedback」を指定します。

1
rails generate model feedback received_via:string category:string tenant_name:string opinion:text

これにより以下のモデルが生成されます。

フィールド名 フィールドタイプ
received_via string
category string
tenant_name string
opinion text

以下のコマンドでマイグレーションを実行して、データベースにテーブルを作成します。

1
rails db:migrate

これで feedbacks テーブルがデータベース上に作成されます。

テスト環境での動作確認

以下のコマンドでローカルの Rails サーバーを起動し、エラーがないか確認します。

1
rails server

以下の画像のようにホームページが表示されれば、成功です。
ただし、「kintone に接続」リンクをクリックしても kintone 側の OAuth クライアントのコールバック URL の設定が異なるためエラーになります。
これは、Heroku へのデプロイが終了後、修正します。

以上で、Rails クライアントの開発は完了です。

4. Heroku へのデプロイ

今回は、テスト用のサーバーに Heroku を使用します。

Heroku では、データベースとして利用している SQLite3 はサポートされていません。
そのため、開発環境(ローカル)では SQLite3 を利用し、本番環境(Heroku)では PostgreSQL を利用するようにします。
Gemfile の SQLite3 に関する記述部分を以下のように書き換えます。

1
2
3
4
5
6
7
group :production do
  gem 'pg'
end

group :development, :test do
  gem 'sqlite3', '~> 1.4'
end

以下のコマンドで、変更をローカルの開発環境に一度反映させます。

1
bundle install --without production

以下のコマンドで、今までのプロジェクトファイルの変更を Git レポジトリに反映させます。

1
2
git add -A
git commit -am "Update Gemfile for Heroku "

以下のコマンドで、Heroku アカウントにログインして、SSH キーを追加します。

1
2
heroku login
heroku keys:add

以下のコマンドで Heroku のアプリケーションを作成します。

1
heroku create

Heroku 内に空のアプリケーションが作成されるので、次のコマンドで、作成した OAuth クライアントのアプリケーションを Heroku にデプロイします。

1
git push heroku master

次のコマンドで Heroku 上にデータベースを作成します。

1
heroku run rake db:migrate

以上で Heroku のデプロイは完了です。

5. 動作確認

デプロイが完了したら、以下のコマンドで、アプリケーションを開きます。

1
heroku open

以下の画像のように表示されれば成功です。
表示された Heroku のアプリケーションの URL をメモしてください。後述の OAuth クライアントの設定で利用します。

次に、kintone の OAuth クライアントの設定画面を開き、リダイレクトポイントの URL を Heroku のアプリケーションの URL に変更し、保存します。

「kintone OAuth 2.0 クライアントサンプル」フォームに戻り、「kintone に接続」リンクをクリックします。

kintone にログインしていない場合は、ログイン画面が表示されます。

「Ruby OAuth クライアントから次の操作が実行されます」というメッセージが表示されるので、「許可」します。

「お客様の声」フォームが表示されるので、各項目を任意に入力し「送信」ボタンをクリックします。

エラー画面が表示されず、フォームの各項目が未入力の状態にリセットされれば、成功です。

kintone アプリにレコードが追加されているか確認します。

OAuth2.0 を使って、Ruby on Rails の外部アプリから kintone API を使ってレコードの追加に成功したことを確認できました。

コードの解説

auth_helper.rb

auth_helper.rb は、認証に関する処理をまとめた helper ファイルです。

2. OAuth Clientの設定 で作成した OAuth クライアントアプリの値を元に各変数を設定します。
なお、設定したスコープの詳細は、 kintone の OAuth スコープ を参考にしてください。今回は、「read」、「write」権限を設定しています。

 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  # kintone側のOAuth設定のクライアントID
  CLIENT_ID = '{クライアントID}'
  # kintone側のOAuth設定のクライアントシークレット
  CLIENT_SECRET = '{クライアントシークレット}'
  # kintoneのURL
  SITE = 'https://{サブドメイン名}.cybozu.com'
  # 認可エンドポイント
  AUTHORIZE_URL = '/oauth2/authorization'
  # トークンエンドポイント
  TOKEN_URL = '/oauth2/token'

  # APIアクセスのスコープ設定 read/write権限
  SCOPES = ['k:app_record:read','k:app_record:write']

  # CSRF対策のランダムな値
  STATE = SecureRandom.alphanumeric

この関数では、ホームページの「承認」リンクに設定する URL を生成しています。
なお、authorize_url は、routes.rb で設定する authorize ページへのルートパスです。このリンクをクリックすると、kintone 側から、認可コードが送信されます。

oauth2 のプラグインのコマンドの詳細は、 A Ruby wrapper for the OAuth 2.0 protocol (External link) を参照してください。

25
26
27
28
29
30
31
32
33
34
35
36
  # ログインURLの生成
  def get_login_url
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    login_url = client.auth_code.authorize_url(:redirect_uri => authorize_url, # Railsの認可ページへのルートパス
                                                :scope => SCOPES.join(' '),
                                                :state => STATE)
  end

こちらの関数では、上記で取得した認可コードからアクセストークンを取得しています。

38
39
40
41
42
43
44
45
46
47
48
49
  # アクセストークン取得のための認可コードを送信
  def get_token_from_code(auth_code)
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    token = client.auth_code.get_token(auth_code,
                                        :redirect_uri => authorize_url,
                                        :scope => SCOPES.join(' '))
  end

この関数では、セッションに保存してあるアクセストークンのハッシュから、アクセストークンを取得します。
すでにアクセストークンが期限切れの場合にリフレッシュトークンより、新アクセストークンを取得します。

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  # アクセストークンの取得
  def get_access_token
    # セッションから現在のアクセストークンハッシュを取得
    token_hash = session[:kintone_token]

    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => SITE,
                                :authorize_url => AUTHORIZE_URL,
                                :token_url => TOKEN_URL)

    token = OAuth2::AccessToken.from_hash(client, token_hash)

    # アクセストークンが期限切れの場合、リフレッシュトークンからアクセストークンを取得
    if token.expired?
      new_token = token.refresh!
      # 新アクセストークンをセッションへ保存
      session[:kintone_token] = new_token.to_hash
      access_token = new_token
    else
      access_token = token
    end
  end

home_controller.rb

承認リンクへの URL を取得して、ホームページにリンクを表示します。

11
12
13
14
15
  def index
    # 承認リンクの表示
    @login_url = get_login_url
  end
end

auth_controller.rb

kintone 側でコールバックされた後、返された認可コードから、アクセストークンを取得します。

12
13
14
15
16
  def gettoken
    token = get_token_from_code params[:code]
    session[:kintone_token] = token.to_hash
    redirect_to feedbacks_url
  end

feedback_controller.rb

いったん、お客様フォームのデータをサイト内のデータベースに保存し、アクセストークンがすでに存在するかチェックします。

21
22
23
24
25
    if @feedback.save
      token = get_access_token

      if token
        # アクセストークンがすでに取得されている場合、データをkintoneへポスト

kintone に送信するデータをハッシュ形式で設定します。

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
        # ハッシュ形式でレコードを設定
        #
        record = {
          "app" => APP_ID,
          "record" => {
            "received_via" => {
              "value" => feedback_params[:received_via]
            },
            "category" => {
              "value" => feedback_params[:category]
            },
            "tenant_name" => {
              "value" => feedback_params[:tenant_name]
            },
            "opinion" => {
              "value" => feedback_params[:opinion]
            }
          }
        }

取得したアクセストークンで、kintone へデータをポストします。

47
        response = token.post("#{DOMAIN}/k/v1/record.json", {:body => record.to_json, :headers => {'Authorization' => "Bearer #{token.token}", 'Content-Type' => 'application/json'}})

アクセストークンが存在しない場合、ホームページの承認リンクへリダイレクトします。

51
52
53
        # アクセストークンが存在しない場合、ホームページへ戻り、承認のやり直し
        #
        redirect_to root_url

お客様の声フォームより、各データを取得します。

60
61
62
63
64
  private
    def feedback_params
      # フォームからデータを取得
      params.require(:feedback).permit(:received_via, :category, :tenant_name, :opinion)
    end

routes.rb

routes.rb は、Rails アプリケーションのルーティング設定するファイルです。

リソース名に「feedbacks」を指定します。

6
  resources :feedbacks

また、ホームページには、承認リンクを表示するページを設定します。

2
  root 'home#index'

おわりに

今回は Ruby on Rails によって、OAuth2.0 を使った外部連携をしました。
これを応用すれば、他の言語で開発した Web アプリケーションから OAuth2.0 を使った kintone API の利用も可能です。
OAuth2.0 による外部連携をすることで、ユーザ毎に kintone API へのアクセス権限をコントロールできます。

参照サイト

information

この Tips は、2020 年 3 月版 kintone で動作を確認しています。