FoursquareのAPIのv2でモバイル版の認証画面が使えたりjsonpが使えたりするらしいので、移行してみた。
OAuth認証処理が少し変わったので、それに合わせて作成した認証用クラスや処理を公開。
下記のような認証用クラスを作成した。
foursquare_oauth.py
from google.appengine.api import urlfetch from google.appengine.ext import db from urllib import urlencode from urllib import quote as urlquote import logging from django.utils import simplejson as json class FsqOAuthToken(db.Model): """key_name is uid""" uid = db.StringProperty(required=True) token = db.StringProperty(required=True) created = db.DateTimeProperty(auto_now_add=True) class FoursquareClient(object): def __init__(self, consumer_key, consumer_secret, callback_url=None): """ Constructor.""" self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.callback_url = callback_url def encode(text): return urlquote(str(text), "") def get_authorization_url(self, is_mobile=False): request_url = "https://foursquare.com/oauth2/authenticate" params = { "client_id": self.consumer_key, "response_type": "code", "redirect_uri": self.callback_url, } if is_mobile: params["display"] = "touch" payload = urlencode(params) url = "%s?%s" % (request_url, payload) return url def get_access_token(self, code): request_url = "https://foursquare.com/oauth2/access_token" params = { "client_id": self.consumer_key, "client_secret": self.consumer_secret, "grant_type": "authorization_code", "redirect_uri": self.callback_url, "code": code, } payload = urlencode(params) url = "%s?%s" % (request_url, payload) rpc = urlfetch.create_rpc(deadline=10.0) urlfetch.make_fetch_call(rpc, url, method=urlfetch.GET, headers={}, payload=None) result = rpc.get_result() data = json.loads(result.content) if "access_token" in data: token = data["access_token"] else: token = None return token def get_userinfo(self, access_token): response = self.make_request("https://api.foursquare.com/v2/users/self", access_token) data = json.loads(response.content) if "response" in data and "user" in data["response"]: user_info = data["response"]["user"] token_info = FsqOAuthToken.get_by_key_name(user_info["id"]) if not token_info: token_info = FsqOAuthToken( key_name=user_info["id"], uid=user_info["id"], token=access_token ) else: token_info.access_token = access_token token_info.put() return user_info else: return None def get_token_by_uid(self, uid): return FsqOAuthToken.get_by_key_name(uid) def make_request(self, url, access_token="", additional_params=None, method=urlfetch.GET): payload = self.prepare_request(url, access_token, additional_params, method) if method == urlfetch.GET: url = "%s?%s" % (url, payload) payload = None headers = {} rpc = urlfetch.create_rpc(deadline=10.0) urlfetch.make_fetch_call(rpc, url, method=method, headers=headers, payload=payload) return rpc.get_result() def prepare_request(self, url, access_token="", additional_params=None, method=urlfetch.GET): params = {} if access_token: params["oauth_token"] = access_token if additional_params: params.update(additional_params) for k,v in params.items(): if isinstance(v, unicode): params[k] = v.encode('utf8') return urlencode(params)
下記のような感じで利用してる。
セッションの管理には、appengine_utilitiesというライブラリを使用。
views.py
# -*- coding: utf-8 -*- import re import foursquare_oauth from google.appengine.ext import webapp import appengine_utilities.sessions from project1 import settings def is_iphone(request): if 'User-Agent' in request.headers: user_agent = request.headers['User-Agent'] iphone_safari_pattern = re.compile(r"iPhone.+AppleWebKit.+Safari") m = iphone_safari_pattern.search(user_agent) return True if m else False else: return False def check_signin(request_handler): session = appengine_utilities.sessions.Session() if not session.has_key("userinfo"): is_user_mobile = is_iphone(request_handler.request) client = foursquare_oauth.FoursquareClient(settings.FSQ_OAUTH_KEY, settings.FSQ_OAUTH_SECRET, settings.FSQ_OAUTH_CALLBACK_URL) request_handler.redirect(client.get_authorization_url(is_user_mobile)) return None else: return session["userinfo"] class FoursquareSigninRequestHandler(webapp.RequestHandler): """サインインボタンなどをクリックした時に呼ばれるハンドラ""" def get(self): userinfo = check_signin(self) if userinfo: self.redirect("/") return class FoursquareCallbackRequestHandler(webapp.RequestHandler): """Foursquareに登録したコールバックURLに対応するハンドラ""" def get(self): code = self.request.GET.get("code") client = foursquare_oauth.FoursquareClient(settings.FSQ_OAUTH_KEY, settings.FSQ_OAUTH_SECRET, settings.FSQ_OAUTH_CALLBACK_URL) access_token = client.get_access_token(code) fsq_userinfo = client.get_userinfo(access_token) session = appengine_utilities.sessions.Session() if fsq_userinfo: session["userinfo"] = fsq_userinfo self.redirect("/") return