ãã®èšäºã¯ãæ°ããFlask Mega-TutorialïŒ2018ïŒèšäºã·ãªãŒãºã®ããŒãã¹ã§ãã
èè
ã¯åããã²ã«ã°ãªãŒã³ããŒã°ã§ãã ãã®èšäºã¯æ°ãããã®ã§ã¯ãããŸãããããã®é¢é£æ§ã¯å€±ãããŠããŸããã
OAuthãã¯ãããžãŒã¯10幎以äžäœ¿çšãããŠãããã€ã³ã¿ãŒãããèŠèŽè
ã®99ïŒ
ãOAuthããµããŒããããªãœãŒã¹ã®å°ãªããšã1ã€ã«ã¢ã«ãŠã³ããæã£ãŠããŸãã ã»ãšãã©ãã¹ãŠã®ãªãœãŒã¹ã«ãµã€ã³ã€ã³ãã¿ã³ããããŸããïŒ Flaskãã€ã¯ããã¬ãŒã ã¯ãŒã¯ã䜿çšããŠãããã©ã®ããã«è¡ãããããèŠãŠã¿ãŸãããã

å€ãã®Webãµã€ãã§ã¯ãããç¥ãããŠãããœãŒã·ã£ã«ãµãŒãã¹ã®ããããã®ãŠãŒã¶ãŒã¢ã«ãŠã³ãã䜿çšãããµãŒãããŒãã£èªèšŒãµãŒãã¹ã䜿çšããŠãã¯ã³ã¯ãªãã¯ã§ç°¡åã«ç»é²ã§ããŸãã ç§ã®å€ãFlask Mega-Tutorialã³ãŒã¹ã§ã¯ããããã®ãããã³ã«ã®1ã€ã§ããOpenIDã䜿çšããæ¹æ³ã説æããŸããïŒçŸåšã¯ã ã³ã¡ã³ããã©ã³ã¹ã¬ãŒã¿ãŒã®äžçã§äœ¿çšãããŠããŸã ïŒã

