在Flask中使用Google OAuth2 [英] Using Google OAuth2 with Flask

查看:291
本文介绍了在Flask中使用Google OAuth2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任何人都可以指出我使用OAuth2和Flask在Google帐户进行身份验证的一个完整示例,而在App Engine上不是

我正尝试让用户访问Google日历,然后使用该访问权限从日历中检索信息并进一步处理。我还需要存储并稍后刷新OAuth2令牌。



我已经查看了Google的 oauth2client 库,并且可以开始跳舞以检索授权码,但是我有点迷路了。查看Google的OAuth 2.0 Playground我知道我需要刷新令牌和访问令牌,但是库中提供的示例仅适用于App Engine和Django。



我也尝试使用包含对OAuth2的引用的 Flask的OAuth模块,但是我没有看到也可以在那里交换授权码。

我可以手动编写请求,但是更愿意使用或改编一个现有的python模块,使得请求变得简单,正确处理可能的反应,甚至可能协助存储令牌。

是否有这样的事情?

解决方案

另一个答案提到 Flask-Rauth ,但没有详细说明如何使用它。有几个谷歌具体的陷阱,但我已经实现了它,最后它运作良好。我将它与Flask-Login整合在一起,这样我就可以用像 @login_required 这样有用的糖来装饰我的视图。我希望能够支持多个OAuth2提供程序,所以部分代码是通用的,并且基于Miguel Grinberg关于在Facebook和Twitter上支持OAuth2的优秀文章这里

首先,添加您的特定Google

  GOOGLE_LOGIN_CLIENT_ID =< your-id-ending-with> .apps.googleusercontent .com
GOOGLE_LOGIN_CLIENT_SECRET =< your-secret>
$ b $ 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 $ b $ from rauth import OAuth2Service 

import json,urllib2

class OAuthSignIn(object):
providers = None
$ b $ 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
$ b $ def get_callback_url (self):
return url_for('oauth_callback',provider = self.provider_name,
_external = True)

@classmethod
def get_provider(self,provider_name) :
如果self.providers是None:
self.providers = {}
为self___中的provider_class子类__():
provider = provider_class()
self。提供者[provider.provider_name] = provider
返回self.providers [provider_name]
$ b $ 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($ b $ scope ='email',
response_type ='code',
redirect_uri = self.get_callback_url())

$ b $如果'code'不在request.args中:

返回None,None,None
oauth_session = self.service.get_auth_session(
data = { 'code':request.args ['code'],
'grant_type':'authorization_code',
're ()')。$($'$)$' (me ['name'],
me ['email'])

一个通用的 OAuthSignIn 类,可以被子类化。 Google子类从Google发布的信息列表中获取信息(以JSON格式此处)。这是可以改变的信息,所以这种方法将确保它始终是最新的。其中一个限制是,如果在Flask应用程序初始化(导入的模块)时,如果服务器上没有Internet连接,则不会正确实例化。这应该几乎从来不是一个问题,但是将最后一个已知值存储在配置数据库中以涵盖这种可能性是一个好主意。

最后,该类返回一个元组 callback()函数中的 name,email Google实际上会返回更多信息,包括Google+个人资料(如果有)。检查由 oauth_session.get('')。json()返回的字典以查看全部内容。
如果在 authorize()函数中扩展范围(对于我的应用程序, email 就足够了) ,您可以通过Google API访问更多信息。



接下来,编写视图将它们结合在一起:

  from flask.ext.login import login_user,logout_user,current_user,login_required 

@ app.route('/授权/< provider>')
def oauth_authorize(provider):
#Flask-Login函数
如果不是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)
用户名,email = oauth.callback()
如果email是None:
#我需要一个有效的电子邮件地址作为我的用户标识
flash('认证失败')
返回重定向(url_for('index'))
#查看用户是否已经存在
user = User.query.filter_by(email = email).first()
如果不是user:
#创建用户。尝试并使用由Google返回的名称
#,但如果未设置,请将@电子邮件地址分开。
昵称=用户名
如果昵称为None或昵称==:
nickname = email.split('@')[0]

#在这里做更多的工作,以确保一个独特的绰号,如果你
#要求。
user = User(昵称=昵称,email = email)
db.session.add(用户)
db.session.commit()
#登录用户默认记住他们的下一次访问
#除非他们注销。
login_user(user,remember = True)
return redirect(url_for('index'))

最后,我的 / login 视图和模板就完成了:

  @ app.route('/ login',methods = ['GET','POST'])
def login():
如果g.user不是None和g .user.is_authenticated():
返回重定向(url_for('index'))
返回render_template('login.html',
title ='登录')


$ p $ login $ c> {%extendsbase.html%}

{%block content%}

< div id =sign-in>
< h1>登入< / h1>
< p>
< a href = {{url_for('oauth_authorize',provider ='google')}}>< img src ={{url_for('static',filename ='img / with-google.png')}}/>< / a>
< / div>
{%endblock%}

确保正确的回拨地址已在Google注册,用户只需点击登录页面上的使用Google登录,即可注册并登录。


Can anyone point me to a complete example for authenticating with Google accounts using OAuth2 and Flask, and not on App Engine?

I am trying to have users give access to Google Calendar, and then use that access to retrieve information from the calendar and process it further. I also need to store and later refresh the OAuth2 tokens.

I have looked at Google's oauth2client library and can get the dance started to retrieve the authorization code, but I'm a little lost from there. Looking at Google's OAuth 2.0 Playground I understand that I need to request the refresh token and access token, but the provided examples in the library are for App Engine and Django only.

I have also tried using Flask's OAuth module that contains references to OAuth2, but I don't see any way to exchange the authorization code there either.

I could probably hand code the requests, but would much prefer to use or adapt an existing python module that makes requests easy, properly handles possible responses and maybe even assists in storage of tokens.

Is there such a thing?

解决方案

Another answer mentions Flask-Rauth, but doesn't go into detail about how to use it. There are a few Google-specific gotchas, but I have implemented it finally and it works well. I integrate it with Flask-Login so I can decorate my views with useful sugar like @login_required.

I wanted to be able to support multiple OAuth2 providers, so part of the code is generic and based on Miguel Grinberg's excellent post about supporting OAuth2 with Facebook and Twitter here.

First, add your specific Google authentication information from Google into your app's configuration:

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
        }
}

And when you create your app (in my case, the module's __init__.py):

app = Flask(__name__)
app.config.from_object('config')

In your app module, create 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'])

This creates a generic OAuthSignIn class that can be subclassed. The Google subclass pulls its information from Google's published list of information (in JSON format here). This is information that is subject to change, so this approach will make sure it is always up-to-date. One limitation of this is that if an Internet connection is not available on your server at the time the Flask application is initialized (the module imported), it will not be instantiated correctly. This should almost never be a problem, but storing last-known values in the configuration database to cover this eventuality is a good idea.

Finally, the class returns a tuple of name, email in the callback() function. Google actually returns a lot more information, including the Google+ profile if available. Inspect the dictionary returned by oauth_session.get('').json() to see it all. If in the authorize() function you expand the scope (for my app, email is sufficient), you can get access to even more information through the Google API.

Next, write the views to tie it all together:

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'))

Finally, my /login view and template to make it all happen:

@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 %}

Make sure the correct callback addresses are registered with Google, and the user should simply have to click on "Sign in with Google" on your login page, and it will register them and log them in.

这篇关于在Flask中使用Google OAuth2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