メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2009年8月3日月曜日

[Apache Shindig][お勉強][OpenSocial] メモ120 shindigで3legged OAuthなるものをしてみる

OAuthには、2legged OAuthなるものと、3legged OAuthなるものがあるらしい。
で、署名付きリクエストは、2legged OAuthというらしい。

2legged OAuthはHMACもRSAもやってみたので、次は3legged OAuthなるものをやってみる。
サービスプロバイダはGoogle。
サンプルとしてGoogle Contact(だっけ?)にアクセスして
自分で構築したShindig環境でアドレス張のデータを表示してみる。

おおきな作業の流れは以下な感じ。
1) ガジェットを用意
2) とりあえずShindigを動かして、該当するガジェット別のdomainを取得
3) google アカウントのManageDomainsページにアクセスし、2)で取得したドメインを登録
4) 3)で登録するとconsumer keyとconsumer secretが発行されるので、それをconfig/oauth.jsonへ記述。
5) ガジェットXMLの一部修正
6) shindigコンパイル&起動
でできるはず。


1) まずガジェットXMLを用意。

http://code.google.com/intl/ja/apis/gadgets/docs/oauth.html
のページを見ると、gadgetのサンプルXMLがあるので、それをコピペ。


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="OAuth Contacts" scrolling="true">
<Require feature="opensocial-0.8" />
<Require feature="locked-domain"/>
<OAuth>
<Service name="google">
<Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" />
<Request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/" method="GET" />
<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" />
</Service>
</OAuth>
</ModulePrefs>
<Content type="html">
<![CDATA[

<!-- shindig oauth popup handling code -->
<script src="http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/popup.js"></script>

<style>
#main {
margin: 0px;
padding: 0px;
font-size: small;
}
</style>

<div id="main" style="display: none">
</div>

<div id="approval" style="display: none">
<img src="http://gadget-doc-examples.googlecode.com/svn/trunk/images/new.gif">
<a href="#" id="personalize">Personalize this gadget</a>
</div>

<div id="waiting" style="display: none">
Please click
<a href="#" id="approvaldone">I've approved access</a>
once you've approved access to your data.
</div>

<script type="text/javascript">
// Display UI depending on OAuth access state of the gadget (see <divs> above).
// If user hasn't approved access to data, provide a "Personalize this gadget" link
// that contains the oauthApprovalUrl returned from makeRequest.
//
// If the user has opened the popup window but hasn't yet approved access, display
// text prompting the user to confirm that s/he approved access to data. The user
// may not ever need to click this link, if the gadget is able to automatically
// detect when the user has approved access, but showing the link gives users
// an option to fetch their data even if the automatic detection fails.
//
// When the user confirms access, the fetchData() function is invoked again to
// obtain and display the user's data.
function showOneSection(toshow) {
var sections = [ 'main', 'approval', 'waiting' ];
for (var i=0; i < sections.length; ++i) {
var s = sections[i];
var el = document.getElementById(s);
if (s === toshow) {
el.style.display = "block";
} else {
el.style.display = "none";
}
}
}

// Process returned JSON feed to display data.
function showResults(result) {
showOneSection('main');

var titleElement = document.createElement('div');
var nameNode = document.createTextNode(result.feed.title.$t);
titleElement.appendChild(nameNode);
document.getElementById("main").appendChild(titleElement);
document.getElementById("main").appendChild(document.createElement("br"));

list = result.feed.entry;

for(var i = 0; i < list.length; i++) {
entry = list[i];
var divElement = document.createElement('div');
divElement.setAttribute('class', 'name');
var valueNode = document.createTextNode(entry.gd$email[0].address);
divElement.appendChild(nameNode);
divElement.appendChild(valueNode);
document.getElementById("main").appendChild(divElement);
}
}

// Invoke makeRequest() to fetch data from the service provider endpoint.
// Depending on the results of makeRequest, decide which version of the UI
// to ask showOneSection() to display. If user has approved access to his
// or her data, display data.
// If the user hasn't approved access yet, response.oauthApprovalUrl contains a
// URL that includes a Google-supplied request token. This is presented in the
// gadget as a link that the user clicks to begin the approval process.
function fetchData() {
var params = {};
url = "http://www.google.com/m8/feeds/contacts/default/base?alt=json";
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH;
params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "google";
params[gadgets.io.RequestParameters.OAUTH_USE_TOKEN] = "always";
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;

gadgets.io.makeRequest(url, function (response) {
if (response.oauthApprovalUrl) {
// Create the popup handler. The onOpen function is called when the user
// opens the popup window. The onClose function is called when the popup
// window is closed.
var popup = shindig.oauth.popup({
destination: response.oauthApprovalUrl,
windowOptions: null,
onOpen: function() { showOneSection('waiting'); },
onClose: function() { fetchData(); }
});
// Use the popup handler to attach onclick handlers to UI elements. The
// createOpenerOnClick() function returns an onclick handler to open the
// popup window. The createApprovedOnClick function returns an onclick
// handler that will close the popup window and attempt to fetch the user's
// data again.
var personalize = document.getElementById('personalize');
personalize.onclick = popup.createOpenerOnClick();
var approvaldone = document.getElementById('approvaldone');
approvaldone.onclick = popup.createApprovedOnClick();
showOneSection('approval');
} else if (response.data) {
showOneSection('main');
showResults(response.data);
} else {
// The response.oauthError and response.oauthErrorText values may help debug
// problems with your gadget.
var main = document.getElementById('main');
var err = document.createTextNode('OAuth error: ' +
response.oauthError + ': ' + response.oauthErrorText);
main.appendChild(err);
showOneSection('main');
}
}, params);
}
// Call fetchData() when gadget loads.
gadgets.util.registerOnLoadHandler(fetchData);
</script>
]]>
</Content>
</Module>


