【Salesforce】(Tips) APEXからCookieを使う/プライベートメソッドのテスト/OAuth2処理まわりの話題

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは植木和樹@上越妙高オフィスです。MFクラウド請求書のAPIを呼ぶためにはOAuth 2.0のアクセストークンが必要になります。

上記のマニュアルを参考にCallBack URLスコープを設定し、発行されたClient ID, Client SecretをSalesforceに保存して利用します。

ただCallback URLを指定するにあたり「はて、これはどのURLを指定してあげればいいんだろう?」と悩んでしまいました。

今回はVisualforce/APEXを用いたOAuth 2.0のトークン取得処理と、そのトークンを用いたAPI利用のフローについて紹介したいと思います。

OAuth 2.0 の認証フローって?

OAuth 2.0 の認可のフローについては下記のページがとても参考になります。このページの「OAuth 2.0 の認証フロー」図をご覧ください。

今回はこの図でいうFacebook (Client) がSalesforceにあたり、Twitter (Service Provider)がMFクラウド請求書になります。この図を今回のケースで置き換えると以下のような流れになります。なおフローを簡単にするため事前にMFクラウド請求書へはログイン済みとします。

  1. ユーザーがSalesforceでPDF表示ページにアクセスする。
  2. Salesforce(APEX)でMFクラウド請求書の認証ページにユーザーをリダイレクトする。(https://invoice.moneyforward.com/oauth/authorize)
  3. (すでにログインしているので)MFクラウド請求書がユーザーをCallback URLに認証コード付きでリダイレクトさせる。
  4. Salesforce(APEX)から認証コードを用いてアクセストークンページにアクセスする。(https://invoice.moneyforward.com/oauth/token)
  5. Salesforce(APEX)はアクセストークンを得たら(カスタムオブジェクトに保存し)、トークンを用いてPDFファイル取得APIをコールする。
  6. Salesforce(APEX)はPDFを取得したらVisualforceページで表示する。

上記のフローを見ると「アクセストークンを取得する処理」と「PDFファイルを取得する処理」が混在しています(1〜5と5〜6)。この2つの処理を同じVisualforce/APEXで行うのは機能詰めすぎ感がありイケてないので、下図のようなフローに分割することにしました。

実際の認証フロー

下記がOAuth 2.0認可フローと実際の処理(ページ遷移)を対応づけた図になります。黄色いブロックがブラウザがリダイレクトされる先になります。

20161229_sf_mfDisplayPDF4

  1. /apex/mfDisplayPDF (token確認)
  2. /apex/mfOAuth (認証コードなし)
  3. https://invoice.moneyforward.com/oauth/authorize (認証コードをつけてCallback)
  4. /apex/mfOAuth?code=xxxxxxxxxx (認証コードあり)
  5. /apex/mfDisplayPDF (再度token確認してPDF表示)

と遷移します。なおMFクラウドからアクセストークンを取得するURL https://invoice.moneyforward.com/oauth/token はAPEX内部でHTTP Callが行われるためブラウザは遷移しません。

コードと解説

ポイント1: PDF表示前のトークンチェック

mfDisplayPDFの <apex:page action="{!valid_oauth}" でアクセストークンの有無を確認しています。MoneyForwardTokenControllerのvalid_oauthでは保存されたトークンの取り出しと、MFクラウドへのアクセスを試みることで正しいトークンかどうかのチェックを行っています。

ただ今回トークンチェックを行うのはPDF表示ページだけなのでこういうやり方をしていますが、複数のページでトークンを利用する場合にはもうちょっと汎用的に仕組みにした方がいいと思います。TomcatのValveやJava ServletのFilterみたいな処理ができるといいのですが、Salesforceにそういう機能があるのか見つけられませんでした。

ポイント2: Cookieを用いた戻り先URLの保存

トークンがなかった場合には Page.mfOAuth ページへリダイレクトさせます。mfOAuthでトークンを取得できたら、再度このページに戻ってきてほしいのでCookieに戻り先URLを保存しています。(MoneyForwardDisplayPDFController の valid_oauth)

またmfOAuthではトークンが取得できたらCookieから戻り先URLを取り出して、そのページへリダイレクトさせています。(MoneyForwardTokenController の guessRetrunUrl メソッド)

ポイント3: 認証コード有無によるフロー分岐

mfOAuth(MoneyForwardTokenController)は2つの役割を持っています

  • トークンがなかった場合の遷移先
  • MFクラウド請求書から認証コードを受け取るためのCallback URL

今回はURLパラメータにcodeがあるかどうかでフローを分岐させています。(MoneyForwardTokenController の oauth メソッド)

ポイント4: Privateメソッドのテスト

Salesforceでは本番環境にコードをリリースする前に、必ずテストが実行されます。コードのカバレッジが一定の基準(75%)を満たしていないとリリースが許可されません。そのためなるべく細かい単位でテストコードを用意することになり、プライベートメソッドも直接テストしたくなります。

プライベートメソッドのユニットテストというとJavaのReflectionを思い出して心折れそうになったのですが、APEXではテスト対象のメソッドに@TestVisibleというアノテーションをつけておけばテストコードからの呼出が可能になります。簡単ですね。

Salesforceでのテストに関するベストプラクティスは下記ページにまとまっているので読んでおくことをお勧めします。

JP:An Introduction to Apex Code Test Methods - developer.force.com

まとめ

OAuth 2.0についてはネットの解説記事を読んでなんとなく分かった感じしかしていませんでしたが、実際に自分でコードを書いてみることで理解が深まりました。 OAuth 2.0については下記のページを何度も読んでお世話になりました。

最後に、今回のコードを実装するにあたりOAuth 2.0理解度チェックしてもらった都元さんにこの場を借りてお礼いたします。今度ファミチキおごります。