【问题标题】:How to authenticate using OpenID in Tornado?如何在 Tornado 中使用 OpenID 进行身份验证?
【发布时间】:2019-04-25 13:14:18
【问题描述】:

我将 Tornado Web 服务器用于一个简单的 Web 应用程序,并希望使用 OpenID 对用户进行身份验证。我是 Tornado 的新手,我设法使用 Node.js 的 Passport 包(首先在 Node.js 上进行测试)来工作,我能够在回调中获得 id_token

我使用来自tornado.authOAuth2Mixin 来授权使用用户凭据授予的访问权限,然后在重定向时,我从code 获取参数。我不知道如何从那里继续:D

from tornado.auth import OpenIdMixin, OAuth2Mixin
from .base import BaseHandler

class LoginHandler(BaseHandler, OAuth2Mixin, OpenIdMixin):
  def get(self):
    self._OAUTH_AUTHORIZE_URL = 'https://authserver.io/uas/oauth2/authorization'
    self._OAUTH_ACCESS_TOKEN_URL = 'https://authserver.io/uas/oauth2/token'
    self.authorize_redirect(
      redirect_uri='http://localhost:3001/success-login',
      client_id='abcd',
      client_secret='1234',
    )

然后在另一个处理程序上。

from tornado.auth import OpenIdMixin, OAuth2Mixin
import tornado.httpclient
from .base import BaseHandler

class SuccessLoginHandler(BaseHandler, OpenIdMixin, OAuth2Mixin):
  async def get(self):
    code = self.get_argument('code', None)
    if code is not None:
      return self.write(code)

    self.write('no code')

我希望id_token 回来;这是一个智威汤逊。我可以对其进行解码并获取所需的数据。

更新: 以防需要配置。