ãã®èšäºã§ã¯ã OAuthãããã³ã«ã®æŠèŠã玹ä»ããŸããæè¿ã§ã¯ã OpenIDãåªå ããããµãŒãããŒãã£èªèšŒã¡ã«ããºã ãšããŠçœ®ãæããããŠããŸãã ãŸããFacebookã§ãµã€ã³ã€ã³æ©èœãšTwitterã§ãµã€ã³ã€ã³æ©èœãå®è£ ããå®å šãªFlaskã¢ããªã玹ä»ããŸãã ããã2ã€ã®å®è£ ãäŸã«ãšããšãå¿ èŠãªä»ã®OAuthãããã€ããŒãç°¡åã«è¿œå ã§ããŸãã
ã泚æ 翻蚳è ïŒ ããã§OAuthãã©ã®ããã«æ©èœãããã«ã€ããŠã®ãªã³ã¯ãããã«2ã€ãããŸãã
OAuthã®ç°¡åãªçŽ¹ä»
OAuthãå°å ¥ããæè¯ã®æ¹æ³ã¯ããã°ã€ã³äžã«çºçããã€ãã³ãããªã¹ãããããšã§ãã
- ãã©ãŒã éä¿¡ã®åŠçã ãŠãŒã¶ãŒã¯ã¢ããªã±ãŒã·ã§ã³ã®ããŒã ããŒãžïŒ http://www.example.comãªã©ïŒã«ç§»åãã ãFacebookã§ãµã€ã³ã€ã³ããã¿ã³ãã¯ãªãã¯ããŸãããã®ãã¿ã³ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã«ãŒãïŒ http://www.example.com/authorize/facebookãªã©ïŒã«ãªã³ã¯ããŸã ã
- ãã§ãããªã¯ãšã¹ãããŒã¯ã³ïŒå
éšãªã¯ãšã¹ãïŒã ãµãŒããŒã¯ãªã¯ãšã¹ããåä¿¡ãã Facebook OAuthèªèšŒURLãžã®ãªãã€ã¬ã¯ãã§å¿çããŸãã ãã¹ãŠã®OAuthãããã€ããŒã¯ããŠãŒã¶ãŒããªãã€ã¬ã¯ãããURLãææžåããå¿
èŠããããŸãã
- ãªã¯ãšã¹ãã¯ã ã³ã³ã·ã¥ãŒãã㌠ïŒãã¢ããªã±ãŒã·ã§ã³ãã°ã€ã³ãïŒãæž¡ãããªã¯ãšã¹ãèªäœã¯ã³ã³ã·ã¥ãŒãã·ãŒã¯ã¬ãã ïŒãã¢ããªã±ãŒã·ã§ã³ãã¹ã¯ãŒããïŒã䜿çšããŠçœ²åãããŸããããã«ãããåœé ããä¿è·ãããŸãã
- å¿çãšããŠããããã€ããŒã¯ãªã¯ãšã¹ãããŒã¯ã³ãšåŒã°ãããã¬ããŒãžã§æºãããããããŒã¯ã³ãçæããŠè¿ããŸãã
- æ¿èªã«ãªãã€ã¬ã¯ãããŸãïŒãã©ãŠã¶ã®ãªãã€ã¬ã¯ãçµç±ïŒã ãŠãŒã¶ãŒã¯Facebookã«ãã°ã€ã³ããããã«æ±ããããŸãïŒãŸã ãã°ã€ã³ããŠããªãå ŽåïŒã ãŠãŒã¶ãŒãèŠæ±ãããæ å ±ãå ã®ã¢ããªã±ãŒã·ã§ã³ãšå ±æããèš±å¯ãFacebookã«ä»äžããå¿ èŠãããå Žåãæ å ±å ±æèŠæ±ãæäŸãããŸãã ããã¯ãã¹ãŠFacebook Webãµã€ãã§è¡ãããFacebookãšãŠãŒã¶ãŒéã®ãã©ã€ããŒããã©ã³ã¶ã¯ã·ã§ã³ã§ãããã¢ããªã±ãŒã·ã§ã³ã¯é¢äžããŸããã
- ã¢ã¯ã»ã¹ããŒã¯ã³ã®ååŸïŒå éšãªã¯ãšã¹ãïŒã ãŠãŒã¶ãŒãæ å ±äº€æèŠæ±ãåãå ¥ããåŸãFacebookã¯äºåã«æ§æãããã³ãŒã«ããã¯URLïŒ http://www.example.com/callback/facebookãªã©ïŒã§ã¢ããªã±ãŒã·ã§ã³ã«ãªãã€ã¬ã¯ãããŸã ã ãªãã€ã¬ã¯ãURLãªã¯ãšã¹ãè¡ã«ã¯ãã¢ããªã±ãŒã·ã§ã³ããŠãŒã¶ãŒã«ä»£ãã£ãŠFacebook APIã«ã¢ã¯ã»ã¹ããããã«äœ¿çšã§ããèªèšŒã³ãŒããå«ãŸããŠããŸãã
- APIïŒå éšãªã¯ãšã¹ãïŒãåŒã³åºããŸãã ã¢ããªã±ãŒã·ã§ã³ã¯Facebook APIã䜿çšããŠãŠãŒã¶ãŒæ å ±ãååŸããŸãã ç¹ã«èå³æ·±ãã®ã¯ããã°ã€ã³ãããŠãŒã¶ãŒãç»é²ããåŸãã¢ããªã±ãŒã·ã§ã³ããŒã¿ããŒã¹ã«ãŠãŒã¶ãŒãç»é²ããããã«äœ¿çšã§ããäžæã®ãŠãŒã¶ãŒèå¥åïŒ Shared Secret ïŒã§ãã
ã¢ããªã±ãŒã·ã§ã³ãšãµãŒãããŒãã£ãµãŒãã¹éã®äº€æã¯ç°¡åã§ã¯ãããŸãããããŠãŒã¶ãŒã«ãšã£ãŠå¿ èŠãªã®ã¯ãµãŒãããŒãã£ã®ãµã€ãã«å ¥ããã¢ããªã±ãŒã·ã§ã³ã䜿çšããŠæ å ±ã亀æããèš±å¯ãäžããã ããªã®ã§ããŠãŒã¶ãŒã«ãšã£ãŠã¯éåžžã«ç°¡åã§ãã
çŸåšã2ã€ã®ããŒãžã§ã³ã®OAuthãããã³ã«ã䜿çšãããŠããŸããäž¡æ¹ãšãäžèšã®äžè¬çãªããã»ã¹ã«åŸã£ãŠãããå®è£ ã®éãããããŸãã Twitterã§äœ¿çšãããOAuth 1.0aã¯ããã®2ã€ã®äžã§æãè€éã§ãã Facebookã§äœ¿çšãããOAuth 2ã¯äºææ§ã®ãªãæ¹èšãããã³ã«ã§ãããããŒãžã§ã³1.0aã®è€éãã®ã»ãšãã©ãæé€ããæå·åã«ã»ãã¥ã¢HTTPã䜿çšããŠããŸãã
OAuthãããã€ããŒã«ç»é²ãã
ã¢ããªã±ãŒã·ã§ã³ããµãŒãããŒãã£ã®OAuthãããã€ããŒã䜿çšããåã«ãç»é²ããå¿ èŠããããŸãã FacebookãšTwitterã®å Žåãããã¯ããããã®éçºè ãµã€ãã§è¡ããããããã®ãµã€ãã®ãŠãŒã¶ãŒã«ã¢ããªã±ãŒã·ã§ã³ãæ瀺ãããã¢ããªããäœæããŸãã
Facebookã¢ããªã±ãŒã·ã§ã³ã¯https://developer.facebook.comããäœæã§ããŸã ã
ãéå§ããšã次ãžããã¯ãªãã¯ããŸãã ãã®éçšã§ãããªãèªèº«ã«é¢ããããŸããŸãªæ
å ±ãèšå
¥ããŠãã ããã


