python - with - read google sheet javascript
Usar Google OAuth2 con Flask (8)
Como oauth2client ahora está en desuso, recomiendo lo que sugiere Bluemoon. El modelo de OAuth2 de Bruno Rocha La autenticación de Google en Flask es un buen punto de partida para utilizar el robusto Flask-OAuthlib ( Flask-OAuthlib pip) de Flask-OAuthlib . Recomiendo imitar, luego expandir para satisfacer sus necesidades.
¿Alguien puede indicarme un ejemplo completo para autenticar con cuentas de Google usando OAuth2 y Flask, y no en App Engine?
Intento que los usuarios den acceso a Google Calendar y luego utilicen ese acceso para recuperar información del calendario y procesarla más. También necesito almacenar y luego actualizar los tokens de OAuth2.
He oauth2client biblioteca oauth2client de Google y puedo comenzar a bailar para recuperar el código de autorización, pero estoy un poco perdido a partir de ahí. Mirando el patio de juegos OAuth 2.0 de Google Entiendo que necesito solicitar el token de actualización y el token de acceso, pero los ejemplos proporcionados en la biblioteca son solo para App Engine y Django.
También he intentado usar el módulo OAuth de Flask que contiene referencias a OAuth2, pero no veo ninguna forma de intercambiar el código de autorización allí tampoco.
Probablemente podría codificar las solicitudes a mano, pero preferiría usar o adaptar un módulo de python existente que haga que las solicitudes sean fáciles, maneje adecuadamente las posibles respuestas e incluso ayude a almacenar tokens.
¿Hay tal cosa?
Flask-oauth es probablemente tu mejor opción en este momento para una forma específica de matraz de hacerlo, hasta donde yo sé, no es compatible con la actualización de los tokens, pero funcionará con Facebook, lo usaremos para eso y es oauth 2. Si no necesita ser específico del matraz; puede consultar las solicitudes-oauth
He buscado bastante sobre el uso de diferentes bibliotecas, pero todas parecían exageradas en cierto sentido (se puede usar en cualquier plataforma, pero para eso se necesita una tonelada de código) o la documentación no explicaba lo que quería. Para resumir, lo escribí desde cero, entendiendo así el proceso de autentificación de la verdadera API de Google. No es tan difícil como parece. Básicamente, debe seguir las https://developers.google.com/accounts/docs/OAuth2WebServer directrices y eso es todo. Para esto, también deberá registrarse en https://code.google.com/apis/console/ para generar credenciales y registrar sus enlaces. He usado un subdominio simple apuntando a mi IP de oficina ya que solo permite dominios.
Para el inicio de sesión / gestión del usuario y las sesiones, he usado este complemento para el matraz http://packages.python.org/Flask-Login/ - habrá un código basado en eso.
Primero lo primero: vista de índice:
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'')
por lo que esta vista no se abrirá hasta que tengamos un usuario autenticado. Hablando de usuarios - modelo de usuario:
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''[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
Probablemente hay algo mal con UserMixin, pero me ocuparé de esto último. Su modelo de usuario tendrá un aspecto diferente, solo haga que sea compatible con el inicio de sesión en el matraz.
Entonces, ¿qué queda? Autenticación en sí misma. Configuré para el flask-login
que la vista de inicio de sesión es ''login''
. Login
vista de Login
representa el html con el botón de inicio de sesión que apunta a google - google redirige a la vista de Auth
. Debería ser posible simplemente redirigir al usuario a google en caso de que sea un sitio web solo para usuarios que hayan iniciado sesión.
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''))
Entonces, todo está dividido en dos partes: una para obtener el token de google en _get_token
. Otro para usarlo y recuperar datos básicos del usuario en _get_data
.
Mi archivo de configuración contiene:
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/''
Tenga en cuenta que las vistas deben tener una ruta url adjunta a la aplicación, así que he usado este archivo urls.py
para poder seguir mis vistas más fácilmente e importar menos cosas al archivo de creación de la aplicación del matraz:
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)
Todo esto junto hace que funcione la autorización de Google en Flask. Si copia, péguelo; puede llevar algunas reparaciones con la documentación de inicio de sesión de matraz y mapeos de SQLAlchemy, pero la idea está allí.
No específicamente para google - https://github.com/lepture/flask-oauthlib y tiene un ejemplo de cómo implementar el cliente y el servidor en https://github.com/lepture/example-oauth2-server
Parece que el nuevo módulo Flask-Rauth es la respuesta a esta pregunta:
Flask-Rauth es una extensión de Flask que le permite interactuar fácilmente con las aplicaciones habilitadas OAuth 2.0, OAuth 1.0a y Ofly. [...] Esto significa que Flask-Rauth permitirá a los usuarios de su sitio web Flask iniciar sesión en servicios web externos (es decir, la API de Twitter, Facebook Graph API, GitHub, etc.).
Ver: Flask-Rauth
Prueba Authomatic (soy el mantenedor de ese proyecto). Es muy simple de usar, funciona con cualquier framework de Python y es compatible con 16 OAuth 2.0 , 10 proveedores de OAuth 1.0a y OpenID .
Aquí hay un ejemplo simple sobre cómo autenticar a un usuario con Google y obtener su lista de videos de 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)
También hay un tutorial Flask muy simple que muestra cómo autenticar a un usuario por Facebook y Twitter y hablar con sus API para leer las noticias del usuario.
Flask-Dance es una nueva biblioteca que vincula Flask, Requests y OAuthlib. Tiene una hermosa API, y tiene soporte integrado para la autenticación de Google, junto con un inicio rápido sobre cómo comenzar . ¡Darle una oportunidad!
Otra respuesta menciona a Flask-Rauth , pero no entra en detalles sobre cómo usarla. Hay algunos errores específicos de Google, pero lo he implementado finalmente y funciona bien. Lo integro con Flask-Login para poder decorar mis vistas con azúcar útil como @login_required
.
Quería poder dar soporte a múltiples proveedores de OAuth2, por lo que parte del código es genérico y está basado en la excelente publicación de Miguel Grinberg sobre el soporte de OAuth2 con Facebook y Twitter here .
Primero, agregue su información específica de autenticación de Google desde Google a la configuración de su aplicación:
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
}
}
Y cuando creas tu aplicación (en mi caso, la __init__.py
del módulo):
app = Flask(__name__)
app.config.from_object(''config'')
En su módulo de aplicación, cree 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''])
Esto crea una clase OAuthSignIn
genérica que se puede subclasificar. La subclase Google extrae su información de la lista de información publicada de Google (en formato JSON here ). Esta es información que está sujeta a cambios, por lo que este enfoque garantizará que esté siempre actualizada. Una limitación de esto es que si una conexión a Internet no está disponible en su servidor en el momento en que se inicializa la aplicación Flask (el módulo importado), no se creará una instancia correctamente. Esto casi nunca debería ser un problema, pero almacenar los últimos valores conocidos en la base de datos de configuración para cubrir esta eventualidad es una buena idea.
Finalmente, la clase devuelve una tupla de name, email
en la función callback()
. Google en realidad devuelve mucha más información, incluido el perfil de Google+ si está disponible. Inspeccione el diccionario devuelto por oauth_session.get('''').json()
para verlo todo. Si en la función authorize()
amplía el alcance (para mi aplicación, el email
es suficiente), puede obtener acceso a aún más información a través de la API de Google.
A continuación, escriba las vistas para unir todo:
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''))
Finalmente, mi vista y plantilla de /login
para que todo suceda:
@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 %}
Asegúrese de que las direcciones de devolución de llamada correctas estén registradas en Google, y el usuario simplemente debe hacer clic en "Iniciar sesión con Google" en su página de inicio de sesión, y las registrará e iniciará sesión.