[英]How to authenticate using OpenID in Tornado?
I'm using Tornado web server for a simple web application, and want to authenticate the user using OpenID. 我将Tornado Web服务器用于简单的Web应用程序,并希望使用OpenID对用户进行身份验证。 I'm new to Tornado, and I managed to work it using Node.js' Passport package (was testing on Node.js first), which I was able to get the
id_token
in the callback. 我是Tornado的新手,我设法使用Node.js的Passport包(首先在Node.js上进行了测试)来使用它,我能够在回调
id_token
中获取id_token
。
I'm using OAuth2Mixin
from tornado.auth
to authorize the access using user credential grant, then on the redirect, I'm getting the from code
get param. 我使用
OAuth2Mixin
从tornado.auth
授权使用用户证书授予访问,然后重定向,我得到了从code
获取PARAM。 I don't know how to continue from there :D 我不知道如何从那里继续: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',
)
Then on the other handler. 然后在另一个处理程序上。
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')
I would expect the id_token
back; 我希望
id_token
回来; which is a JWT. 这是JWT。 I can decode it and get the data needed.
我可以解码并获取所需的数据。
UPDATE: In case the configurations are needed. 更新:如果需要配置。
{"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"]}
You'll need to call get_authenticated_user
from SuccessLoginHandler
to get the access token. 您需要从
SuccessLoginHandler
调用get_authenticated_user
来获取访问令牌。
But, I'd rather write everything in a single handler to keep code shorter and non repetitive. 但是,我宁愿将所有内容都写在一个处理程序中,以使代码更短且不重复。 You can rewrite
LoginHandler
like this: 您可以这样重写
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',
)
I ended up using Tornado's httpclient
to send the requests to the OpenID server. 我最终使用了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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.