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で動作を確認しています。