write with sheets sheet read gspread google docs conectar con code python oauth oauth-2.0 flask

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í.



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.



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.