我正在将Tornado Web服务器用于一个简单的Web应用程序,并想使用OpenID对用户进行身份验证。我是Tornado的新手,我设法使用Node.js的Passport包(首先在Node.js上进行了测试)来使用它,我能够在回调中获得id_token
。
我正在使用OAuth2Mixin
中的tornado.auth
通过用户凭证授予来授权访问,然后在重定向中,我从code
中获取了param。我不知道如何从那里继续: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
回来;这是JWT。我可以解码并获取所需的数据。
更新: 如果需要配置。
{"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"]}
答案 0 :(得分:0)
您需要从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',
)
答案 1 :(得分:0)
我最终使用了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)