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

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

问题描述

谁能告诉我一个完整的示例,用于使用 OAuth2 和 Flask 对 Google 帐户进行身份验证,而不是在 App Engine 上?

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

我试图让用户授予对 Google 日历的访问权限,然后使用该访问权限从日历中检索信息并对其进行进一步处理.我还需要存储 OAuth2 令牌并稍后刷新.

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.

我查看了 Google 的 oauth2client 库和可以让舞蹈开始检索授权码,但我从那里有点迷失了.查看 Google 的 OAuth 2.0 Playground 我明白我需要请求刷新令牌和访问令牌,但库中提供的示例仅适用于 App Engine 和 Django.

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.

我也尝试使用 Flask 的 OAuth 模块,其中包含对 OAuth2 的引用,但我没有也看不到有什么方法可以在那里交换授权码.

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.

我可能可以手动编码请求,但更喜欢使用或调整现有的 python 模块,使请求变得简单,正确处理可能的响应,甚至可能有助于存储令牌.

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.

有这种东西吗?

推荐答案

另一个答案提到了 Flask-Rauth,但没有详细说明如何使用它.有一些特定于 Google 的陷阱,但我最终实现了它并且运行良好.我将它与 Flask-Login 集成在一起,这样我就可以用像 @login_required 这样有用的糖来装饰我的视图.

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.

我希望能够支持多个 OAuth2 提供者,因此部分代码是通用的,并且基于 Miguel Grinberg 关于通过 Facebook 和 Twitter 支持 OAuth2 的优秀帖子这里.

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.

首先,将您从 Google 获取的特定 Google 身份验证信息添加到您的应用配置中:

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

当你创建你的应用程序时(在我的例子中,模块的 __init__.py):

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

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

在您的应用模块中,创建auth.py:

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

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

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.

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

接下来,编写视图以将它们联系在一起:

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:

最后,我的 /login 视图和模板让这一切发生:

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:

登录.html:

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 登录"即可注册并登录.

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天全站免登陆