スマフォのリワード広告におけるUDIDに依存しない設計案

前回のまとめ

前エントリで触れたとおり、スマートフォンのUDIDの問題点というのは、自分の把握する限り3つあった。

1. サーバとのセッションを管理するための認証に使われてしまっている
2. アプリケーションをまたがっての行動トラッキングに使われてしまっている
3. スマフォにおけるリワード広告がUDIDが取れることに依存した実装になっている。

このうち、1. に関しては代替案があり、ほぼ既存のユーザビリティを損なわない形に実装できるものなので、単に実装側の怠慢か知識不足に帰結されると感じている。今の時点でこれほど安易に使われてしまっているのは残念なことではあるかもしれないが、今後改善できる余地がある。

2. に関しては、実はデバイス固有IDであることについてはそれほどこの時点では問題ではなく、単にトラッキングされることに対してユーザの同意も拒否も介在する余地が無い、というところを問題にしている。たとえば、もしAdNetwork固有のIDを生成して割り振れたとしても(実際にはそれは無理なのだが)ユーザがそのトラッキングについて受入の許認可ができない時点でアウトと宣告される。つまりは、3rd Party Cookie的なオプトイン・アウトの仕組みが提供され無い限り、そもそも無理筋のビジネスモデルではないか、というのが自分の感想だ。

で、問題は 3. だ。

スマフォのリワード広告はevilなのか?

リワード広告について、ビジネスモデルの健全性を疑う声もあるようだが、僕はそこは今回とは分けて考えるべきである、という意見である。少なくとも個人的には、アコギなことをしている、という印象までは抱かない。もっと言うと、そのシステムがはたして世界にどういう結末をもたらすのか、という点で、第三者的な興味がある。もし、広告ビジネス自体を卑下して、そんなものはだめだ、という立場を取る人がいたとしたら、あさはかだな、とは思う。

ビジネス自体をやめろ、というのは簡単だ。もし無断かつ回避できない行動追跡のようにevilであるというコンセンサスが確立している場合には、正直そういうしかない。でも、リワード広告については、そこまでevilとは思わなかった。これは、先日の調査の結果、いままでの自身の価値観に照らし合わせて判断したもののため、いやそうじゃないよ、という意見もあるかもしれない。もしそうなら是非その点についてコメントなりで教えていただきたい。

ただし、ビジネス自体はありとしても、あたりまえながら実装においてはルールが存在し、 UDIDの収集によってそれを実現するのは、ルール違反といえる。また、起こりうるプライバシー問題についての想像力がない、という意味で、センスがない。

しかしながら、世の中はルール以外のところで動いているようだ。このままだとUDIDがとれなくなるんだったらMACアドレス(のハッシュ値)でやっちゃえばいいじゃん、という流れになるかもしれない。これは避けるべきところだ。

スマフォにおけるリワード広告のフロー(推定)

今回、正直に告白すると、自分はiOSネイティブのまともなアプリを作ったことは全くない。なので、実際とかけ離れた推測になっていた場合はご容赦いただきたい。

スマフォアプリのリワード広告で、肝となるのは、ユーザが該当アプリをダウンロードしたかどうかをどうやって把握するのか、ということだ。これさえできるのであれば、別にUDIDというのは必ずしも必要ではない。

今、リワード広告を掲載するアプリをA(ソーシャルゲームのようなものを想定すると良い)、広告主のアプリをBとしてみる。おそらく現在は以下のフローになっている。これはもちろん推測。

1) ユーザは、アプリAのゲームを楽しんでいる。
2) ユーザは、アプリA内のゲーム内での仮想通貨ポイントが欲しくなった。
3) ユーザは、アプリA内に表示されている広告をクリックする。
4) アプリAは、ユーザのスマフォのUDIDを取得し、サーバ上でユーザ情報に紐付けた後、App Storeを起動してアプリBのページへ遷移させる。
5) ユーザは、アプリBをスマフォにダウンロードする。
6) アプリBは、初回起動時にスマフォのUDIDを取得し、ゲームAのサーバに送信する
7) アプリAは、サーバ側でUDIDを照らし合わせて、そのユーザに対してポイントを付与する
8) ユーザは、アプリAにおいて、仮想通貨ポイントをゲットできた!