コピペしただけ。
hogehoge.xmlとして保存する。
外部からhttp://www.example.com/opensocial/hogehoge.xmlとして見える場所に設置。


2) とりあえずShindigを動かして、該当するガジェット別のdomainを取得
lockedDomain機能を有効にしているので、gadgetXML毎のiframeのdomainが分からない。
SHA1の結果をBase32してみても良いんだけど、Shindig動かした方が楽しかったので、
そうした。

表示された結果のソースファイルを見てみると、、

http://59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com/gadgets/ifr?url=http://www.example.com/opensocial/hogehoge.xml

などとiframeのsrc属性だったので、ホスト名を抜き出す。
すると、

59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com

なんてなものが抜き出せる。


3) google アカウントのManageDomainsページにアクセスし、2)で取得したドメインを登録

2)で抜き出したgadgets毎のiframeのドメインをgoogleアカウントのmanageDomainsから登録する。
URLはhttps://www.google.com/accounts/ManageDomains
で、「Add a New Domain」ってところから2)で取得した

59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com

を登録。
Googleからverifyなリクエストが飛んでくるので、とある決まったファイルを置いておく。
(登録時にGoogleの画面に従えばOK)



4) 3)で登録するとconsumer keyとconsumer secretが発行されるので、それをconfig/oauth.jsonへ記述。
3)で登録完了すると、

OAuth Consumer Key: 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com
OAuth Consumer Secret: hogehogehoge

などと表示されるので、それをshindigのconfig/oauth.jsonへ記述する。
記述したoauth.jsonは以下な感じ。

{
"http://www.example.com/opensocial/hello.xml" : {
"google" : {
"consumer_key" : "59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com",
"consumer_secret": "hogehogehoge",
"key_type" : "HMAC_SYMMETRIC"
}
}
}

3行目の"google"は、ガジェットの<Service name="google">と合わせておくっぽい。


5) ガジェットXMLの一部修正

Googleからコピーしてきたガジェットを修正する。
ガジェットXMLの10行目、

<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" />

というところを

<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken" />

とoauth_callback以降を削除。
とりあえず、今はコールバックいらないんで。
また、shindig.propertiesのshindig.signing.global-callback-urlも空にセット。


6) shindigコンパイル&起動
そしたら、shindigをコンパイル&起動する。
で、ガジェットを表示してみる。

すると画面に

Personalize this gadget

というリンクが表示されるんで、クリックする。
すると、別Windowが開いて、Googleにログインしていなければ、Googleのログイン画面が表示される。
ログインすると、

The site 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com is requesting access to your Google Account for the product(s) listed below.

なんて書かれたページが表示される。そのページの「Grant access」ボタンを押下。
ボタンを押下すると、

You have successfully granted 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com access to your Google Account. You can revoke access at any time under 'My Account'.

なんて書かれたページが表示される。このWindowはもういらないので、閉じる。

するとあら不思議。
ガジェットにメールアドレス一覧が表示されているではありませんか!


そんだけ。

--
まだいろいろ試したわけじゃないので、なんとも言えないが、
ガジェットを表示する際のOWNERとVIEWERをGoogleのアカウント名と合わせておかないと
駄目かも。
--
OWNERを合わせておかないと、ダメかも。
OWNERが違うときにはGoogle側でエラーになる。
--
ちなみに上記は、Shindig-1.1-SNAPSHOTでやった。
(Java版)

--(2009/08/11)
OwnerIdとViewerIdが違うとShindigでエラーになる。
OAuthRequest.javaの320行目あたり参照。
.

0 コメント: