expressでOAuth。Twitterに投稿。
なんだかよく分からなかった。やっとできた。基本的なことが分かってないんだね。
追)githubにサンプルを作りました https://github.com/piglovesyou/express-twitter-oauth-sample
やること:
- 下準備。ライブラリの準備。
- app.get('/', routes.index);。ログインしてなかったら、/login にリダイレクト。
- app.get('/login', routes.login); ログイン画面。ユーザーに「Twitterでログイン」をクリックしてもらう。
- app.get('/auth/twitter', routes.auth.twitter); request_token を取得。取得したら、authenticate を叩く。
- app.get('/auth/twitter/callback', routes.auth.twitter.callback); verifierを取得。取得したら、access_tokenを取得。
- 準備完了。ユーザーのscreen_name を出して、ログイン中画面にユーザーをお招き。
- app.post('/post', routes.post); アプリ経由でtweet を投稿してみる。
- app.get('/logout', routes.logout); ログアウト。セッションを破壊。大爆発。
1. 下準備。ライブラリの準備。
https://dev.twitter.com/appsで、アプリを登録。今回はtweetしたいので、read and write に設定しておく。
expressは、sessionを使うオプションでプロジェクトを作成。
npm install oauth しておいて、./routes/index.js で読みこむ。 ./routes/index.js の先頭で、oauth の設定をする(インスタンス作り)。
// ./routes/index.js var OAuth = require('oauth').OAuth; var oa = new OAuth( "https://twitter.com/oauth/request_token", "https://twitter.com/oauth/access_token", "Your consumer key is here.", "Your consumer secret is here.", "1.0", "http://localhost.com:3000/auth/twitter/callback", "HMAC-SHA1");
2. app.get('/', routes.index);
このページは、ログインユーザーしか見れなくする。
ログインしてる人のために、テンプレート「index」が必要。
ログインしてない人は、ただちに /login に飛んでもらう。
exports.index = function (req, res) { if(req.session.oauth && req.session.oauth.access_token) { res.render('index', { screen_name: req.session.twitter.screen_name }); } else { res.redirect("/login"); } };
3. app.get('/login', routes.login);
ログインしてない人が、「ログイン」リンクをクリックしてもらうためのページ。無くてもいい。ここに「Twitterでログイン」「Facebookでログイン」の2つをあとで置こうかなと思ってます。今は、Twitterだけ。
テンプレート「login」が要ります。
exports.login = function (req, res) { if(req.session.oauth && req.session.oauth.access_token) { } else { res.render('login'); } };
4. app.get('/auth/twitter', routes.auth.twitter);
認証のためのURL。テンプレートはなし。アクセスと同時に、oauthインスタンスにrequest_tokenを取りに行かせる。
request_tokenをtwitterにもらったら、ユーザーにそれを握らせて、アプリの認証画面に行ってもらう。あと、貰ったほかのものはsessionにしまっておく。ユーザーが「許可」してくれることを祈って待つ。
exports.auth = {}; exports.auth.twitter = function(req, res){ oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){ if (error) { res.send("yeah no. didn't work.") } else { req.session.oauth = {}; req.session.oauth.token = oauth_token; req.session.oauth.token_secret = oauth_token_secret; res.redirect('https://twitter.com/oauth/authenticate?oauth_token='+oauth_token) } }); };
5. app.get('/auth/twitter/callback', routes.auth.twitter.callback);
OAuthをnew したところと同じcallback用のURL。これはTwitterのアプリ設定画面でも同じものを登録しとく必要がある。
ユーザーが「許可します」ボタンを押してくれたら、Twitterはユーザーをここに連れてくる。パラメータには、ユーザーからのOKサインである「verifier」が入っているので、ありがたく貰っておく。
verifierと、さっきsessionにしまったものをきっちり揃えて、Twitterにお願いしに行く。書類をそろえて、「このユーザーと懇意にさせてください」とお願いする。書類に不備がなければ、OKのハンコがもらえる。OKハンコは「access_token」と「access_token_secret」の2つ。これがずっと欲しかったもの。目的を達成したから、それをsessionに大事にしまって、ユーザーをログイン中画面に連れていく。
exports.auth.twitter.callback = function(req, res, next){ if (req.session.oauth) { req.session.oauth.verifier = req.query.oauth_verifier; var oauth = req.session.oauth; oa.getOAuthAccessToken(oauth.token, oauth.token_secret, oauth.verifier, function(error, oauth_access_token, oauth_access_token_secret, results){ if (error){ res.send("yeah something broke."); } else { req.session.oauth.access_token = oauth_access_token; req.session.oauth.access_token_secret = oauth_access_token_secret; req.session.twitter = results; res.redirect("/"); } } ); } else next(new Error("you're not supposed to be here.")); };
6. 準備完了。
ユーザーのscreen_name を出して、「ログイン中画面」にユーザーをお招きする。
exports.index = function (req, res) { if(req.session.oauth && req.session.oauth.access_token) { // コッチ。 res.render('index', { screen_name: req.session.twitter.screen_name , title: 'Express' }); } else { res.redirect("/login"); } };
これで「Twitterでログイン」ができました。
7. app.post('/post', routes.post);
このアプリ経由で、Tweetしてもらう。
ここは、xhrで叩く想定。ユーザーがここにPOSTすると、textパラメータの値をTwitterに投稿するしくみにする。
クライアントサイドのHTMLとJSはこんな。さきにjQueryを読んでおく。
// index.jade form#tweet input(type="text", name="text") input(type="submit", value="ツイート。")
// client side script $('#tweet').submit(function (e) { var text = e.target.text.value; $.ajax({ url: '/post' , type: 'POST' , data: {text: text} }); return false; });
うん。
nodeは、リクエストを受け取ると、こんどはoauthインスタンスにリクエストを投げさせる。引数に、TwitterのOKハンコであるaccess_tokenとaccess_token_secretを渡す(ユーザ特定のヒントとユーザ許可済みのしるし)。oauthインスタインスは、Twitterに特別なリクエストを投げてくれる。
特別っていうか、セッションに保存しているtokenやらいろいろを使って、特別なヘッダを作って、リクエストを飛ばす。
このときどんなリクエストを投げて欲しいかを、Twitterは丁寧に解説している。
... At a very simplified level, Twitter's implementation requires that requests needing authorization contain an additional HTTP Authorization header with enough information to answer the questions listed above. ...
https://dev.twitter.com/docs/auth/authorizing-requestPOST /1/statuses/update.json?include_entities=true HTTP/1.1
Accept: */*
Connection: close
User-Agent: OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Authorization:
OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
oauth_version="1.0"
Content-Length: 76
Host: api.twitter.comstatus=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21
consumer_keyやら、access_tokenやら。もう必要なものは全部持っているんだけど、この特別なヘッダ(Authorization header)を作るのが大変。いろいろ混ぜたりこねたりしなくちゃいけない。
これをやってくれるのが、node-oauthモジュール。node-oauthの中身をのぞくと、たとえばaccess_token_secretを使って、まぜたりこねたりして、シグネチャーを作っているのが分かる。絶対に作りたくないね。それをやってくれる。ありがたいね、本当の話。
exports.post = function (req, res) { if(req.session.oauth && req.session.oauth.access_token) { var text = req.body.text; oa.post( 'https://api.twitter.com/1/statuses/update.json', req.session.oauth.access_token, req.session.oauth.access_token_secret, {"status": text}, function (err, data, response) { if (err) { res.send('too bad.' + JSON.stringify(err)); } else { res.send('posted successfully...!'); } }); } else { res.send('fail.'); } };
登録しているアプリ経由で、投稿できたはず。
8. app.get('/logout', routes.logout);
ログアウトする。
ログアウトのリンクをどっかに置いておいて、テンプレート「logout」も作っておく。テンプレートには「完全にログアウトしたから安心するように」と書いておく。
exports.logout = function (req, res) { req.session.destroy(); res.render('logout'); };
ボンッッ。おしまい。
参考: