Использование Google OAuth2 с колбой
может ли кто-нибудь указать мне полный пример для аутентификации с помощью учетных записей Google с помощью OAuth2 и Flask, и не на приложения двигателя?
Я пытаюсь заставить пользователей предоставить доступ к календарю Google, а затем использовать этот доступ для получения информации из календаря и дальнейшей обработки. Мне также нужно сохранить и позже обновить маркеры OAuth2.
Я посмотрел на Google oauth2client библиотека и может начать танец получить код авторизации, но я немного потерял от этого. Глядя на игровую площадку Google OAuth 2.0, я понимаю, что мне нужно запросить маркер обновления и маркер доступа, но приведенные примеры в библиотеке предназначены только для App Engine и Django.
Я также пробовал использовать модуль OAuth колбы который содержит ссылки на OAuth2, но я не вижу никакого способа обменять код авторизации там тоже.
Я мог бы, вероятно, ручной код запросы, но предпочел бы использовать или адаптировать существующий модуль python, который упрощает запросы, правильно обрабатывает возможные ответы и, возможно, даже помогает в хранении токенов.
есть ли такая вещь?
8 ответов:
другого ответа упоминает Фляга-Rauth, но не вдаваться в подробности о том, как использовать его. Есть несколько Google-конкретных gotchas, но я реализовал его, наконец, и он хорошо работает. Я интегрирую его с Flask-Login, чтобы я мог украсить свои взгляды полезным сахаром, таким как
@login_required
.я хотел иметь возможность поддерживать несколько поставщиков OAuth2, поэтому часть кода является общей и основана на отличном посте Мигеля Гринберга о поддержке OAuth2 с Facebook и Твиттер здесь.
во-первых, добавьте свою конкретную информацию аутентификации 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 здесь). Это информация, которая может быть изменена, поэтому такой подход позволит убедиться, что она всегда актуальна. Одним из ограничений этого является то, что если подключение к Интернету не доступно на вашем сервере во время инициализации приложения Flask (импортированный модуль), он не будет создан правильно. Это почти никогда не должно быть проблемой, но хранение последних известных значений в базе данных конфигурации для покрытия этой возможности является хорошим идея.наконец, класс возвращает кортеж
name, email
на . Google фактически возвращает гораздо больше информации, в том числе профиль Google+, если он доступен. Проверьте словарь, возвращенныйoauth_session.get('').json()
чтобы увидеть все это. Если вauthorize()
функция вы расширяете область (для моего приложения,далее пишем вид чтобы связать все это вместе:
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')
логин.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" на странице входа в систему, и он зарегистрирует их и войти в систему.
я искал совсем немного об использовании разных библиотек, но все они казались эфиром излишним в некотором смысле (вы можете использовать его на любой платформе, но для этого вам нужна тонна кода) или документация не объяснила, что я хотел. Короче говоря-я написал его с нуля, таким образом понимая процесс аутентификации true Google API. Это не так сложно, как кажется. В основном вам нужно следовать https://developers.google.com/accounts/docs/OAuth2WebServer руководящие принципы и это все. Для этого Вам также нужно будет зарегистрироваться по адресу https://code.google.com/apis/console/ для создания учетных данных и регистрации ссылок. Я использовал простой поддомен, указывающий на мой IP-адрес office, так как он разрешает только домены.
для входа пользователя / управления и сессий я использовал этот плагин для 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): __tablename__ = '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'propper@email.com', 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.
Итак, что осталось-аутентификация это само. Я установил для
flask-login
это представление входа'login'
.Login
просмотр отображает html с кнопкой входа, которая указывает на Гугл - Гугл перенаправляет наAuth
вид. Это должно быть возможно просто перенаправить пользователя в 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'))
таким образом, все разделено на две части - одна для получения токена google в
_get_token
. Другое для его использования и получения основных пользовательских данных в_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-путь, прикрепленный к приложению, поэтому я использую это так что я может отслеживать мои взгляды более легко и импортировать меньше материала в файл создания приложения flask:
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)
все это вместе делает рабочую авторизацию Google в колбе. Если вы скопируете вставить его - это может занять некоторое исправление с помощью flask-login documentation и SQLAlchemy mappings, но идея есть.
дать Authomatic попробовать (я координатор этого проекта). Он очень прост в использовании, работает с любой Python framework и обслуживает 16 OAuth 2.0, 10 OAuth 1.0 a поставщики и 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 __name__ == '__main__': app.run(debug=True)
существует также очень простой фляга учебник который показывает, как аутентифицировать пользователя с помощью Facebook и Twitter и поговорить с их API, чтобы прочитать ленты новостей пользователя.
Фляга-Танец это новая библиотека, которая связывает вместе колбу, запросы и OAuthlib. Он имеет красивый API, и он имеет встроенную поддержку Google auth,вместе с кратким руководством о том, как начать работу. Дайте ему попробовать!
Flask-oauth, вероятно, ваш лучший выбор прямо сейчас для колбы конкретного способа сделать это, насколько я знаю, он не поддерживает обновление токенов, но он будет работать с Facebook, мы используем его для этого, и это oauth 2. Если это не должно быть колба конкретной вы можете посмотреть на запросы-oauth
похоже, что новый модуль Flask-Rauth является ответом на этот вопрос:
Flask-Rauth-это расширения Flask, которые позволяют легко взаимодействовать с приложениями OAuth 2.0, OAuth 1.0 a и ofly. [...] Это означает, что Flask-Rauth позволит пользователям на вашем веб-сайте Flask входить во внешние веб-службы (например, API Twitter, API Facebook Graph, GitHub и т. д.).
посмотреть: Фляга-Rauth
не специально для google -- https://github.com/lepture/flask-oauthlib и у него есть пример того, как реализовать клиент и сервер в https://github.com/lepture/example-oauth2-server
поскольку oauth2client теперь устарел, я рекомендую то, что предлагает bluemoon. модель Бруно Роша аутентификации OAuth2 Google в колбе является хорошей отправной точкой для использования надежного лептура Колба-OAuthlib (pip-installable). Я рекомендую имитировать, а затем расширять в соответствии с вашими потребностями.