ラベル OAuth の投稿を表示しています。 すべての投稿を表示
ラベル OAuth の投稿を表示しています。 すべての投稿を表示

2017/01/17

[Python][Google App Engine][Twitter][OAuth]request token

昔、書いた[Google App Engine][twitter][oauth]ログインを自作するから少し仕様が変わったみたいなので、少し見やすくして、新しくクラスを作ってみた。
//twitter.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from google.appengine.api import urlfetch
import datetime
import time
import random
import urllib
import hmac
import hashlib
import logging

class LoginTwitter(object):
    def __init__(self,consumer_secret,consumer_key,oauth_callback_url):
        self.consumer_secret = consumer_secret
        self.method = "POST"
        self.request_token_url = "https://api.twitter.com/oauth/request_token"
        self.oauth_authenticate_url = "https://api.twitter.com/oauth/authenticate"
        self.prms = {
            "oauth_consumer_key":consumer_key,
            "oauth_nonce":LoginTwitter._nonce(),
            "oauth_signature_method":'HMAC-SHA1',
            "oauth_version":'1.0',
            "oauth_timestamp":LoginTwitter._timeStamp(),
            "oauth_callback":oauth_callback_url
        }

        self.prms['oauth_signature'] = self._signature()
        self._request()

    @classmethod
    def _timeStamp(cls):
        _d = datetime.datetime.today()
        _d = time.mktime(_d.timetuple())
        _d = str(int(_d))
        return _d

    @classmethod
    def _nonce(cls):
        _s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789[]{}!$%&'()-^\:;*+><"
        _l = list(_s)
        _n = ""
        for i in range(20):
            _x = random.randint(0,len(_l)-1)
            _n += _l[_x]
        return _n

    def _signature(self):
        _prms_keys = sorted(self.prms.keys())
        _prms = ''
        for i in _prms_keys:
            _prms = _prms + "&" + i + "=" + urllib.quote_plus(self.prms[i])
        else:
            _prms = _prms[1:]

        _prms = urllib.quote_plus(self.method) + "&" + urllib.quote_plus(self.request_token_url) + "&" + urllib.quote_plus(_prms)
        _h = hmac.new("%s&%s" % (urllib.quote(self.consumer_secret), urllib.quote("")), _prms, hashlib.sha1)
        _sig = _h.digest().encode("base64").strip()
        return _sig

    def _request(self):
        
        if self.method == 'POST':
            _headers = {
                'Content-Type': 'application/x-www-form-urlencoded'
            }

            _prms_keys = sorted(self.prms.keys())
            _prms = ''
            for i in _prms_keys:
                _prms = _prms + "," + i + "=\"" + urllib.quote_plus(self.prms[i]) + "\""
            else:
                _prms = _prms[1:]

            _prms = "OAuth " + _prms
            _headers["Authorization"] = _prms
            _result = urlfetch.fetch(
                url = self.request_token_url,
                payload = urllib.urlencode({"oauth_callback":self.prms["oauth_callback"]}),
                method = urlfetch.POST,
                headers = _headers
            )
            logging.info(_result.status_code)
            logging.info(_result.content)
呼び出しはいたって簡単。
from twitter import LoginTwitter
consumer_secret
consumer_key
oauth_callback_url
LoginTwitter(consumer_secret,consumer_key,oauth_callback_url)
まだ全部の処理が書き終わっていないので、改訂が入るかも。

2013/08/07

[Instagram][Python][GoogleAppEngine]認証(ログイン)方法

Instagramの認証を読みながら実際に、ログイン方法について調べる。

Google App Engineを使った場合は、下のように実装できるようだ。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import webapp2
import json
import urllib
from google.appengine.api import urlfetch

class LoginInstagram(webapp2.RequestHandler):
 def get(self):
  self.redirect("https://api.instagram.com/oauth/authorize/?client_id=hoge&redirect_uri=http%3A%2F%2Ffoo.appspot.com%2Ftest%2Finsta%2Fredirect&response_type=code")
  

class RedirectFromInstagram(webapp2.RequestHandler):
 def get(self):
  self.response.headers['Content-Type'] = 'text/plain'
  code = self.request.get('code')

  url = "https://api.instagram.com/oauth/access_token"
  payload = urllib.urlencode({
   "client_id":"hoge",
   "client_secret":"hoge_secret",
   "grant_type":"authorization_code",
   "redirect_uri":"http://foo.appspot.com/test/insta/redirect",
   "code":str(code)
  })
  result = urlfetch.fetch(
   url=url,
   payload=payload,
   method=urlfetch.POST,
   headers={'Content-Type': 'application/x-www-form-urlencoded'}
  )
  if result.status_code == 200:
   #tokenの表示
   self.response.out.write(result.content)

