🦁

[Rails]SalesforceでOAuthのアクセストークンを取得してみる

2022/08/08に公開

業務で、SalesforceのOAuth認証機能をつけたので、
最低限の設定でひとまずアクセストークンを取得できるところまでをやっていきます。

OAuthの認証フローは、Salesforceでは複数パターン用意されています。
https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_flows.htm&type=5

今回はよく見かける(?)、Salesforceの画面へ遷移してそこでID,Passwordでログインして権限を与える許可をするフロー(Web サーバフロー)で試してみます。

Salesforceアカウントを取得

  • クライアントアプリ側のアカウント
  • リソース側のアカウント
    と二つ作成しておきます。

Salesforceは無料トライアルはあるものの、基本有料となっています。
https://www.salesforce.com/jp/editions-pricing/sales-cloud/

しかし、Developer Editionという開発向けのプランがありこちらは無料で使えます。
なのでこちらを使っていきます。

アカウント設定

クライアントアプリ側として使うアカウントでOAuthの設定を行います。

https://developer.salesforce.com/docs/atlas.ja-jp.salesforce1api.meta/salesforce1api/intro_defining_remote_access_applications.htm

左メニューのアプリケーション->アプリケーションマネージャで、右上の新規接続アプリケーションへ遷移します。

必須情報を入れて、OAuthの有効化にチェックを入れます。
https://help.salesforce.com/s/articleView?id=sf.connected_app_create_api_integration.htm&type=5

OAuth範囲は今回はフルアクセスとします。
実際に業務で使うときは必要最低限の範囲を設定するのがいいかと思います。

OAuth認証実装

認証URLは以下のようになります。
https://developer.salesforce.com/docs/atlas.ja-jp.salesforce1api.meta/salesforce1api/intro_understanding_web_server_oauth_flow.htm
https://login.salesforce.com/services/oauth2/authorize
パラメータとして必須なのは

  • response_type
  • client_id
  • redirect_uri
    の3つです。
routes.rb
namespace :auth do
  resource :salesforce, only: [:create], controller: 'salesforce' do
    collection do
      get "callback"
    end
  end
end
app/controllers/auth/salesforce_controller.rb
class Auth::SalesforceController < ApplicationController
  def create
    uri = URI("https://login.salesforce.com/services/oauth2/authorize")

    # salesforce側でコールバックURLに127.0.0.1が使えないのでlocalhostを使っている
    redirect_uri = "http://localhost#{callback_auth_salesforce_path}"
    uri.query = {
      response_type: "code",
      client_id: ENV["SALESFORCE_CLIENT_ID"],
      redirect_uri: redirect_uri
    }.to_query
    redirect_to(uri.to_s)
  end

  def callback

  end
end

client_idredirect_uriには以下をセットします。

  • client_id
    • 「コンシューマーの詳細を管理」へ遷移した先のコンシューマ鍵
  • redirect_uri
    • コールバックURL

これで、http://127.0.0.1:3003/auth/salesforceへPOSTでリクエストするとsalesforceへリダイレクトします。

ここでリソース側のアカウントでログインしてみます。
その後アクセスを許可すると認可コードとともに
http://localhost:3003/auth/salesforce/callback?code=hogehogeにリダイレクトされます。

認可コードからアクセストークンを取得します。
https://developer.salesforce.com/docs/atlas.ja-jp.salesforce1api.meta/salesforce1api/intro_understanding_web_server_oauth_flow.htm
の4番に書いてある通りです。
トークン要求エンドポイントにhttps://login.salesforce.com/services/oauth2/token POSTします。
その時の必須パラメータは以下です。

  • grant_type
    • このフローの値は authorization_code である必要があります。
  • client_id
    • 接続アプリケーション定義の [コンシューマ鍵]。
  • client_secret
    • 接続アプリケーション定義の [コンシューマの秘密]。
  • redirect_uri
    • 接続アプリケーション定義の [コールバック URL]。
  • code
    • コンシューマがアクセストークンと更新トークンを取得するために使用する必要がある認証コード。
app/controllers/auth/salesforce_controller.rb
def callback
  code = params[:code]
  salesforce_oauth = SalesforceOauth.retrieve_access_token(code)
end
app/models/salesforce_oauth.rb
class SalesforceOauth
  attr_reader :access_token

  ACCESS_TOKEN_REQUEST_ENDPONT = "https://login.salesforce.com/services/oauth2/token"

  class << self
    def retrieve_access_token(code)
      uri = URI(ACCESS_TOKEN_REQUEST_ENDPONT)
      conn = Faraday.new(
        headers: {
          "Content-Type" => "application/x-www-form-urlencoded",
        },
      )
      params = {
        grant_type: "authorization_code",
        client_id: client_id,
        client_secret: client_secret,
        redirect_uri: redirect_uri,
        code: code,
      }

      response = conn.post(uri) do |req|
        req.body = params.to_query
      end

      if response.status != 200
        # TODO: エラー処理
        Rails.logger.error(response.body)
        raise response.body
      end

      parsed_response_body = JSON.parse(response.body)

      new(parsed_response_body["access_token"])
    end

    def client_id
      ENV["SALESFORCE_CLIENT_ID"]
    end

    def client_secret
      ENV["SALESFORCE_CLIENT_SECRET"]
    end

    def redirect_uri
      # salesforce側でコールバックURLに127.0.0.1が使えないのでlocalhostを使っている
      "http://localhost#{Rails.application.routes.url_helpers.callback_auth_salesforce_path}"
    end
  end

  def initialize(access_token)
    @access_token = access_token
  end
end

これでアクセストークンが取得できました。

アクセストークンで情報が取得できるか試してみる

試しに、取得したアクセストークンを使って何かデータを取得してみます。

以下のユーザー情報を取得してみます。
https://help.salesforce.com/s/articleView?id=remoteaccess_using_userinfo_endpoint.htm&type=5

sub, user_id, name ,emailなどなどユーザー情報が取得できるようです。
今回はgiven_nameだけ取ってみます。

app/controllers/auth/salesforce_controller.rb
def callback
  code = params[:code]
 salesforce_oauth = SalesforceOauth.retrieve_access_token(code)

  salesforce_user_info = SalesforceUserInfo.retrieve(salesforce_oauth.access_token)
  given_name = salesforce_user_info.given_name
  
  render plain: "given_name: #{given_name}"
end
app/models/salesforce_user_info.rb
class SalesforceUserInfo
  attr_reader :given_name

  ENDPOINT = "https://login.salesforce.com/services/oauth2/userinfo"

  class << self
    def retrieve(access_token)
      uri = URI(ENDPOINT)
      conn = Faraday.new
      params = {
        access_token: access_token,
      }
      response = conn.get(uri, params)

      if response.status != 200
        # TODO: エラー
        raise
      end

      parsed_response_body = JSON.parse(response.body)

      new(
        given_name: parsed_response_body["given_name"]
      )
    end
  end

  def initialize(given_name:)
    @given_name = given_name
  end
end

ブラウザでgiven_nameが表示されます。
これでアクセストークンを使って情報が取得できたことが確認できました。

記事中のコードは多少リファクタリングして以下に残しています。
https://github.com/hid3h/rails-iroiro-sample/pull/3

アクセストークンの期限

実際に業務で使う場合には期限なども気にする必要が出てくるので注意です。

アクセストークンの有効期間は認証コードよりも長く、通常は数分~数時間です。期限が切れたアクセストークンの使用を試みると失敗します。その場合、クライアントは更新トークンを使用するか、認証フローを再開して新しいアクセストークンを取得する必要があります。
https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_tokens_scopes.htm&type=5

Discussion