ãFacebookã§ãµã€ã³ã€ã³ããéžæããŸã

å¯èœãªã¢ããªã±ãŒã·ã§ã³ã®ãªã¹ããããã¿ã€ããWWW / Webãµã€ãããéžæããŸãã

ã¢ããªã±ãŒã·ã§ã³ã®URLãæå®ããŸããããã¯ãã³ã³ãã¥ãŒã¿ãŒã§èµ·åããå Žåã
http://localhost:5000
ãŸãã

OAuthèªèšŒã®äŸ
以äžã®ã»ã¯ã·ã§ã³ã§ã¯ãFacebookããã³TwitterèªèšŒãå®è£ ããæ¯èŒçåçŽãªFlaskã¢ããªã±ãŒã·ã§ã³ã«ã€ããŠèª¬æããŸãã
ãã®èšäºã§ã¯ã¢ããªã±ãŒã·ã§ã³ã®éèŠãªéšåã玹ä»ããŸãããå®å šãªã¢ããªã±ãŒã·ã§ã³ã¯GitHubãªããžããªhttps://github.com/miguelgrinberg/flask-oauth-exampleã§å ¥æã§ããŸã ã ãã®èšäºã®æåŸã«ãå®è¡æ¹æ³ã瀺ããŸãã
ãŠãŒã¶ãŒã¢ãã«
ãµã³ãã«ã¢ããªã±ãŒã·ã§ã³ã®ãŠãŒã¶ãŒã¯ãSQLAlchemyããŒã¿ããŒã¹ã«ä¿åãããŸãã ã¢ããªã±ãŒã·ã§ã³ã¯ã Flask-SQLAlchemyæ¡åŒµæ©èœã䜿çšããŠããŒã¿ããŒã¹ãæäœãã Flask-Loginæ¡åŒµæ©èœã䜿çšããŠç»é²ãŠãŒã¶ãŒã远跡ããŸãã
from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.login import LoginManager, UserMixin db = SQLAlchemy(app) lm = LoginManager(app) class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) social_id = db.Column(db.String(64), nullable=False, unique=True) nickname = db.Column(db.String(64), nullable=False) email = db.Column(db.String(64), nullable=True) @lm.user_loader def load_user(id): return User.query.get(int(id))
ããŒã¿ããŒã¹ã«ã¯ããŠãŒã¶ãŒusersçšã®å¥ã®ããŒãã«ããããã¡ã€ã³ããŒã§ããid
ã«å ããŠã3ã€ã®åãå«ãŸããŠããŸãã
social_id
ïŒãã°ã€ã³ã«äœ¿çšããããµãŒãããŒãã£èªèšŒãµãŒãã¹ããã®äžæã®èå¥åãèå¥ããæååã
nickname
ïŒãŠãŒã¶ãŒã®ããã¯ããŒã ã ãã¹ãŠã®ãŠãŒã¶ãŒã«å¯ŸããŠå®çŸ©ããå¿ èŠããããäžæã§ããå¿ èŠã¯ãããŸããã
email
ïŒãŠãŒã¶ãŒã®ã¡ãŒã«ã¢ãã¬ã¹ã ãã®åã¯ãªãã·ã§ã³ã§ãã
Userã¯ã©ã¹ã¯Flask-Loginã®UserMixin
ãç¶æ¿ãããã®æ¡åŒµæ©èœã«å¿
èŠãªã¡ãœãããæäŸããŸãã ãŸããFlask-Loginã«å¿
èŠãªuser_loader
ã³ãŒã«ããã¯é¢æ°ã¯ããŠãŒã¶ãŒã®äž»ããŒã«ãã£ãŠãŠãŒã¶ãŒãèªã¿èŸŒã¿ãŸãã
OAuthã®å®è£
Pythonçšã®OAuthã¯ã©ã€ã¢ã³ãããã±ãŒãžãããã€ããããŸãã ãã®äŸã§ã¯ã Rauthã䜿çšããããšã«ããŸãã ã ãã ããOAuthããã±ãŒãžã䜿çšããå Žåã§ãããããã€ããŒèªèšŒã«ã¯å€ãã®åŽé¢ããããã¿ã¹ã¯ãå°é£ã«ãªããŸãã
ãŸããåºã䜿çšãããŠããOAuthãããã³ã«ã«ã¯2ã€ã®ããŒãžã§ã³ããããŸãã ãã ããåãããŒãžã§ã³ã®OAuthã䜿çšããŠãããããã€ããŒã®éã§ããä»æ§ã®äžéšã§ã¯ãªãå€ãã®è©³çŽ°ããããç¬èªã®ããã¥ã¡ã³ãã«åŸã£ãŠè¡ãå¿ èŠããããŸãã
ãã®ãããFlaskã¢ããªã±ãŒã·ã§ã³ãäžè¬çãªæ¹æ³ã§èšè¿°ã§ããããã«ãRauthã®äžã«æœè±¡åã¬ã€ã€ãŒãå®è£ ããããšã«ããŸããã 以äžã¯ãç¹å®ã®ãããã€ããŒå®è£ ãèšè¿°ãããåçŽãªåºæ¬ã¯ã©ã¹ã§ãã
class OAuthSignIn(object): providers = None def __init__(self, provider_name): self.provider_name = provider_name credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name] self.consumer_id = credentials['id'] self.consumer_secret = credentials['secret'] def authorize(self): pass def callback(self): pass def get_callback_url(self): return url_for('oauth_callback', provider=self.provider_name, _external=True) @classmethod def get_provider(self, provider_name): if self.providers is None: self.providers = {} for provider_class in self.__subclasses__(): provider = provider_class() self.providers[provider.provider_name] = provider return self.providers[provider_name] class FacebookSignIn(OAuthSignIn): pass class TwitterSignIn(OAuthSignIn): pass
OAuthSignIn
åºæ¬ã¯ã©ã¹ã¯ãåãããã€ããŒã®ãµãŒãã¹ãå®è£
ãããµãã¯ã©ã¹ãåŸããªããã°ãªããªãæ§é ãå®çŸ©ããŸãã ã³ã³ã¹ãã©ã¯ã¿ãŒã¯ããããã€ããŒã®ååãšãããã«å²ãåœãŠãããæ§æããååŸãããã¢ããªã±ãŒã·ã§ã³èå¥åãšã·ãŒã¯ã¬ããã³ãŒããåæåããŸãã 以äžã¯ãã¢ããªã±ãŒã·ã§ã³æ§æã®äŸã§ãïŒãã¡ããããããã®ã³ãŒããç¬èªã®ãã®ã«çœ®ãæããå¿
èŠããããŸãïŒã
app.config['OAUTH_CREDENTIALS'] = { 'facebook': { 'id': '470154729788964', 'secret': '010cc08bd4f51e34f3f3e684fbdea8a7' }, 'twitter': { 'id': '3RzWQclolxWZIMq5LJqzRZPTl', 'secret': 'm9TEd58DSEtRrZHpz2EjrV9AhsBRxKMo8m3kuIZj3zLwzwIimt' } }
æäžäœã«ã¯ããã®ã¯ã©ã¹ã§ãµããŒãããã2ã€ã®éèŠãªã€ãã³ãããããŸãããããã¯ãã¹ãŠã®OAuthãããã€ããŒã«å ±éã§ãã
- èªèšŒããã»ã¹ã®éå§ã ãããè¡ãã«ã¯ãã¢ããªã±ãŒã·ã§ã³ããããã€ããŒã®Webãµã€ãã«ãªãã€ã¬ã¯ãããŠããŠãŒã¶ãŒãããã§èªèšŒã§ããããã«ããå¿
èŠããããŸãã ããã¯ã
authorize()
ã¡ãœããã§è¡šãããŸãã - èªèšŒãå®äºãããšããããã€ããŒã¯ã¢ããªã±ãŒã·ã§ã³ããªãã€ã¬ã¯ãããŸãã ããã¯ã
callback()
ã¡ãœããã«ãã£ãŠåŠçããcallback()
ã ãããã€ããŒã¯ã¢ããªã±ãŒã·ã§ã³ã®å éšã¡ãœããã«çŽæ¥ã¢ã¯ã»ã¹ã§ããªãããããããåŒã³åºãURLã«ãªãã€ã¬ã¯ããããŸãã ãããã€ããŒããªãã€ã¬ã¯ãããå¿ èŠãããURLã¯get_callback_url()
ã¡ãœããã«ãã£ãŠè¿ããããããã€ããŒã®ååã䜿çšããŠäœæããããããåãããã€ããŒã¯ç¬èªã®å°çšã«ãŒããååŸããŸãã
get_provider()
ã¡ãœããã¯ããããã€ããŒåãæã€æ£ããOAuthSignIn
ã€ã³ã¹ã¿ã³ã¹ãèŠã€ããããã«äœ¿çšãããŸãã ãã®ã¡ãœããã¯ãã€ã³ããã¹ãã¯ã·ã§ã³ã䜿çšããŠOAuthSignIn
ãã¹ãŠã®ãµãã¯ã©ã¹ãæ€çŽ¢ããããããã®ã€ã³ã¹ã¿ã³ã¹ãèŸæžã«ä¿åããŸãã
Rauthã䜿çšããOAuthèªèšŒ
Rauthã¯ã䜿çšããããããã³ã«ã®ããŒãžã§ã³ã«å¿ããŠãã¯ã©ã¹OAuth1Service
ãŸãã¯OAuth2Service
ãªããžã§ã¯ããæã€OAuthãããã€ããŒãè¡šããŸãã åãããã€ããŒã®OAuthSignIn
ãµãã¯ã©ã¹ã«ãã®ã¯ã©ã¹ã®ãªããžã§ã¯ããäœæããŸãã Facebookããã³Twitterã®å®è£
ã以äžã«ç€ºããŸãã
class FacebookSignIn(OAuthSignIn): def __init__(self): super(FacebookSignIn, self).__init__('facebook') self.service = OAuth2Service( name='facebook', client_id=self.consumer_id, client_secret=self.consumer_secret, authorize_url='https://graph.facebook.com/oauth/authorize', access_token_url='https://graph.facebook.com/oauth/access_token', base_url='https://graph.facebook.com/' ) class TwitterSignIn(OAuthSignIn): def __init__(self): super(TwitterSignIn, self).__init__('twitter') self.service = OAuth1Service( name='twitter', consumer_key=self.consumer_id, consumer_secret=self.consumer_secret, request_token_url='https://api.twitter.com/oauth/request_token', authorize_url='https://api.twitter.com/oauth/authorize', access_token_url='https://api.twitter.com/oauth/access_token', base_url='https://api.twitter.com/1.1/' )
OAuth 2ãå®è£
ããFacebookã§ã¯ã OAuth2Service
ã¯ã©ã¹ãOAuth2Service
ãŸãã ãµãŒãã¹ãªããžã§ã¯ãã¯ããµãŒãã¹åãšããã€ãã®OAuthåºæã®åŒæ°ã§åæåãããŸãã client_id
client_secret
ãšclient_secret
ã¯ãFacebookéçºè
ãµã€ãã®ã¢ããªã±ãŒã·ã§ã³ã«å²ãåœãŠãããclient_secret
ã§ãã Authorize_url
ããã³access_token_url
ã¯ãèªèšŒããã»ã¹äžã«æ¥ç¶ããå¿
èŠãããã¢ããªã±ãŒã·ã§ã³çšã«Facebookã«ãã£ãŠå®çŸ©ãããURLã§ãã æåŸã«ã base_url
ã¯ãèªèšŒã®å®äºåŸã«Facebook APIåŒã³åºãã®ãã¬ãã£ãã¯ã¹URLãèšå®ããŸãã
Twitterã¯OAuth 1.0aãå®è£
ããŠããããã OAuth1Service
ã¯ã©ã¹ãOAuth1Service
ãŸãã OAuth 1.0aã§ã¯ãèå¥ã³ãŒããšã·ãŒã¯ã¬ããã³ãŒãã¯consumer_key
ããã³consumer_secret
ãšåŒã°ããŸãããOAuth 2ãããã³ã«ãšæ©èœãåãã§ããOAuth1ãããã³ã«ã§ã¯ããããã€ããŒã¯2ã€ã§ã¯ãªã3ã€ã®URLã衚瀺ããå¿
èŠããããŸããè¿œå ã®request_token_url
ãªã¯ãšã¹ãããããŸãã name
ããã³base_url
åŒæ°ã¯ãOAuth 2ãµãŒãã¹ã§äœ¿çšããããã®ãšåãã§ãTwitterã¯ã authorize_url
ãã©ã¡ãŒã¿ãŒã«2ã€ã®ãã©ã¡ãŒã¿ãŒãæäŸããããšã«æ³šæããŠãã ããã äžèšã®URL https://api.twitter.com/oauth/authorize
ã¯ãã¢ããªã±ãŒã·ã§ã³ã«æ¯åTwitterãžã®ã¢ã¯ã»ã¹ãèš±å¯ããå¿
èŠããããŠã£ã³ããŠããŠãŒã¶ãŒã«è¡šç€ºãããããæãå®å
šã§ãã ãã®URLãhttps://api.twitter.com/oauth/authenticate
å€æŽãããšãTwitterãåããŠèš±å¯ãèŠæ±ãããŠãŒã¶ãŒãTwitterãããã°ã¢ãŠããããŸã§éãã«ã¢ã¯ã»ã¹ãèš±å¯ããŸãã
URLã®ãšã³ããªãã€ã³ãã«ã¯æšæºåãååšããªãããšã«æ³šæããŠãã ãã; OAuthãããã€ããŒã¯ãé©åã§ãããšå€æããŠããããå®çŸ©ããŸãã æ°ããOAuthãããã€ããŒãè¿œå ããã«ã¯ããããã®URLãããã¥ã¡ã³ãããååŸããå¿ èŠããããŸãã
OAuthæ¿èªãã§ãŒãº
ãŠãŒã¶ãŒãã...ã§ãã°ãªã³ããªã³ã¯ãã¯ãªãã¯ããŠOAuthèªèšŒãéå§ãããšãåŒã³åºãã¯æ¬¡ã®ã«ãŒãã«åŸããŸãã
@app.route('/authorize/<provider>') def oauth_authorize(provider): if not current_user.is_anonymous(): return redirect(url_for('index')) oauth = OAuthSignIn.get_provider(provider) return oauth.authorize()
ãã®ã«ãŒãã§ã¯ããŸããŠãŒã¶ãŒããã°ã€ã³ããŠããªãããšã確èªããŠããããããã€ããŒã«äžèŽããOAuthSignIn
ãµãã¯ã©ã¹ãååŸãããã®authorize()
ã¡ãœãããåŒã³åºããŠããã»ã¹ãéå§ããŸãã 以äžã¯ãFacebookããã³Twitterã®authorize()
å®è£
ã§ãã
class FacebookSignIn(OAuthSignIn): # ... def authorize(self): return redirect(self.service.get_authorize_url( scope='email', response_type='code', redirect_uri=self.get_callback_url()) ) class TwitterSignIn(OAuthSignIn): # ... def authorize(self): request_token = self.service.get_request_token( params={'oauth_callback': self.get_callback_url()} ) session['request_token'] = request_token return redirect(self.service.get_authorize_url(request_token[0]))
Facebookãªã©ã®OAuth 2ãããã€ããŒã®å Žåãå®è£
ã¯åã«rauth
ãµãŒãã¹rauth
ã«ãã£ãŠçæãããURLãžã®ãªãã€ã¬ã¯ããçºè¡ãrauth
ã ã¹ã³ãŒãã¯ãããã€ããŒã«äŸåããŸãããã®ç¹å®ã®ã±ãŒã¹ã§ã¯ãFacebookã«ãŠãŒã¶ãŒã®ã¡ãŒã«ãæäŸããããäŸé ŒããŸãã response_type = 'code'ã§ã¯ãåŒæ°ã¯ã¢ããªã±ãŒã·ã§ã³ãWebã¢ããªã±ãŒã·ã§ã³ã§ããããšãoauthãããã€ããŒã«äŒããŸãïŒããŸããŸãªèªèšŒããã»ã¹ã«ä»ã®å¯èœãªå€ããããŸãïŒã æåŸã«ãredirect_uriåŒæ°ã¯ãèªèšŒã®å®äºåŸã«ãããã€ããŒãåŒã³åºãå¿
èŠãããã¢ããªã±ãŒã·ã§ã³ã®ã«ãŒããæå®ããŸãã
OAuth 1.0ãããã€ããŒã¯ãããå°ãè€éãªããã»ã¹ã䜿çšããŸãããã®ããã»ã¹ã§ã¯ããããã€ããŒãããªã¯ãšã¹ãããŒã¯ã³ãåä¿¡ããŸãããªã¯ãšã¹ãããŒã¯ã³ã¯2ã€ã®ã¢ã€ãã ã®ãªã¹ãã§ãæåã®ã¢ã€ãã ã¯ãªãã€ã¬ã¯ãã®åŒæ°ãšããŠäœ¿çšãããŸãã ã³ãŒã«ããã¯ã§åã³å¿ èŠã«ãªãããããªã¯ãšã¹ãããŒã¯ã³å šäœããŠãŒã¶ãŒã»ãã·ã§ã³ã«ä¿åãããŸãã
ã³ãŒã«ããã¯OAuthã³ãŒã«ããã¯ãã§ãŒãº
OAuthãããã€ããŒã¯ããŠãŒã¶ãŒèªèšŒåŸã«ã¢ããªã±ãŒã·ã§ã³ã«ãªãã€ã¬ã¯ãããæ å ±ã亀æããèš±å¯ãäžããŸãã ãã®ã³ãŒã«ããã¯ãåŠçããã«ãŒãã以äžã«ç€ºããŸãã
@app.route('/callback/<provider>') def oauth_callback(provider): if not current_user.is_anonymous(): return redirect(url_for('index')) oauth = OAuthSignIn.get_provider(provider) social_id, username, email = oauth.callback() if social_id is None: flash('Authentication failed.') return redirect(url_for('index')) user = User.query.filter_by(social_id=social_id).first() if not user: user = User(social_id=social_id, nickname=username, email=email) db.session.add(user) db.session.commit() login_user(user, True) return redirect(url_for('index'))
ãã®ã«ãŒãã¯ã OAuthSignIn
ãããã€ããŒOAuthSignIn
ã€ã³ã¹ã¿ã³ã¹ãäœæãããã®callback()
ã¡ãœãããåŒã³åºãcallback()
ã ãã®ã¡ãœããã«ã¯ããããã€ããŒãšã®èªèšŒãå®äºãããŠãŒã¶ãŒæ
å ±ãååŸããæ©èœããããŸãã æ»ãå€ã¯ãäžæã®èå¥åïŒprimary key id
ãšåºå¥ããããsocial_id
ãšåŒã°ããïŒããŠãŒã¶ãŒãšã€ãªã¢ã¹ããŠãŒã¶ãŒã®ã¡ãŒã«ã¢ãã¬ã¹ã®3ã€ã®å€ãæã€ã¿ãã«ã§ãã IDãšãšã€ãªã¢ã¹ã¯å¿
é ã§ããããã®ãµã³ãã«ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãTwitterããã®æ
å ±ãã¢ããªã±ãŒã·ã§ã³ãšå
±æããªããããã¡ãŒã«ããªãã·ã§ã³ã«ããŸããã
ãŠãŒã¶ãŒã¯ã social_id
ãã£ãŒã«ãã«ãã£ãŠããŒã¿ããŒã¹ã§è¡šç€ºããã social_id
ãªãå Žåã¯ããããã€ããŒããåä¿¡ããæ
å ±ã䜿çšããŠæ°ãããŠãŒã¶ãŒãããŒã¿ããŒã¹ã«è¿œå ãããæ°ãããŠãŒã¶ãŒãèªåçã«å¹æçã«ç»é²ãããŸãã 次ã«ããŠãŒã¶ãŒã¯Flask-Loginã®login_user()
é¢æ°ã䜿çšããŠãã°ã€ã³ããæçµçã«ããŒã ããŒãžã«ãªãã€ã¬ã¯ãããŸãã
Facebookããã³Twitterã®OAuthãããã€ããŒã®callback()
ã¡ãœããã®å®è£
ã以äžã«ç€ºããŸãã
class FacebookSignIn(OAuthSignIn): # ... def callback(self): def decode_json(payload): return json.loads(payload.decode('utf-8')) if 'code' not in request.args: return None, None, None oauth_session = self.service.get_auth_session( data={'code': request.args['code'], 'grant_type': 'authorization_code', 'redirect_uri': self.get_callback_url()}, decoder=decode_json ) me = oauth_session.get('me').json() return ( 'facebook$' + me['id'], me.get('email').split('@')[0], # Facebook does not provide # username, so the email's user # is used instead me.get('email') ) class TwitterSignIn(OAuthSignIn): # ... def callback(self): request_token = session.pop('request_token') if 'oauth_verifier' not in request.args: return None, None, None oauth_session = self.service.get_auth_session( request_token[0], request_token[1], data={'oauth_verifier': request.args['oauth_verifier']} ) me = oauth_session.get('account/verify_credentials.json').json() social_id = 'twitter$' + str(me.get('id')) username = me.get('screen_name') return social_id, username, None # Twitter does not provide email
callback()
ã¡ãœããã¯æ€èšŒããŒã¯ã³ãæž¡ããŸããããã¯ãã¢ããªã±ãŒã·ã§ã³ããããã€ããŒã®APIãšéä¿¡ããããã«äœ¿çšã§ããŸãã OAuth 2ã®å Žåãããã¯code
åŒæ°ãšããŠçºçããŸãããOAuth 1.0aã®å Žåã¯oauth_verifier
ãäž¡æ¹ãšãã¯ãšãªæååã§æå®ãããŸãã
ãã®ã³ãŒãã¯ã rauthãµãŒãã¹ãªããžã§ã¯ãããoauth_session
ãååŸããããã«äœ¿çšãããŸã ã
Facebook APIã®ææ°ããŒãžã§ã³ã§ã¯ãã»ãã·ã§ã³ããŒã¯ã³ã¯JSON圢åŒã§è¿ãããããšã«æ³šæããŠãã ããã ãã®ããŒã¯ã³ã«å¯ŸããŠrauthãäºæããããã©ã«ãã®åœ¢åŒã¯ãã¯ãšãªæååã§æå®ããå¿
èŠããããŸãã ãã®ãããJSONã®ã³ã³ãã³ãããã³ãŒãããdecoder
åŒæ°ãè¿œå ããå¿
èŠããããŸãã Python 2ã§ã¯json.loads
ãæž¡ãjson.loads
ã§ãããPython 3ã§ã¯ãã€ããŒããjsonããŒãµãŒãç解ã§ããªããã€ããšããŠè¿ããããããè¿œå ã®æé ãå¿
èŠã§ãã ãã€ãããæååãžã®å€æã¯ãdecode_jsonå
éšé¢æ°ã§å®è¡ãããŸãã
oauth_session
ãªããžã§ã¯ãã䜿çšããŠããããã€ããŒã«APIèŠæ±ãæäŸã§ããŸãã ããã§ã¯ãç¹å®ã®ãããã€ããŒãæäŸããå¿
èŠããããŠãŒã¶ãŒæ
å ±ãèŠæ±ããããã«äœ¿çšãããŸãã Facebookã¯ãŠãŒã¶ãŒIDãšã¡ãŒã«ã¢ãã¬ã¹ãæäŸããŸããããŠãŒã¶ãŒåã¯æäŸããŸããããã®ãããã¢ããªã±ãŒã·ã§ã³ã®ãŠãŒã¶ãŒåã¯ã¡ãŒã«ã¢ãã¬ã¹ã®å·ŠåŽããäœæãããŸãã Twitterã¯èå¥åãšãŠãŒã¶ãŒåãæäŸããŸãããã¡ãŒã«ããµããŒãããŠããªããããã¡ãŒã«ã¯None
ãšããŠè¿ãããŸãã
ãããã€ããŒããåä¿¡ããããŒã¿ã¯ãæåŸã«viewé¢æ°ã®3èŠçŽ ã¿ãã«ãšããŠè¿ãããŸãã ã©ã¡ãã®å Žåãããããã€ããŒããã®id
å€ã¯ãè¿ãããåã«Â«facebook $»
ãŸãã¯Â«twitter $»
è¿œå ããããã¹ãŠã®ãããã€ããŒã§äžæã«ãªãããã«æ³šæããŠãã ããã ããã¯ã¢ããªã±ãŒã·ã§ã³ãããŒã¿ããŒã¹ã«social_id
ãšããŠsocial_id
ãããã®ãªã®ã§ãåãid
2人ã®ç°ãªããŠãŒã¶ãŒã«å²ãåœãŠã2ã€ã®ãããã€ããŒãã¢ããªã±ãŒã·ã§ã³ããŒã¿ããŒã¹ã§ç«¶åããªãããã«ããå¿
èŠããããŸãã
ãããã«
åè¿°ããããã«ããµã³ãã«ã¢ããªã±ãŒã·ã§ã³ã§ã¯ã誰ã§ãFacebookãŸãã¯Twitterã¢ã«ãŠã³ãã䜿çšããŠç»é²ããã³ãã°ã€ã³ã§ããŸãã ãã®ã¢ããªã±ãŒã·ã§ã³ã¯ãæ
å ±ãå
¥åããã«ãŠãŒã¶ãŒãç»é²ããæ¹æ³ã瀺ããŠããŸãããŠãŒã¶ãŒãè¡ãå¿
èŠãããã®ã¯ããããã€ããŒã«ãã°ã€ã³ããŠæ
å ±ã®å
±æãèš±å¯ããããšã ãã§ãã
ãã®äŸãè©ŠããŠã¿ããå Žåã¯ãããã€ãã®æºåæé ã«åŸãå¿
èŠããããŸãã
ãããžã§ã¯ããªããžããªã®ã¯ããŒã³ãŸãã¯ããŠã³ããŒãïŒ https : //github.com/miguelgrinberg/flask-oauth-example
ä»®æ³ç°å¢ãäœæãã requirements.txtãã¡ã€ã«ã®ãªã¹ãããããã±ãŒãžãã€ã³ã¹ããŒã«ããŸãïŒPython 2.7ãŸãã¯3.4ã䜿çšã§ããŸãïŒã
ãã¢ããªããFacebookãšTwitterã«ç»é²ãã
äžèšã®ããã«ã
Facebookããã³Twitterã¢ããªã®IDãšã·ãŒã¯ã¬ããã³ãŒãã䜿çšããŠapp.pyãç·šéããŸãã
ãããã®æé ãå®è¡ããåŸã python app.pyã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãèµ·åãããã©ãŠã¶ãŒã§httpïŒ// localhost ïŒ5000ãå®è¡ã§ããŸãã
ãã®èšäºãOAuthã®åããããã説æã«åœ¹ç«ã€ããšãé¡ã£ãŠããŸãã
質åãããå Žåã¯ãäžã«æžããŠãã ããã
ãã²ã«