ここで、アプリAとアプリBで、ユーザのセッションを突き合わせるという目的のためにUDIDが使われているのがわかる。つまり、アプリ間でセッションの連携さえ出来るのならばこれは解決される。

スマフォアプリ間でセッションを共有する方法

今回、iPhoneアプリ間で連携する方法には、一般的に以下の方法がある、ということを教えいていただいた。

iOSの中で、アプリケーション同士が連携するためのしくみ - More the iPhone Development Playground

URLによるアプリの起動
Pasteboardによるデータ共有
UIDocumentInteractionControllerによるファイルの受渡し

このうち、URLによるアプリ起動によって解決できる可能性があるのではないかと考え、以下のようなフローを考えてみた。(4以降が変更点)

1) ユーザは、アプリAのゲームを楽しんでいる。
2) ユーザは、アプリA内のゲーム内での仮想通貨ポイントが欲しくなった。
3) ユーザは、アプリA内に表示されている広告をクリックする。
4) アプリAは、アプリA内で一時的にUUIDを生成し、ユーザ情報に紐付けた上で、ユーザをApp StoreのアプリBのページへ遷移させる。
5) ユーザは、アプリBをスマフォにダウンロードする。
6) アプリAは、4)で生成したUUIDをURLにパラメータとしてつけて、アプリBを起動する。
7) アプリBは、パラメータでもらったUUIDをアプリAのサーバへ送信する
8) アプリAは、サーバ側でUUIDを照らし合わせて、そのユーザに対してポイントを付与する
9) ユーザは、アプリAにおいて、仮想通貨ポイントをゲットできた!

こちらのフローだと、一旦6)でアプリAからアプリBを明示的に起動する、というフローが発生してしまうが、目的は達成されている。受け渡しされるのはアプリケーションの橋渡しをするための一時的なIDであるため、漏えいしても悪用されることはない。

UDIDを用いたフローが、即座にポイントが獲得できるのに対し、この提案はアプリケーションの確認となる 6)の ユーザインタラクションが入ってしまうため「ユーザビリティが損なわれる!」と憤慨するかもしれない。実際、ターゲットユーザ層を考えると、そのあたりのわかりにくさによる離脱は十分考えられるため、そんな要素は極力無くしたいというのが、ビジネスを企画する側の本音だろう。

ただ、申し訳ないのだが、今回の件でUDIDなど端末固有IDを利用することによるビジネス上のリスクというのは十分にわかったはずだ。デバイスIDを広く収集させてしまうことについての忌避感は、まともなベンダーならかならず感じていることだ。iOSAppleがやったことは、世間的に見てむしろ常識的な対応なのだ。さてじゃあAndroidではそんなことはないと誰がいっているか? そのようなリスクをとってまで最初のフローに固執するのはなぜだ? もしスマフォアプリのリワード広告というビジネスを継続させたいなら、今のところこれしかありえない、というのが自分の結論になる。

もちろん、まだ他の実装方法もあるかもしれない。その中には、ユーザインタラクションを損なわない妙案もあるかもしれない。残念ながら自分には見つけられなかったが、まあそこまで頑張る義理というのもない。

#ちなみにもしかしたらAndroidだったらIntentによってユーザのインタラクションを介さずにバックグラウンドでパラメータ渡しができるのかもしれない。そうすれば既存のユーザビリティを著しく損なうということはない。

なぜiOSでUDIDが必要とされていたのか、メモ

iOSやその開発事情に詳しいと言える状態にはないので、調査を兼ねて書く。

Apple Sneaks A Big Change Into iOS 5: Phasing Out Developer Access To The UDID – TechCrunch
http://wirelesswire.jp/Watching_World/201108221335.html