app = webapp2.WSGIApplication([
 ('/test/insta', LoginInstagram),
 ('/test/insta/redirect', RedirectFromInstagram)
 ],debug=False
)
Oauth2だとこんなに簡単なものかとびっくりしてしまった。

ライブラリなどを使わずに実装できてしまうのである。

Oauth2は、どんどん普及してほしいなと思う昨今である。

2011/09/25

[Google App Engine][twitter][oauth]ログインを自作する

twitterのUsing OAuth 1.0aを読んでいて、認証を行なうには、oauthを使う必要があるとのこと。

oauthを使うにあたって、すでに、サンプルがころがっているのだが、理由はわからないが、すぐに受け入れることはできなかった。

Djangoや、JavaScriptのjQueryは受け入れられたのに。

きっと、3rdパーティーの不確定要素が多いライブラリからだと思うのだが。
(この判断も一方的なのだが。)

前置きが長くなってしまったが、というわけで、Google App EngineのPythonを使って自作をすることに。


#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import datetime,time,random,urllib,hmac,hashlib,re,os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.api import urlfetch


#django
from google.appengine.dist import use_library
use_library('django', '1.0')
from django.utils import simplejson
from google.appengine.ext.webapp import template

# Request Token URL
reqt_url = 'https://api.twitter.com/oauth/request_token'
# Authorize URL
auth_url = 'https://api.twitter.com/oauth/authorize'
# Access Token URL
acct_url = 'https://api.twitter.com/oauth/access_token'

class oauth():

def __init__(self,secret= "",method="POST"):

self.consumer_secret = '●●●●●●'
self.token_secret = secret
self.method = method

self.param = {
'oauth_consumer_key':'●●●●●●',
'oauth_nonce':self.getNonce(),
'oauth_signature_method':'HMAC-SHA1',
'oauth_version':'1.0',
'oauth_timestamp':self.getTimeStamp()
}

def setParam(self,param):
for i in param:
self.param[i] = param[i]
self.param['oauth_signature'] = self.makeSignature()

def callRequest(self,requrl):
param = urllib.urlencode(self.param)

if self.method == 'POST':

result = urlfetch.fetch(
url=requrl,
payload=param,
method=urlfetch.POST,
headers={'Content-Type': 'application/x-www-form-urlencoded','User-Agent':'***'}
)

else:

param = re.sub("&", ",", param)

result = urlfetch.fetch(
url=requrl,
method=urlfetch.GET,
headers={'Authorization': 'OAuth realm="",'+param,'User-Agent':'***'}
)


if result.status_code == 200:
return {'result':True,'content':result.content}
else:
return {'result':False,'content':result.content}
#return {'result':False,'content':str(result.status_code)}

def getNonce(self):
str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789[]{}!$%&'()-^\:;*+><"
strlist = list(str)
string = ""
for i in range(20):
x = random.randint(0,len(strlist)-1)
string += strlist[x]
return string

def getTimeStamp(self):
d = datetime.datetime.today()
d = time.mktime(d.timetuple())
d = str(int(d))
return d

def makeSignature(self):
pks = sorted(self.param.keys())
param = ''

for i in pks:
param = param + '&' + i + '=' + urllib.quote_plus(self.param[i])
else:
param = param[1:]

param = urllib.quote_plus(self.method) + '&' + urllib.quote_plus(reqt_url) + '&' + urllib.quote_plus(param)

h = hmac.new("%s&%s" % (urllib.quote(self.consumer_secret), urllib.quote(self.token_secret)), param, hashlib.sha1)
sig = h.digest().encode("base64").strip()
return sig


class LoginHandler(webapp.RequestHandler):

def get(self):

oauth_token = self.request.get('oauth_token')
oauth_verifier = self.request.get('oauth_verifier')

if oauth_token == '' and oauth_verifier == '':

ioauth = oauth()
param = {'oauth_callback':'この処理が走るURL'}
ioauth.setParam(param)
ret = ioauth.callRequest(reqt_url)
if ret['result']:
content = ret['content']

content = content.split('&')
for i in content:
temp = i.split('=')
ioauth.param[temp[0]] = temp[1]

self.response.headers.add_header('Set-Cookie', 'ts='+ioauth.param['oauth_token_secret'])
self.redirect(auth_url+"?oauth_token="+ioauth.param['oauth_token'], permanent=True)