{"issuer":"https://authserver.io/uas","authorization_endpoint":"https://authserver.io/uas/oauth2/authorization","token_endpoint":"https://authserver.io/uas/oauth2/token","userinfo_endpoint":"https://authserver.io/uas/oauth2/userinfo","jwks_uri":"https://authserver.io/uas/oauth2/metadata.jwks","tokeninfo_endpoint":"https://authserver.io/uas/oauth2/introspection","introspection_endpoint":"https://authserver.io/uas/oauth2/introspection","revocation_endpoint":"https://authserver.io/uas/oauth2/revocation","response_types_supported":["code"],"grant_types_supported":["authorization_code","password","refresh_token","urn:ietf:params:oauth:grant-type:saml2-bearer","http://globalsign.com/iam/sso/oauth2/grant-type/sms-mt-otp","http://globalsign.com/iam/sso/oauth2/grant-type/smtp-otp"],"subject_types_supported":["public"],"request_object_signing_alg_values_supported":["RS256","HS256"],"request_object_encryption_alg_values_supported":["RSA-OAEP","RSA1_5","A128KW"],"request_object_encryption_enc_values_supported":["A128GCM","A128CBC-HS256"],"id_token_signing_alg_values_supported":["RS256","HS256"],"id_token_encryption_alg_values_supported":["RSA-OAEP","RSA1_5","A128KW"],"id_token_encryption_enc_values_supported":["A128GCM","A128CBC-HS256"],"userinfo_signing_alg_values_supported":["RS256","HS256"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","RSA1_5","A128KW"],"userinfo_encryption_enc_values_supported":["A128GCM","A128CBC-HS256"],"token_endpoint_auth_methods_supported":["client_secret_post","client_secret_basic","client_secret_jwt","private_key_jwt"],"token_endpoint_auth_signing_alg_values_supported":["RS256","HS256"],"introspection_endpoint_auth_methods_supported":["client_secret_post","client_secret_basic","client_secret_jwt","private_key_jwt"],"introspection_endpoint_auth_signing_alg_values_supported":["RS256","HS256"],"revocation_endpoint_auth_methods_supported":["client_secret_post","client_secret_basic","client_secret_jwt","private_key_jwt"],"revocation_endpoint_auth_signing_alg_values_supported":["RS256","HS256"],"scopes_supported":["openid","userinfo"]}

【问题讨论】:

    标签: python oauth-2.0 tornado openid openid-connect


    【解决方案1】:

    您需要从SuccessLoginHandler 调用get_authenticated_user 以获取访问令牌。

    但是,我宁愿在单个处理程序中编写所有内容,以使代码更短且不重复。你可以像这样重写LoginHandler

    class LoginHandler(BaseHandler, OAuth2Mixin, OpenIdMixin):
    
        _OAUTH_AUTHORIZE_URL = 'https://authserver.io/uas/oauth2/authorization'
        _OAUTH_ACCESS_TOKEN_URL = 'https://authserver.io/uas/oauth2/token'
    
        async def get(self):
            redirect_uri = 'http://localhost:3001/login'
    
            code = self.get_argument('code', None)
    
            if code:
                # if there's `code`, get access token
                user = await self.get_authenticated_user()
    
                # the `user` variable now contains the returned data
                # from the oauth server.
                # you'll probably want to `set_secure_cookie`
                # or do something else to save the user
    
                # then redirect the user to some page
                self.redirect("/") # redirects to home page
                return 
    
            else:
                # otherwise get authorization `code`       
                self.authorize_redirect(
                    redirect_uri=redirec_uri,
                    client_id='abcd',
                    client_secret='1234',
                )
    

    【讨论】:

    • 它抱怨传递给get_authenticated_user 的参数数量。 TypeError: get_authenticated_user() got an unexpected keyword argument 'redirect_uri'
    • 这里是方法定义async def get_authenticated_user( self, http_client: httpclient.AsyncHTTPClient = None )
    • @Qvikie 啊!对于那个很抱歉。只需调用get_authenticated_user,不带任何参数。
    • @Qvikie 另外,如果获取访问令牌需要code,您可能必须覆盖get_authenticated_user 并手动发出HTTP 请求,就像在GoogleOAuth2Mixin 类中所做的那样。跨度>
    • 没用!我遇到了其他一些错误。我必须通过使用tornado.httpclient.AsyncHTTPClient() 发送请求来手动实现这一点。
    【解决方案2】:

    我最终使用 Tornado 的 httpclient 将请求发送到 OpenID 服务器。

    import base64
    import urllib.parse
    import json
    import tornado.httpclient
    
    from .base import BaseHandler
    from settings import OID_AUTH_API, OID_REDIRECT_URI, OID_CLIENT_ID, OID_CLIENT_PASSWORD
    from lib import logger
    
    class LoginHandler(BaseHandler):
        _redirect_uri = urllib.parse.quote(OID_REDIRECT_URI, safe='')
        _scope = 'openid+profile+email'
        _response_type = 'code'
        _http_client = tornado.httpclient.AsyncHTTPClient()
    
        async def get(self):
            try:
                code = self.get_argument('code', None)
    
                if (code is None):
                    self.redirect('%s/authorization?client_id=%s&scope=%s&response_type=%s&redirect_uri=%s' % (
                        OID_AUTH_API, OID_CLIENT_ID, self._scope, self._response_type, self._redirect_uri), self.request.uri)
                    return
    
                # exchange the authorization code with the access token
                grant_type = 'authorization_code'
                redirect_uri = self._redirect_uri
                authorization_header = '%s:%s' % (
                    OID_CLIENT_ID, OID_CLIENT_PASSWORD)
                authorization_header_encoded = base64.b64encode(
                    authorization_header.encode('UTF-8')).decode('UTF-8')
                url = '%s/token?grant_type=%s&code=%s&redirect_uri=%s' % (
                    OID_AUTH_API, grant_type, code, redirect_uri)
                token_exchange_response = await self._http_client.fetch(
                    url,
                    method='POST',
                    headers={
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'Authorization': 'Basic %s' % authorization_header_encoded,
                        'Accept': 'application/json'
                    },
                    body='')
    
                token_exchange_response_body_dict = json.loads(
                    token_exchange_response.body)
    
                access_token = token_exchange_response_body_dict.get(
                    'access_token')
    
                self.send_response({
                    'access_token': access_token
                })
            except tornado.httpclient.HTTPClientError as error:
                logger.log_error(error.response.body.decode('UTF-8'))
    
                self.send_response({
                    'success': False,
                    'message': 'Error occurred while trying to obtain the access token'
                }, 500)
            except Exception as error:
                logger.log_error_with_traceback(error)
                self.send_response({
                    'success': False,
                    'message': 'Internal server error. Please try again later.'
                }, 500)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-13
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 2019-05-03
      • 2021-07-29
      • 2011-06-18
      • 2012-06-17
      相关资源
      最近更新 更多