上記の「iOSでUDIDの利用が禁止」というニュースを聞いた時、正直TL上にこんなにいっぱい反応が貼り出されるとは思っていなかった。さすがにUDIDをいじるのはまずいよね、っていうコンセンサスは開発者の間では常識的部類に入ってくるのだろうと楽観的に捉えていたのかもしれない。

以下、なぜUDIDがそのようにスマートフォン開発者に利用されてきたのかについて、調べた限りでまとめてみた。

アプリケーションのサーバとのセッション保持

いわゆるかんたんログインの実現のために利用する、というもの。
たぶんほとんどのUDID批判者はこの点について最初に批判していたと思われる。

UDIDがこのような用途に向かないことは、すぐに分かる。

iOSの開発トレーニングでUDIDを利用していたことが伺えるツイート。

ということで、UDIDの利用は半ば常識化していたことが読み取れる。

ただし、これには以下のツイートもあったので、そもそもAppleが勧めてるようにみえたんじゃないのどうなの?という疑問もちょっとは残る。


じゃあ、かんたんログインできないし、ユーザ登録してもらわなきゃいけなくて、スマフォの画面でそれやらせる?むりだろ、という話になってしまうかもしれないが、そもそもそんなデバイス固有のIDを使う理由がない。

アプリ内でのサーバとのセッション管理に関して、UDIDを用いずに利用する方法について

これでいい。UUIDの生成さえセキュアであれば永続化Cookieによるセッション管理と同等だ。

つまり、ガラケーIDの時と違い(当時はCookieサポートしていない端末が数多くあった)、回避策はすぐあるわけだ。ログインさせるのが嫌だ、という理由にはならない。トレーニングという点ではUDIDを使った方が簡単に見えるかもしれんけど、非セキュアな方法をあたりまえのものとしてトレーニングしてよいわけがない。


なお、上記の方法ではKeychainを使っており、Keychainはアプリ間では情報の共有ができない(正確には同一のプロビジョニングIDをもっていればできるが、他社のアプリの情報を読めたりはしない)。つまり、アプリをまたがってセッションを共有したいという用途には用いることはできない。

行動トラッキングによるターゲティング広告

でもアプリをまたがってセッションを特定したいってのはどういう時なんだ?

最初に思いつくのは、行動トラッキングによるターゲティング広告になる。

現在スマートフォンのアプリ向けにいろいろベンチャー・大手含めてアドネットワークが参入している。TLの反応を見ても、このへんの商売への影響を心配している声が強かったようだ。

一見、3rd Party Cookieによるトラッキングの話と似ているじゃないのか、と思う。しかしながら、通常ブラウザは3rd party Cookieの受け入れはオプトアウトできるようになっている。だがUDIDの場合はそれができない。アプリケーションレベルで開発者の倫理により同意ダイアログを出すことは可能だろうけど、アプリに入り込んでいるターゲット広告がそれを出すわけがない。だから、この目的(行動トラッキング)によるUDIDの利用の正当性にはプライバシー保護上明確にNOと言われる可能性が高いし、歴史上そのような試みはほとんど糾弾されて来ている。スマフォは例外で特別であるという理由が存在しない。

つまりこのような行動トラッキングによる広告は、そもそもビジネスモデル自体が危うい。それが今回、Appleの(とりあえずは健全な)判断により現れたとすれば、そいつはまあ自己責任でないかといえる。

なおこの記事には正直びっくりした。いやこれくらい想定してないといかんのだろうが。

/ WSJ日本版 - jp.WSJ.com - Wsj.com

携帯アプリへと事業を拡大しつつある、エピック・メディア・グループ傘下のネット広告ネットワーク、トラフィックマーケットプレイスのメガン・オホレラン氏は、「携帯の素晴らしいところは、UDIDをクッキーのようには削除できないことだ。われわれは、UDIDを通じて、すべてを追跡する」と語る。

リワード広告

ただし、ことはそう単純でもないことが、調べているうちにわかってきた。
どちらかというと、影響があるのは行動トラッキングによる広告などではなく、違う広告分類のものらしい。

「リワード広告」というキーワードで調査した。