else:

ioauth = oauth(secret=self.request.cookies.get('ts'))
param = {'oauth_token':oauth_token,'oauth_verifier':oauth_verifier}
ioauth.setParam(param)
ret = ioauth.callRequest(acct_url)

if ret['result']:
content = ret['content']

content = content.split('&')
for i in content:
temp = i.split('=')
ioauth.param[temp[0]] = temp[1]

#認証成功!!
path = os.path.join(os.path.dirname(__file__), 'redirect.html')
template_values = {
'oauth_token':ioauth.param['oauth_token'],
'oauth_token_secret':ioauth.param['oauth_token_secret'],
'user_id':ioauth.param['user_id'],
'screen_name':ioauth.param['screen_name']
}
self.response.out.write(template.render(path,template_values))


def main():
application = webapp.WSGIApplication(
[('/twitter/login', LoginHandler)],
debug=True
)
util.run_wsgi_app(application)

if __name__ == '__main__':
main()
http://hoge/twitter/loginにアクセスすると、LoginHandlerクラスが反応します。

最初、直アクセスした場合は、当然、

oauth_token
と、
oauth_verifier
がないので、最初のif文が実行されます。

その中で、oauthクラスのインスタンス変数を宣言するのですが、コンストラクタの時点で、作成できる変数群をすべて設定しておきます。

setParamクラスでsignatureを作成し、httpのPOSTで通信を行ないます。

oauth_token、oauth_token_secret返ってくるので、oauth_token_secretをクッキーに保存しoauth_tokenをquery stringに設定して、リダイレクトをします。

リダイレクトの結果、oauth_token、oauth_verifierがコールバック先のquery stringに付与されて呼び出されるので、それを元に、再度、signatureを作成、access_tokenをゲットします。

今回、上記の処理を書くにあたって、Twitter API を OAuth で認証するスクリプトを 0 から書いてみたを参考にしました。

ただ参照先サイトは、コールバックの設定がなかったので、そこだけ、ちょっとプログラムの変更をする必要がありましたが。

こうしてまとめておけば、後でいつでも、振り返るので、便利ですね。

2010/02/12

[PHP]OAuth最終確定版

やっとOAuthのログインがスムーズに行くようになりました。

こちらの記事にものすごくわかりやすい説明があったので、結局、それを参考に実装しました。

前提条件として、こちらにあるOAuth.phpとOAuth_TestServer.phpの2つをアップロードして、あらかじめプログラムにincludeしておきます。

まず、https://www.google.com/accounts/OAuthGetRequestTokenにアクセスするところまで

$consumer['key'] = 'anonymous';
$consumer['secret'] = 'anonymous';
$consumer = new OAuthConsumer($consumer['key'], $consumer['secret'], NULL);

$server = new TestOAuthServer(new MockOAuthDataStore());
$server->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());

$sig_methods = $server->get_signature_methods();
$sig_method = $sig_methods['HMAC-SHA1'];

$endpoint = "https://www.google.com/accounts/OAuthGetRequestToken";

$request = OAuthRequest::from_consumer_and_token($consumer, NULL, "GET", $endpoint, array("scope"=>"http://tables.googlelabs.com/api/query"
,"oauth_callback"=>"リダイレクト先のURL"));
$request->sign_request($sig_method, $consumer, NULL);

$req = curl_init($request);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

次に、https://www.google.com/accounts/OAuthAuthorizeTokenへの問い合わせについて

//tokens配列に問い合わせの結果をセットする
parse_str($result, $tokens);

$_SESSION["oauth_token_secret"] = $tokens["oauth_token_secret"];

$auth_url = "https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=".urlencode($tokens['oauth_token'])."&hl=jp";
$req = curl_init($auth_url);
curl_setopt($req, CURLOPT_HEADER, TRUE);
curl_setopt($req, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($req);

list($header) = explode("\r\n\r\n", $result, 2);
$matches = array();

preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
$url = trim(array_pop($matches));

//リダイレクトを行う
header('Location: '.$url);

この後、前回と違って、いきなりリダイレクトが開始されます。
(前回は、2回ログインする必要がありました。)

で、許可のボタンを押すと、リダイレクト先のurlに飛びますので、引き続き、そこから先の処理について。
$consumer['key'] = 'anonymous';
$consumer['secret'] = 'anonymous';
$consumer = new OAuthConsumer($consumer['key'], $consumer['secret'], NULL);

$server = new TestOAuthServer(new MockOAuthDataStore());
$server->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());

