OAuth 2.0 User-Agent Flowを使ってサーバレスでSalesforceにアクセスする

もはや自分はブログほとんど更新しておらず、最近はゆるいことをTwitterでのみやってる感が強いですが、ネタ的にこれは自分が書いとくべきとおもったネタに関してはこのブログで書いておくことにしています。今回もそれです。


Salesforce の Winter '11 で、OAuth 2.0へ対応(Draft 10相当)されたわけだけど(OAuth 1.0a への対応はすでに去年の秋にしている)、この中にはUser-Agent Flowという方式への対応もなされている。これはJavaScriptFlashなどのWebページ埋め込みのスクリプトからOAuth認証を受けるためのフロー。

つまり、これを利用すると、Facebook ConnectTwitter @Anywhereなどと同じように、任意のWebページからSalesforceAPIに対して直接、サーバの介在なしで接続できるようになる。

SalesforceのOAuth 2.0 User-Agent Flowへの対応についてはこちら

OAuth2 利用のフロー

コンシューマ登録(リモートアクセスの作成)

SalesforceにおけるOAuthのコンシューマ登録は、管理設定画面の中から「アプリケーションの設定」>「開発」>「リモートアクセス」のメニューより新規に登録できる。

トークンの取得(リクエスト)

次に、HTMLページを作成し、前のステップで作成したコールバックのHTMLファイルと同じドメインに配置する。この中でOAuthでの承認を行うアクションを記述する。

具体的には、Salesforceの提供するAuthorization Endpointに対して、事前のコンシューマ登録の際に生成されたConsumer Keyをclient_idとし、承認後にコールバックされるURIをredirect_uriとしてパラメータにくっつけた上でブラウザを同エンドポイントに対してリダイレクト(あるいはWindowをポップアップ)する。

URLは以下のような形になる。

https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=3MVG9lKcPoNINVBIPJjdw1J9LLJbP_pqwoJYyuisjQhr_LLurNDv7AgQvDTZwCoZuDZrXcPCmBv4o.8ds.5iE&redirect_uri=https%3A%2F%2Fwww.mysite.com%2Fuser_callback.jsp&state=mystate

リダイレクトされた画面上でSalesforceはログインおよび承認画面を表示し、アクセス許可してよいかをプロンプトする。

トークンの取得(レスポンス)

ユーザがアプリによるアクセスを承認した場合、コールバック先のURLに対してWebブラウザをリダイレクトする。その際、トークン値は以下のような形でFragment Identifier内に渡される。

https://www.mysite.com/user_callback.jsp#access_token=00Dx00000008mib%21AQIAQL34fBVczu13lJBiDAlX3Vqg9Fgna3ad8wvu_mUGXRyN5syhxyOOktu4wGx2gavlSXPC5Y4D594l1RLV1dCUDGqCAmNr&instance_url=https%3A%2F%2Fna1.salesforce.com&id=https%3A%2F%2Flogin.salesforce.com%2Fid%2F00Dx00000008mib%2F005x0000000I8It&issued_at=1286646354574&signature=HKMw6%2FV2Hem%2FXulGvvSm80J94tJ1DScsLAUar1T32cc%3D&state=mystate

これにより、WebページのリダイレクトだけでAccessTokenをGETできる。ここまではOAuth 2.0 User-Agent Flowと同じ(なんか微妙にちょっと違うかもしれんけどまあいいや)。

API呼び出し

いままでSalesforceAPIでは、API認証に独自のSessionIDという値を利用していた。そして以前のOAuth 1.0aの際は、AccessTokenからこのSessionIDに変換するリクエストを余分に行う必要があったが、今度からこの値にOAuthのAccessTokenを利用できるようになった。なので、以下のようにそのままSessionIDの値にセットすればよい。

sforce.connection.sessionId = accessToken;

これでセッションIDを利用して認証された状態でAPIリクエストを送ることができるわけだが、しかし通常Webページでは異なるドメインからリクエストを送ることができないため、いろいろ工夫しなければAPI Endpointにアクセスできない。

Flashの世界では、crossdomain.xmlというポリシーファイルによって、クロスドメインのアクセスをオプトインできるような仕組がある。ほぼ知られていないとおもうのだが、SalesforceSOAP API Endpointにはcrossdomain.xml が配置されている。場所はルートではなく https://xxx.salesforce.com/services/Soap/u/crossdomain.xml になるため、明示的にこのURLを指定してポリシーファイルをロードしなければいけない。

残念ながらまだXHR Level2には対応していないようなので、直接JavaScriptからXMLHttpRequestでリクエストを送るのは難しい。ということで、JavaScriptからはFlash経由でXHRをやるのがよいかもしれない。何らかのServer Proxyを用いてもいいが、サーバレスのポータビリティという利点は失われる。さらにAPIリクエストがクライアントIPから送られなくなるので、Salesforceの管理設定によって企業内ファイアウォールからのIP帯域にリクエストを制限をしている場合は使えない。後で示すサンプルコードはflXHRというライブラリを利用してFlash経由でクロスドメインリクエストを送信している。

Identity URL

ちなみに先のリダイレクトによるコールバックレスポンスの際に、idというパラメータにそのユーザのOpenIDのURLが渡されるようになっているのだが、そのURLはそのままREST形式のユーザ情報取得エンドポイントになっている。こちらはJSONPに対応しておりクロスドメインでリクエストができる。

Using Identity URLs

リクエスト方法としては、OpenIDのURLに対してOAuthのAccessTokenをパラメータとしてつけて以下のようにアクセスする。

https://login.salesforce.com/id/00Dx00000008mib/005x0000000I8It?oauth_token=00Dx0000000BV7z%21AR8AQBM8J_xr9kLqmZIRyQxZgLcM4HVi41aGtW0qW3JCzf5xdTGGGSoVim8FfJkZEqxbjaFbberKGk8v8AnYrvChG4qJbQo8&format=json&callback=callback


なお、もうしばらくすると現行のSOAP APIの他にRESTful APIが出てくるらしいく、このような形でおそらく何らかのクロスドメイン対応がされているのだろうと思われる。ということで今わざわざSOAP APIを使わなくてもいいわけだが、正直JavaScriptからSalesforce APIを利用するためにSalesforceが提供しているAJAX Toolkitが現段階でもなかなかよいWrapperになっているので、JavaScriptからアクセスする限りSOAPでもRESTでもそんなに変わらないだろう。

サンプルコード

最後に、せっかくなのだから、作ったコードは以下に公開しておく。HTMLページはSSLである必要があるのでS3に置いておいた。

https://stomita-lab.s3.amazonaws.com/sfdc-oauth2/hello.html


けれども、Salesforce OAuthのちょっとイケてないのは、誰かがこのコードを見てその場で実際に試そうとしても試すことができない、というところにある。

たとえば、上記のSalesforce OAuth2ログインのテストページは、もしあなたが何らかのSalesforceのアカウントを持っていたとしても、あなたの組織の管理者が事前に設定登録していない限り、OAuth認証フローが通らないようになっている。

SalesforceのOAuthの場合、どんなOAuthのコンシューマも事前にユーザ企業の管理者がそのコンシューマを登録設定しておかないと利用できない。これは一般的な開発者のコンシューマ登録とは別に行うことになる。開発者とユーザ企業がイコールの場合、つまり内製アプリケーションの場合はこれは問題ないが、外部サービスが複数のSalesforce導入企業にOAuth認証でサービスを行なおうとしたときには上記の設定をユーザ企業にお願いしなければならない。

設定登録といっても、具体的にはパッケージという設定の塊を組織内にコピーしてインストールするだけなので作業的には大した問題ではないのだけれど、管理者権限が必要というところで導入の敷居はちょっと高くなる。

このあたりはGoogle AppsのOAuthとかなり異なっている。Google Apps での 3-legged OAuthの場合、事前に管理者がそのコンシューマを受け入れていようがいまいが、ユーザが許可すればGDataでアクセスができる。まあEnterpriseなサービスとして見たとき、Appsの方がガバナンスを効かせられていない、という見方もできるので一概にSalesforceが劣っているというような批判をするつもりはない。また、Appsの方もいくつかのオプションがあるようなので、もしかしたらApps管理者は野良OAuthアプリのアクセスを許可しないようにできるのかもしれない。この点については判断を保留する。


とりあえず、上記のSalesforce OAuth2でログインしようと思った場合、こちらのパッケージを事前にSalesforce管理者にインストールしてもらわなければならない。

https://login.salesforce.com/?startURL=%2Fpackaging%2FinstallPackage.apexp%3Fp0%3D04tA0000000IMjA

ただ、このめんどくささによって、外部サイトにユーザパスワードを預ける悪習を取り払うことができずに、結局一部サービスはそのままに依ってしまうのではないかな、などと思う。

(追記:2011/03/04)
@metadaddy によると、Spring '11 によるアップデートで管理者パッケージインストールは要らなくなった模様。すばらしい。