こちらはiPhone上でリワード広告を行っているTapjoy。

Has Tapjoy solved monetization of free apps on the iPhone and Android? – TechCrunch

They track the sale using the device owner’s Unique Device Identifier (UDID) so there is no confusion on whether the app is downloaded or not.

その他にも、スマートフォン上でリワード広告を提供している国内サービス。

http://www.gree.co.jp/news/press/2011/0516_03.html
http://www.venturenow.jp/news/2011/04/07/1711_010512.html

(訂正:こちらはスマフォ向けではなかった。が、エンジニア向けにリワード広告について分かりやすい図が書いてある)
リワード広告システムPoncanの仕組みと実例/ソーシャルアプリ勉強会(第2回)


リワード広告自体は一般的な広告提供の仕組みであると言えるけれども、これがスマートフォンの場合は、アプリのインストールに対して、アフィリエイトのような形で、ポイントなどを付与する。広告主はアプリのダウンロードが増えて満足、広告を掲載したアプリは広告料が入ってニコニコ、ユーザはポイントがもらえてラッキー、という仕組み。

この際に、ユーザにポイントを付与するためにアプリをダウンロードしたクライアントを特定する必要があるのだが、これにUDIDを用いている(少なくともTapjoyはそうしている、と書いてある)。

これを上記の行動トラッキングの場合と比較すると、ユーザのインタラクションが介在しているだけに、プライバシーの面からアドネットワークのプロバイダーの不誠実を訴えるのは難しいケースなのではないかと、個人的に感じた。このサービス自体がデバイスにアプリをインストールしたかどうかでポイントがもらえることを明確に宣言しており、ユーザはそれを認識しているのだから、それにまつわるデバイス情報を取得するのは当然だろ、という理論武装はそれなりにありえるからだ。

しかしながらそれにおいても、本来なら、ユーザとアプリの紐付けを行うのをUDIDというデバイス固有IDで行う必要はないはずだ。実際Webサイトのリワード広告はサイト間のセッションのやりとりだけで固有IDなどは用いていない。

しかしながら、どうもiOS上でアプリ間でスマートにセッションを共有する仕組みがなさそうなのである。
これについては、ただ単に自分の知識が足りないだけかもしれない。わからない。
これができない以上、スマフォアプリのリワード広告のシステムは作ることができない、ということになる。

じゃあUDID使っていいじゃん、っていう結論には、どう考えても落ちるはずはないのだけど、はてさて、だからといってこのようなビジネスモデル自体にダメだしするには、さすがに根拠がない。一番いいのはAppleがアプリ間でセッションを共有するための仕組みを整えてくれることだろうけど、それがiOS5およびその後可能になるのかどうかは、これまたわからない。

まとめ

なお、今回は、もともと興味を持っていた分野であったので、ちょっと時間を取ったけど、まだ浅い調査だけのメモ程度で、だからどうすべきか、までには至っていない。

ただし、今回わかったのは、ただ単にかんたんログインなどの技術者の知識レベルの話ではなく、すでにスマートフォンのアプリという成長市場で、一つのエコシステムができ上がりつつある中での廃止のニュースだったのが、各方面に波紋を巻き起こしているということだった。

リワード広告のエコシステム自体は、市場の活性化に必要なのは理解できるが、UDIDに依存した実装に関しては可能ならば早急に対策をしなければいけないだろう。で、Appleはそれを強制した。それは実はリワード広告というビジネス自体を潰したいという意図があるのかどうか知らない。またiAdがUDIDをトラッキングのために独占して利用するのか、そしてどれほど悪意を持ち得るのかもわからない。知らないわからないことだらけだったな、というのしかわからなかった、という、まとめにならないまとめ。

追記

Appleがリワード広告自体を締め出そうとしているのではないか?というのが伺える記事について、コメントしていただいた。
Appleが景品付きアプリダウンロードを締め付けへ | TechCrunch Japan

追記2

スマフォのリワード広告の実装について、UDIDを利用する以外の代替案について考えてみた。

スマフォのリワード広告におけるUDIDに依存しない設計案 - snippets from shinichitomita’s journal

まあ、上記のようにAppleというプラットフォーマーがそのビジネス自体を禁止したい意図がある場合は、こんなことをしても意味がないが、もしこれがAndroidなどでも同じような状況なのだとしたら、少しは役に立つかもしれない

訪問者のTwitterアカウントをWebサイトが知る方法について

覚えている人もいるだろうけど、その昔、Twitterには「WWWあしあと」とも言うべき機能(というよりも欠陥に近い)があった。要は、ユーザ自身のタイムラインのツイート情報をWebブラウザ上のJavaScriptからとってこれたため、どのTwitterユーザがWebサイトに訪問しているかをまったく簡単に特定することができたのだが、残念ながらというかやっぱりねというか、Twitterがメジャーになるにつれてそのような完全プライバシー無視な機能は(おそらく良識により)排除されていった。

その後、OAuthによるアクセス承認の仕組みがTwitterより提供されることで、外部Webサイトが訪問者のTwitter上のアイデンティティ情報をオフィシャルに許された形で知ることができるようになった。これは、情報を取得する前にユーザの同意を得ることが前提なため、全てのサイトにTwitterIDが垂れ流しになっている先のような状態に比べれば比較的プライバシーには配慮されている。

つまり、現在のTwitter

  • まったく知らないサイトに自分のTwitterアカウントが勝手に知られることはない
  • もしサイトにTwitterアカウントを教えたい場合には、ユーザが同意した上でサイトごとにその情報を提供する仕組みがある

という状態であるといえる。

しかしながら、現在でも、ユーザにはその意図がないにもかかわらず、WebサイトがそのユーザのTwitter アカウント情報を知ることができる場合があることを、先ほど確認したので、ブログに書く。

Twitterアカウントが特定されるケース

どのような場合にTwitterアカウントが知られてしまうのかというと、そのサイトに設置されたTwitterTweetボタンを利用して、ツイートを行った場合である。

通常ソーシャル共有ボタンを設置したサイトは、それがFacebookにせよはてブあるいはMixiチェックにせよ、そのWebサイトの訪問者が実際にソーシャルメディアに対して投稿を行ったのか、あるいは誰がいつ投稿したのかまではわからないようになっている。これは実際の投稿画面がそのWebサイトではなくそれぞれのソーシャルメディアが提供するWebページ上で提供されるため、元のWebサイトの側からはSame-Origin Policyによりアクセスが不可能なためだ。Facebookのいいねボタンの場合はボタン自体がIFRAMEで提供されているのでわかりにくいが、Facebookドメインに属したページとなっており、Webサイトの管理の及ぶ外になっている。

ところが、TwitterTweetボタンの場合には、Web Intent JavaScript Eventという、ユーザがTweetを投稿したことをボタンを設置した元のページに対して通知する仕組みが提供されている。

https://dev.twitter.com/docs/intents/events

このイベント通知には、投稿者のTwitterアカウント情報こそ含まれていないものの、イベントの通知されたタイミングによって、ツイートが投稿された時刻を1秒程度の誤差の範囲で知ることができる。そして、該当するURLの投稿履歴から、その時刻にツイートされたステータスを特定することにより、そのステータスの投稿者=Webサイトにアクセスしているユーザとして特定できる、ということになる。

実証コード

実際にこれが可能であることを確かめたコードをこちらに置いておく。

http://stomita-lab.s3.amazonaws.com/twitter-id-detect/estimate.html

上記サイトのTweetボタンをおしてツイートを投稿し、しばらく待つと、自分のTwitterアカウントがページ内に表示される。

まあ別に何かしているわけではないので構わないのだが、慎重な方やあまりTLを汚したくないけれども試したい場合は、適当なダミーアカウントでTwitterにログインした上で利用されたほうがいいかもしれない。

注意事項