$sig_methods = $server->get_signature_methods();
$sig_method = $sig_methods['HMAC-SHA1'];

$endpoint = "https://www.google.com/accounts/OAuthGetAccessToken";
$tokener = new OAuthConsumer($_GET['oauth_token'],
https://www.google.com/accounts/OAuthGetRequestTokenの問い合わせの時にゲットしたoauth_token_secret);
$access = OAuthRequest::from_consumer_and_token($consumer, $tokener, "GET", $endpoint, array("oauth_verifier"=>$_GET["oauth_verifier"]));
$access->sign_request($sig_method, $consumer, $tokener);
$req = curl_init($access);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

で、これの問い合わせの結果、oauth_token_secretと、oauth_tokenをゲットします。

$endpoint = "http://tables.googlelabs.com/api/query";
parse_str($result, $tokens);
$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);
$tokener = new OAuthConsumer($tokens['oauth_token'], $tokens['oauth_token_secret']);
$resource = OAuthRequest::from_consumer_and_token($consumer, $tokener, "GET", $endpoint, array('sql'=>'SHOW TABLES'));
$resource->sign_request($sig_method, $consumer, $tokener);
$req = curl_init($resource);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($req);

//デバッグ
print($result);

exit();

printの結果、正常に処理が返されました。

うーん、しかし、結局のところ、何が問題だったのだろうか?

ここから先は、推測ですが、おそらく、問い合わせにcurlを使わなかったのが原因なのではないかと。

リクエストの方法が違うだけでこうも結果に違いがでちゃうんだよなー。

CFと違ってPHPは、たくさん関数があるので一日でも早く慣れたいですね。

さっき東武練馬で50歳の恋愛白書を見たんだけど、全然、おもしろくなかった。
当初、キアヌリーブスが助演するということで、タイトル的にも、恋愛適齢期を想定して見に行ったのに、タイトルと内容があっていないような。。。

2010/02/03

[OAuth][php]AccessTokenを取得する

前回は、認証されたRequestTokenを取得する方法を書いたので、AccessTokenを取得する方法について書きたいと思います。

mb_http_output("utf-8");
require_once('HTTP/Request.php');

//urlの設定
$url = "https://www.google.com/accounts/OAuthGetAccessToken";
$method = "GET";

$params["oauth_consumer_key"] = "anonymous";
$params["oauth_nonce"] = md5(microtime().mt_rand());
$params["oauth_signature_method"] = "HMAC-SHA1";
$params["oauth_signature"] = "";
$params["oauth_timestamp"] = time();
$params["oauth_token"] = rawurlencode($_GET["oauth_token"]);
$params["oauth_verifier"] = rawurlencode($_GET["oauth_verifier"]);
$params["oauth_version"] = "1.0";

$params["oauth_signature"] = make_Signature_Base_String($method,$url,$params);
$params["oauth_signature"] =
base64_encode(hash_hmac("sha1"
,$params["oauth_signature"]
,"anonymous&".前々回取得したoauth_token_secret
,true)
);

$request = new HTTP_Request();
$request->setURL($url);

$request->
addHeader("Authorization"," OAuth");

$request->
addQueryString("oauth_consumer_key",$params["oauth_consumer_key"],false);
$request->
addQueryString("oauth_nonce",$params["oauth_nonce"],false);
$request->
addQueryString("oauth_signature_method",$params["oauth_signature_method"],false);
$request->
addQueryString("oauth_signature",$params["oauth_signature"],false);
$request->
addQueryString("oauth_timestamp",$params["oauth_timestamp"],false);
$request->
addQueryString("oauth_token",$params["oauth_token"],true);
$request->
addQueryString("oauth_verifier",$params["oauth_verifier"],true);
$request->
addQueryString("oauth_version",$params["oauth_version"],false);

//メソッドをセット
$request->setMethod($method);

//リクエストを実行
$result = $request->sendRequest();

上を実行することによって、$resultに「oauth_token=hoge&oauth_token_secret=foo」みたいな感じで結果が返ってくるんだけど、返ってきたoauth_token、oauth_token_secretを使ってAPIの問い合わせを行ってもエラーが発生するんだよなー。

なぜだろー??

2010/02/01

[OAuth][php]AuthorizeされたTokenを取得するリクエスト

前回に引き続き、今日もPHPを使ったOAuth関連。

前回は、UnAuthorizedRequestTokenが取得できたので、AuthorizedRequestTokenを取得する方法について下記のように実装しました。

