OAuth2とFlaskを使用してGoogleアカウントで認証するための完全な例と、App Engineでnotを教えてくれる人はいますか?
ユーザーにGoogleカレンダーへのアクセスを許可し、そのアクセスを使用してカレンダーから情報を取得し、さらに処理しようとしています。また、OAuth2トークンを保存して後で更新する必要があります。
Googleの oauth2client ライブラリを見て、認証コードを取得するためにダンスを開始できましたが、そこから少し迷っています。 GoogleのOAuth 2.0 Playgroundを見ると、更新トークンとアクセストークンをリクエストする必要があることを理解していますが、ライブラリで提供されている例はApp EngineとDjango =のみ。
また、OAuth2への参照を含む FlaskのOAuthモジュール を使用しようとしましたが、認証コードを交換する方法も見当たりません。
私はおそらくリクエストを手でコーディングできますが、リクエストを簡単にし、可能なレスポンスを適切に処理し、トークンの保存を支援する既存のpythonモジュールを使用または適応することを好むでしょう.
そのようなことはありますか?
別の回答では Flask-Rauth に言及していますが、使用方法については詳しく説明していません。 Google固有の落とし穴がいくつかありますが、最終的に実装しましたが、うまく機能します。 Flask-Loginと統合して、_@login_required
_のような便利なシュガーでビューを装飾できるようにします。
複数のOAuth2プロバイダーをサポートできるようにしたかったので、コードの一部は汎用であり、FacebookおよびTwitterでOAuth2をサポートすることに関するMiguel Grinbergの優れた投稿に基づいています here 。
最初に、Googleから特定のGoogle認証情報をアプリの構成に追加します。
_GOOGLE_LOGIN_CLIENT_ID = "<your-id-ending-with>.apps.googleusercontent.com"
GOOGLE_LOGIN_CLIENT_SECRET = "<your-secret>"
OAUTH_CREDENTIALS={
'google': {
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
}
}
_
そして、アプリを作成するとき(私の場合、モジュールの___init__.py
_):
_app = Flask(__name__)
app.config.from_object('config')
_
アプリモジュールで、_auth.py
_を作成します。
_from flask import url_for, current_app, redirect, request
from rauth import OAuth2Service
import json, urllib2
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 GoogleSignIn(OAuthSignIn):
def __init__(self):
super(GoogleSignIn, self).__init__('google')
googleinfo = urllib2.urlopen('https://accounts.google.com/.well-known/openid-configuration')
google_params = json.load(googleinfo)
self.service = OAuth2Service(
name='google',
client_id=self.consumer_id,
client_secret=self.consumer_secret,
authorize_url=google_params.get('authorization_endpoint'),
base_url=google_params.get('userinfo_endpoint'),
access_token_url=google_params.get('token_endpoint')
)
def authorize(self):
return redirect(self.service.get_authorize_url(
scope='email',
response_type='code',
redirect_uri=self.get_callback_url())
)
def callback(self):
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 = json.loads
)
me = oauth_session.get('').json()
return (me['name'],
me['email'])
_
これにより、サブクラス化できる汎用OAuthSignIn
クラスが作成されます。 Googleサブクラスは、Googleが公開している情報のリスト(JSON形式 here )から情報を引き出します。これは変更される可能性がある情報であるため、このアプローチにより、常に最新の状態になります。この制限の1つは、Flaskアプリケーションが初期化された(モジュールがインポートされた)ときにサーバーでインターネット接続が利用できない場合、正しくインスタンス化されないことです。問題になりますが、この不測の事態をカバーするために構成データベースに最後に既知の値を保存することは良い考えです。
最後に、クラスはcallback()
関数で_name, email
_のタプルを返します。 Googleは実際には、利用可能な場合はGoogle+プロフィールなど、より多くの情報を返します。 oauth_session.get('').json()
によって返される辞書を調べて、すべてを確認してください。 authorize()
関数でスコープを拡張すると(私のアプリではemail
で十分です)、Google APIを介してさらに多くの情報にアクセスできます。
次に、viewsを記述して、すべてを結び付けます。
_from flask.ext.login import login_user, logout_user, current_user, login_required
@app.route('/authorize/<provider>')
def oauth_authorize(provider):
# Flask-Login function
if not current_user.is_anonymous():
return redirect(url_for('index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
@app.route('/callback/<provider>')
def oauth_callback(provider):
if not current_user.is_anonymous():
return redirect(url_for('index'))
oauth = OAuthSignIn.get_provider(provider)
username, email = oauth.callback()
if email is None:
# I need a valid email address for my user identification
flash('Authentication failed.')
return redirect(url_for('index'))
# Look if the user already exists
user=User.query.filter_by(email=email).first()
if not user:
# Create the user. Try and use their name returned by Google,
# but if it is not set, split the email address at the @.
nickname = username
if nickname is None or nickname == "":
nickname = email.split('@')[0]
# We can do more work here to ensure a unique nickname, if you
# require that.
user=User(nickname=nickname, email=email)
db.session.add(user)
db.session.commit()
# Log in the user, by default remembering them for their next visit
# unless they log out.
login_user(user, remember=True)
return redirect(url_for('index'))
_
最後に、それをすべて実現するための_/login
_ビューとテンプレート:
_@app.route('/login', methods=['GET', 'POST'])
def login():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
return render_template('login.html',
title='Sign In')
_
login.html:
_{% extends "base.html" %}
{% block content %}
<div id="sign-in">
<h1>Sign In</h1>
<p>
<a href={{ url_for('oauth_authorize', provider='google') }}><img src="{{ url_for('static', filename='img/sign-in-with-google.png') }}" /></a>
</div>
{% endblock %}
_
正しいコールバックアドレスがGoogleに登録されていることを確認してください。ユーザーはログインページの[Googleでサインイン]をクリックするだけで、登録してログインできます。
私はさまざまなライブラリの使用についてかなり検索しましたが、それらのすべては何らかの意味でエーテル過剰であるように見えました(任意のプラットフォームで使用できますが、そのためには大量のコードが必要です)またはドキュメントは私がしたいことを説明していませんでした。長い話-ゼロから書いたので、真のGoogle API認証プロセスを理解できます。思ったほど難しくありません。基本的には、 https://developers.google.com/accounts/docs/OAuth2WebServer ガイドラインに従う必要があります。このためには、 https://code.google.com/apis/console/ で登録して資格情報を生成し、リンクを登録する必要もあります。ドメインのみを許可するため、オフィスIPを指す単純なサブドメインを使用しました。
ユーザーのログイン/管理およびセッションの場合、flask http://packages.python.org/Flask-Login/ にこのプラグインを使用しました。それに基づくコード。
まず最初に-インデックスビュー:
from flask import render_template
from flask.ext.login import current_user
from flask.views import MethodView
from myapp import app
class Index(MethodView):
def get(self):
# check if user is logged in
if not current_user.is_authenticated():
return app.login_manager.unauthorized()
return render_template('index.html')
そのため、ユーザーが認証されるまで、このビューは開きません。ユーザーについて話す-ユーザーモデル:
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy import Column, Integer, DateTime, Boolean, String
from flask.ext.login import UserMixin
from myapp.metadata import Session, Base
class User(Base):
__table= 'myapp_users'
id = Column(Integer, primary_key=True)
email = Column(String(80), unique=True, nullable=False)
username = Column(String(80), unique=True, nullable=False)
def __init__(self, email, username):
self.email = email
self.username = username
def __repr__(self):
return "<User('%d', '%s', '%s')>" \
% (self.id, self.username, self.email)
@classmethod
def get_or_create(cls, data):
"""
data contains:
{u'family_name': u'Surname',
u'name': u'Name Surname',
u'picture': u'https://link.to.photo',
u'locale': u'en',
u'gender': u'male',
u'email': u'[email protected]',
u'birthday': u'0000-08-17',
u'link': u'https://plus.google.com/id',
u'given_name': u'Name',
u'id': u'Google ID',
u'verified_email': True}
"""
try:
#.one() ensures that there would be just one user with that email.
# Although database should prevent that from happening -
# lets make it buletproof
user = Session.query(cls).filter_by(email=data['email']).one()
except NoResultFound:
user = cls(
email=data['email'],
username=data['given_name'],
)
Session.add(user)
Session.commit()
return user
def is_active(self):
return True
def is_authenticated(self):
"""
Returns `True`. User is always authenticated. Herp Derp.
"""
return True
def is_anonymous(self):
"""
Returns `False`. There are no Anonymous here.
"""
return False
def get_id(self):
"""
Assuming that the user object has an `id` attribute, this will take
that and convert it to `unicode`.
"""
try:
return unicode(self.id)
except AttributeError:
raise NotImplementedError("No `id` attribute - override get_id")
def __eq__(self, other):
"""
Checks the equality of two `UserMixin` objects using `get_id`.
"""
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
"""
Checks the inequality of two `UserMixin` objects using `get_id`.
"""
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
おそらくUserMixinに何か問題があるかもしれませんが、私は後者を扱います。ユーザーモデルの外観は異なりますが、フラスコログインと互換性を持たせるだけです。
残っているもの-それ自体を認証します。ログインビューがflask-login
である'login'
を設定しました。 Login
ビューは、googleを指すログインボタンでhtmlをレンダリングします-GoogleはAuth
ビューにリダイレクトします。ログインしているユーザー専用のWebサイトの場合は、ユーザーをgoogleにリダイレクトするだけで可能です。
import logging
import urllib
import urllib2
import json
from flask import render_template, url_for, request, redirect
from flask.views import MethodView
from flask.ext.login import login_user
from myapp import settings
from myapp.models import User
logger = logging.getLogger(__name__)
class Login(BaseViewMixin):
def get(self):
logger.debug('GET: %s' % request.args)
params = {
'response_type': 'code',
'client_id': settings.GOOGLE_API_CLIENT_ID,
'redirect_uri': url_for('auth', _external=True),
'scope': settings.GOOGLE_API_SCOPE,
'state': request.args.get('next'),
}
logger.debug('Login Params: %s' % params)
url = settings.GOOGLE_OAUTH2_URL + 'auth?' + urllib.urlencode(params)
context = {'login_url': url}
return render_template('login.html', **context)
class Auth(MethodView):
def _get_token(self):
params = {
'code': request.args.get('code'),
'client_id': settings.GOOGLE_API_CLIENT_ID,
'client_secret': settings.GOOGLE_API_CLIENT_SECRET,
'redirect_uri': url_for('auth', _external=True),
'grant_type': 'authorization_code',
}
payload = urllib.urlencode(params)
url = settings.GOOGLE_OAUTH2_URL + 'token'
req = urllib2.Request(url, payload) # must be POST
return json.loads(urllib2.urlopen(req).read())
def _get_data(self, response):
params = {
'access_token': response['access_token'],
}
payload = urllib.urlencode(params)
url = settings.GOOGLE_API_URL + 'userinfo?' + payload
req = urllib2.Request(url) # must be GET
return json.loads(urllib2.urlopen(req).read())
def get(self):
logger.debug('GET: %s' % request.args)
response = self._get_token()
logger.debug('Google Response: %s' % response)
data = self._get_data(response)
logger.debug('Google Data: %s' % data)
user = User.get_or_create(data)
login_user(user)
logger.debug('User Login: %s' % user)
return redirect(request.args.get('state') or url_for('index'))
したがって、すべてが2つの部分に分割されます。1つは_get_token
でGoogleトークンを取得するためのものです。それを使用し、_get_data
の基本的なユーザーデータを取得するためのその他。
私の設定ファイルには以下が含まれます:
GOOGLE_API_CLIENT_ID = 'myid.apps.googleusercontent.com'
GOOGLE_API_CLIENT_SECRET = 'my secret code'
GOOGLE_API_SCOPE = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
GOOGLE_OAUTH2_URL = 'https://accounts.google.com/o/oauth2/'
GOOGLE_API_URL = 'https://www.googleapis.com/oauth2/v1/'
ビューにはアプリにURLパスをアタッチする必要があるため、このurls.py
ファイルを使用して、ビューをより簡単に追跡し、flask app作成ファイル:
from myapp import app
from myapp.views.auth import Login, Auth
from myapp.views.index import Index
urls = {
'/login/': Login.as_view('login'),
'/auth/': Auth.as_view('auth'),
'/': Index.as_view('index'),
}
for url, view in urls.iteritems():
app.add_url_rule(url, view_func=view)
これらすべてが合わさって、FlaskでGoogleの承認が機能します。コピーして貼り付けた場合-フラスコログインドキュメントとSQLAlchemyマッピングの修正が必要になる場合がありますが、アイデアはそこにあります。
Authomatic を試してください(私はそのプロジェクトのメンテナーです)。非常に使いやすく、any Python framework)で動作し、をサポートします16 OAuth 2.0、)10 OAuth 1.0aプロバイダーおよびOpenID。
以下に、Googleでユーザーを認証し、YouTubeビデオのリストを取得する方法の簡単な例を示します。
# main.py
from flask import Flask, request, make_response, render_template
from authomatic.adapters import WerkzeugAdapter
from authomatic import Authomatic
from authomatic.providers import oauth2
CONFIG = {
'google': {
'class_': oauth2.Google,
'consumer_key': '########################',
'consumer_secret': '########################',
'scope': oauth2.Google.user_info_scope + ['https://gdata.youtube.com'],
},
}
app = Flask(__name__)
authomatic = Authomatic(CONFIG, 'random secret string for session signing')
@app.route('/login/<provider_name>/', methods=['GET', 'POST'])
def login(provider_name):
response = make_response()
# Authenticate the user
result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
if result:
videos = []
if result.user:
# Get user info
result.user.update()
# Talk to Google YouTube API
if result.user.credentials:
response = result.provider.access('https://gdata.youtube.com/'
'feeds/api/users/default/playlists?alt=json')
if response.status == 200:
videos = response.data.get('feed', {}).get('entry', [])
return render_template(user_name=result.user.name,
user_email=result.user.email,
user_id=result.user.id,
youtube_videos=videos)
return response
if __== '__main__':
app.run(debug=True)
また、非常に単純な Flaskチュートリアル があり、FacebookおよびTwitterでユーザーを認証し、APIにアクセスしてユーザーのニュースフィードを読む方法を示しています。
Flask-Dance は、Flask、Requests、およびOAuthlibをリンクする新しいライブラリです。美しいAPIがあり、Google認証の組み込みサポートがあります 開始方法のクイックスタートと共に 。試してみる!
Rauth の代わりに Requests-OAuthlib を使用するために、受け入れられた回答を移植することができました。この記事の執筆時点では、パッケージの最後のコミットは2019年6月で、現在30K以上のリポジトリで使用されていました。
インストールするには、次を実行します:
$ pip install requests_oauthlib
注意、 OAUTHLIB_RELAX_TOKEN_SCOPE
環境変数をTrue
に設定して、抑制します スコープが警告を変更しました 。 Windowsでは、次を実行することでこれを実行できます。
$ set OAUTHLIB_RELAX_TOKEN_SCOPE=1
...
from requests_oauthlib import OAuth2Session
from urllib.request import urlopen
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleLogin, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope=self.openid_config["scopes_supported"]
)
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
return redirect(auth_url)
def callback(self):
if "code" not in request.args:
return None, None
self.session.fetch_token(
token_url=self.openid_config["token_endpoint"],
code=request.args["code"],
client_secret=self.consumer_secret,
)
me = self.session.get(self.openid_config["userinfo_endpoint"]).json()
return me["name"], me["email"]
Requests-OAuthlibのドキュメントはここにあります https://requests-oauthlib.readthedocs.io/en/latest/index.html 。
Flask-oauthは、おそらくflaskの特定の方法に最適です。トークンの更新をサポートしていない限り、Facebookで動作します。そのため、oauth 2. 2。flaskである必要がない場合、requests-oauthを見ることができます。
新しいモジュールFlask-Rauthがこの質問に対する答えであるようです。
Flask-RauthはFlask拡張機能で、これによりOAuth 2.0、OAuth 1.0a、およびOfly [...]これは、Flask-RauthがFlask Webサイト上のユーザーが外部Webサービス(Twitter API、Facebook Graph API、GitHubなど)にサインインできるようにすることを意味します。 )。
参照: Flask-Rauth
Google専用ではありません- https://github.com/lepture/flask-oauthlib で、クライアントとサーバーを実装する方法の例が https:// github。 com/lepture/example-oauth2-server
Oauth2clientは非推奨になったため、bluemoonが提案することをお勧めします。 Bruno Rochaのモデル OAuth2のGoogle認証Flaskは、leptureの堅牢な使用のための良い出発点です Flask-OAuthlib (pip-installable)。模倣してから、ニーズに合わせて拡張することをお勧めします。