アカウント推定の仕組み上、同一タイミングに同じURLへの投稿が重なると、間違って特定される場合がある。ただし数秒の間にツイートが集中するというのは、あり得ないことではないし無視はできないが、全体からすれば稀であると思われる。むしろ、その他の場合にはほぼ確実にアカウントの特定が可能であるというところが注目すべき点である。そしてもちろん、同一のユーザセッションから幾つかのツイートが得られる場合は、それをもとにして精度を上げることができる。

問題についての考察

まず、自分の意見では、このこと(Webサイトが訪問者のTwitterアカウント情報を同意なく知ることができること)は憂慮すべきことである、と考えている。ただ単にプライバシーが毀損される可能性だけでなく、おそらくこのままだとこれを利用する人が多く出てくるだろう、という意味でだ。

何が問題となるか思い至らない場合のために、一つ例を上げておく。

もしあなたがとあるECサイトを利用していて、そこでは以前に自分の個人的な趣味(あまりおおっぴらにしたくない類の)の製品を閲覧し購入していたとする。そして、そのサイトで、ふと目に止まった家電製品が大変気に入ったので、その商品の横についていたTweetボタンを押して、自分のTL上に商品のURLを紹介したとする。ここで、そのECサイトはそのツイート情報から得られるTwitterアカウント情報と、ECサイトにアクセスしているユーザの閲覧・購買履歴を付きあわせることができてしまう。(そしてその後なぜかあなたのTwitterアカウントには自分の隠れた趣味に関するメンションが多く届くことになるかもしれない)

なお、訪問者のTwitterアカウント情報を利用したいと考えている人が多いだろう、というのは個人的な感想ではあるが、おそらく間違いないと思う。現在、Webサイトを運営する事業者は、訪問者のソーシャルなアイデンティティ情報を利用することで、より機会を多くすることができると考えている。彼ら曰く、よりよいカスタマーサービスを行うために、あるいはインフルエンサーとなり得る顧客を把握するために、それらは必要となるし、実際にそれらを活用したサービスを提供しようと意欲的に取り組んでいるところも多くある。

しかしながら、そういった情報の取得は、必ずユーザの同意を得た上で行うのが原則である。今回の場合は、そのサイト内のある情報についてツイートしただけなのだから、それをソーシャルメディア上でのアイデンティティ情報を紐付けてもよいと同意した、というように解釈するには、あまりに遠すぎる。Tweetボタンを押して投稿したユーザは、もちろん投稿した以上はタイムライン上から自分のTwitter投稿とそのアカウントが検索可能になること自体は想定するだろうが、それが今ブラウザを開いている自分自身が閲覧中のセッションとリンクされることについては、決して想定していないだろう。

この問題は、プライバシー保護に対するコンプライアンスの面からももちろんそうなのだが、ソーシャルメディアのエコシステムという面から特に考えるべきことではないかとおもう。もしもツイートからユーザを特定することが当たり前のものとして普及してしまうと、それによって自らの特定をされたくないユーザは、サイトに設置したソーシャルボタンのクリックをためらうことになるだろう。それは果たしてサイト運営者が望むものなのかどうなのか。あるいはTwitterというソーシャルメディアが望むものなのかどうなのか。


(追記)おそらくなのだけれども、FackebookのLikeボタンの場合も、そのようなユーザの追跡と紐付けが可能になる(?)かもしれない。Like Boxをクリックした際のイベントを取るためのJS APIが定義されている。

http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe/

ただし、FBのページに対してLikeしたユーザの一覧を取得するAPIがあるのかどうか、そしてそこにユーザがLikeを押した時刻の情報も含まれるのかどうかについては、ちょっと調べただけではよくわからなかった。ご存じの方がいたら教えてほしい。

iOSのTwitter統合とAndroidのAccountManager、そしてBrowserIDに見るUser-Centric Identityの再来?について

最近、自分自身のテクノロジー的な世界観に対して影響を与える刺激がいろいろとあったわけだけど、その中におぼろげながら共通点が感じられたので、最初はGoogle+にLimitedなメモとして書いたのだが、ブログの方に清書する。

続きを読む

Apps Script から OAuth + RESTful API でSalesforceへつなぐ