$url = "https://www.google.com/accounts/OAuthAuthorizeToken";

$request = new HTTP_Request();
$request->setURL($url);

$request->addQueryString("oauth_token",前回取得したoauth_tokenの値,true);
$request->addQueryString("hd","default",false);
$request->addQueryString("hl","ja",false);

//メソッドをセット
$request->setMethod($method);

//リクエストを実行
$result = $request->sendRequest();


print($request->getResponseBody());

これを実行すると、下のようの画面になります。
Photobucket
上の画面で、ログインを行うと下のような画面になります。
Photobucket
(なぜかこの画面に入るまでに2回ログインする必要があったのですが、これは実装バグなのだろうか?)

そして「アクセス許可」ボタンを押すと、

「http://リダイレクト先のURL?oauth_token=hoge&oauth_verifier=foo」

というurlを返してくれます。

2010/01/29

[OAuth][php]UnAuthorizedRequestTokenを取得するリクエスト

今、データをクラウドに保存する候補として、Google App Engineももちろんのこと、Google Fusion Tablesも検討の一つに入れています。

Google Fusion Tables上にあるデータをやりとりするためには、OAuthが必要ということで、さっそく調査をしてみました。

まず、認証の流れはこちらにあってちょっと説明が長いので割愛。

リンク先の1と2について書いてみたいと思います。

UnAuthorizedRequestTokenを取得するためにリクエストを出すのですが、主なパラメータはOAuthGetRequestTokenにあって、これをもとにphpで表現するとこんな感じになりました。

mb_http_output("utf-8");
require_once('HTTP/Request.php');

//urlの設定
$url = 'https://www.google.com/accounts/OAuthGetRequestToken';

$method = "GET";

$params["oauth_callback"] = rawurlencode("コールバック先のurl");
$params["oauth_consumer_key"] = "anonymous";
$params["oauth_nonce"] = md5(microtime().mt_rand());
$params["oauth_signature_method"] = "HMAC-SHA1";
$params["oauth_signature"] = "";
$params["oauth_timestamp"] = time();
$params["oauth_version"] = "1.0";

$params["scope"] = rawurlencode("http://tables.googlelabs.com/api/query");

$params["oauth_signature"] = make_Signature_Base_String($method,$url,$params);
$params["oauth_signature"] = base64_encode(hash_hmac("sha1" ,$params["oauth_signature"],"anonymous&",true));

$request = new HTTP_Request();
$request->setURL($url);

$request->addHeader("Authorization"," OAuth");

$request->
addQueryString("oauth_callback",$params["oauth_callback"],true);
$request->
addQueryString("oauth_consumer_key",$params["oauth_consumer_key"],false);
$request->
addQueryString("oauth_nonce",$params["oauth_nonce"],false);
$request->
addQueryString("oauth_signature_method",$params["oauth_signature_method"],false);
$request->
addQueryString("oauth_signature",$params["oauth_signature"],false);
$request->
addQueryString("oauth_timestamp",$params["oauth_timestamp"],false);
$request->
addQueryString("oauth_version",$params["oauth_version"],false);

$request->addQueryString("scope",$params["scope"],true);

//メソッドをセット
$request->setMethod($method);

//リクエストを実行
$result = $request->sendRequest();
$ret = $request->getResponseBody();

print($ret);

function make_Signature_Base_String($aMethod,$aUrl,$aParams){
$ret = "";
foreach($aParams as $key => $value){
if($key != "oauth_signature"){
$ret = $ret."&".$key."=".$value;
}
}
$ret = substr($ret,1);
$ret = rawurlencode($ret);
$ret = $aMethod."&".rawurlencode($aUrl)."&".$ret;
return $ret;
}

これを実行すると、結果として
oauth_token=hoge1&oauth_token_secret=hoge2&oauth_callback_confirmed=true

みたいな感じで表示されると思います。

一番悩んだのが、oauth_signatureを作り方。。。
(まる3日間悩みました。)

アルファベット順に並べてエンコードをかけるというところまではオッケーだったのですが、コールバック関数にあらかじめエンコードをかけておく必要があり(つまり2回エンコードする)、さらに、hash_hmac関数の第二引数の最後に「&」が必要だというのに気づくのにすっげー苦労した。

どうして気がついたのかというと、[OAuth]PHPを読んだことある人のためのOAuthのSignature解説に紹介されていたPHP用ライブラリのプログラムを読んで気がつきました。

次回は、ここら先について書きいきたいと思います。