Google Apps ScriptのUrlFetchAppで、OAuth Serviceによって認証&SalesforceのRESTful APIに接続する。なお通常のPassword認証での接続については、[twitter:@mino0123]さんが既にやってるお話。

事前に https://na7.salesforce.com/help/doc/ja/remoteaccess_define.htm にしたがって、Salesforceにリモートアプリケーションを登録し、OAuth Consumer Key/Secret のペアを入手する。今回の場合、コールバックURLには "https://spreadsheets.google.com/macros" を指定する。その後、スプレッドシート側で同情報をスクリプトプロパティとして登録する。

ソースコード

/**
 * Connect and fetch Salesforce data via OAuth
 */
function queryDataFromSalesforce() {
  // Read OAuth consumer key / secret of this client app from script properties, 
  // which can be issued from Salesforce's remote access setting in advance.
  var sfConsumerKey = ScriptProperties.getProperty("sfConsumerKey");
  var sfConsumerSecret = ScriptProperties.getProperty("sfConsumerSecret");
  if (!sfConsumerKey || !sfConsumerSecret) {
    Browser.msgBox("Register Salesforce OAuth Consumer Key and Secret in Script Properties");
    return;
  }

  // Register new OAuth service, named "salesforce"
  // For OAuth endpoint information, see help doc in Salesforce.
  // https://na7.salesforce.com/help/doc/en/remoteaccess_oauth_1_flows.htm
  var oauth = UrlFetchApp.addOAuthService("salesforce");
  oauth.setAccessTokenUrl("https://login.salesforce.com/_nc_external/system/security/oauth/AccessTokenHandler");
  oauth.setRequestTokenUrl("https://login.salesforce.com/_nc_external/system/security/oauth/RequestTokenHandler");
  oauth.setAuthorizationUrl("https://login.salesforce.com/setup/secur/RemoteAccessAuthorizationPage.apexp?oauth_consumer_key="+encodeURIComponent(sfConsumerKey));
  oauth.setConsumerKey(sfConsumerKey);
  oauth.setConsumerSecret(sfConsumerSecret);

  // Convert OAuth1 access token to Salesforce sessionId (mostly equivalent to OAuth2 access token)
  var sessionLoginUrl = "https://login.salesforce.com/services/OAuth/u/21.0";
  var options = { method : "POST", oAuthServiceName : "salesforce", oAuthUseToken : "always" };
  var result = UrlFetchApp.fetch(sessionLoginUrl, options);
  var txt = result.getContentText();
  var accessToken = txt.match(/<sessionId>([^<]+)/)[1];
  var serverUrl = txt.match(/<serverUrl>([^<]+)/)[1];
  var instanceUrl = serverUrl.match(/^https?:\/\/[^\/]+/)[0];
  
  // Query account data from Salesforce, using REST API with OAuth2 access token.
  var fields = "Id,Name,Type,BillingState,BillingCity,BillingStreet";
  var soql = "SELECT "+fields+" FROM Account LIMIT 100";
  var queryUrl = instanceUrl + "/services/data/v21.0/query?q="+encodeURIComponent(soql);
  var response = UrlFetchApp.fetch(queryUrl, { method : "GET", headers : { "Authorization" : "OAuth "+accessToken } });
  var queryResult = Utilities.jsonParse(response.getContentText());

  // Render query result to Spreadsheet
  var sheet = SpreadsheetApp.getActiveSheet();
  sheet.clear();
  sheet.setFrozenRows(1);

  // Render all field names in header row.
  var cell = sheet.getRange('a1');
  fields = fields.split(',');
  fields.forEach(function(field, j){ cell.offset(0, j).setValue(field) })

  // Render result records into cells
  queryResult.records.forEach(function(record, i) {
    fields.forEach(function(field, j) { cell.offset(i+1, j).setValue(record[field]) });
  });

}

課題

  • 一旦OAuth認証されてしまうと、ログアウトして別アカウントでログインを行うことができない。UrlFetchAppに登録してるサービス名を分ければ可能だとは思うが、それはあまりアプローチとしては正しくないか。
  • OAuthの認証がスクリプトエディタからRunしないとできない。これはエンドユーザに使わせるには致命的。

SFDCには悪いですが、Force.comの正統な後継者の一番手はGoogle Apps Scriptかもしれない、という思い

先週 Google Apps Script 勉強会 #3に行ってきました。大遅刻で10分くらいしか参加しませんでしたが。

partake.in

とりあえず親睦会で気になってたことをちょろちょろお話できてよかったです。GWTを「ぐうぃっと」と読むことを教わりました。ありがとうございます。

続きを読む

「実践JS サーバサイド JavaScript 入門」について

献本いただきました。ありえるえりあの著者の文体そのままですね。

実践JS サーバサイド JavaScript 入門

実践JS サーバサイド JavaScript 入門


サーバサイドJavaScript。2011年のWeb技術者のトレンドはどうやらこれみたいです(HTML5とか大本命を除く)。
早めにサーバサイドJSに入れ込んで、日の目をみることなく散っていった(あるいはただ単に日の目を見ていないだけで生きている)アーリーアダプターたちのサービス・プロダクトを良く知っています。とはいってもサーバサイドJS(というよりNon-Browser JS)は、この本にも触れられてるとおり、昨日今日でてきたものでもなく結構な歴史があるのですが。ともかく、Node.jsがでてきてよかったですね。

しかしどうやらこの本的にはそのNode.jsにはあまり触れていない。「NodeなどV8ベースのがどうなるかは今後に期待」というスタンス。実践アプリ開発においてコードとして乗っているのもRhino+Jersey(On GAE/J)。今ならnode.js+express(on Cloud Foundry ?) でサックリ書けるところかもしれない。
なのでNode.jsによるイベント駆動型でのサービス作成に興味がある人にとってはどうか、とは思う。おそらくNodeは今後他にも本が出てくるので、そっちを見ればいい。こちらは歴史も含めてサーバサイドJSというものを概観するのにふさわしい。


さて、この本での一番のポイントは、個人的には「クラウドの拡張言語」としてのJavaScript、に対してもスポットを当てた点ではないかと思う。具体的には、Google Apps Scriptの事を1章使って紹介している。PaaSのレイヤで動くものでなく*1SaaSのレイヤで利用できる言語として最適である、としている。
これについては、古くから(といってもつい5,6年前頃なんだけど)SalesforceJavaScriptをサーバロジック記述に採用すべきだ、と主張してきた身*2としては、納得いく主張ではある。クラウドでなくても、Lotus Notes/Dominoとか、それこそ著者の会社のArielのプロダクトとか、拡張で使うことは多いみたいですね。あまり語られないですが、NetSuiteのSuiteScriptってのがあるんですが、これもJavaScriptベースみたいですよ旦那。


ちなみに、JavaScriptがプロダクトの拡張機能を記述する目的において優れている、というだけではなく、クラウド特有(=マルチテナント環境特有)のメリットというのはあるのかないのか、これを僕自身は知りたいところではある。
例えばなんらかのSandboxみたいなものは、マルチテナント環境には必ず必要になるだろう。ユーザ/アプリケーション/リソースの種類毎に、プログラム実行時の振る舞い(アクセス制御、リソースの利用制限)をより低いレイヤから制御する、というようなことが可能になるのかどうか。たとえば、先にあげたGoogle Apps Scriptは、ループなどで暴走するコードは実行時にステップが多くなりすぎると中止するようになっている。これは統制がより下のレイヤで行われている、と考えていい。
もちろんこれは言語そのものより実行系として与えられたものの性質の話ではあるけれども、ともかく、そのようなことが楽に可能になることがPaaSをよりスケーラブルに展開するのに役に立つことは間違いないだろうと思う。

*1:このあたり分解点は微妙ではあるが。ロジック記述できたらそれPaaSでもよくね?と思うこともある

*2:もし型チェックがほしいならScalaとかでもいいから、とにかくApexでない何かでいい(笑)