mirror of https://github.com/jumpserver/jumpserver
Merge pull request #9201 from jumpserver/pr@v3@perf_support_openid_pkce
perf: OpenID支持PKCE方式对接pull/9205/head
commit
b6d6c54d8f
|
@ -88,7 +88,7 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ssl_verification
|
@ssl_verification
|
||||||
def authenticate(self, request, nonce=None, **kwargs):
|
def authenticate(self, request, nonce=None, code_verifier=None, **kwargs):
|
||||||
""" Authenticates users in case of the OpenID Connect Authorization code flow. """
|
""" Authenticates users in case of the OpenID Connect Authorization code flow. """
|
||||||
log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
|
log_prompt = "Process authenticate [OIDCAuthCodeBackend]: {}"
|
||||||
logger.debug(log_prompt.format('start'))
|
logger.debug(log_prompt.format('start'))
|
||||||
|
@ -134,6 +134,8 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
||||||
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
|
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if settings.AUTH_OPENID_PKCE and code_verifier:
|
||||||
|
token_payload['code_verifier'] = code_verifier
|
||||||
if settings.AUTH_OPENID_CLIENT_AUTH_METHOD == 'client_secret_post':
|
if settings.AUTH_OPENID_CLIENT_AUTH_METHOD == 'client_secret_post':
|
||||||
token_payload.update({
|
token_payload.update({
|
||||||
'client_id': settings.AUTH_OPENID_CLIENT_ID,
|
'client_id': settings.AUTH_OPENID_CLIENT_ID,
|
||||||
|
|
|
@ -9,7 +9,10 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
import time
|
import time
|
||||||
|
import secrets
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
|
@ -38,6 +41,19 @@ class OIDCAuthRequestView(View):
|
||||||
|
|
||||||
http_method_names = ['get', ]
|
http_method_names = ['get', ]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gen_code_verifier(length=128):
|
||||||
|
# length range 43 ~ 128
|
||||||
|
return secrets.token_urlsafe(length - 32)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gen_code_challenge(code_verifier, code_challenge_method):
|
||||||
|
if code_challenge_method == 'plain':
|
||||||
|
return code_verifier
|
||||||
|
h = hashlib.sha256(code_verifier.encode('ascii')).digest()
|
||||||
|
b = base64.urlsafe_b64encode(h)
|
||||||
|
return b.decode('ascii')[:-1]
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
""" Processes GET requests. """
|
""" Processes GET requests. """
|
||||||
|
|
||||||
|
@ -56,6 +72,16 @@ class OIDCAuthRequestView(View):
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if settings.AUTH_OPENID_PKCE:
|
||||||
|
code_verifier = self.gen_code_verifier()
|
||||||
|
code_challenge_method = settings.AUTH_OPENID_CODE_CHALLENGE_METHOD or 'S256'
|
||||||
|
code_challenge = self.gen_code_challenge(code_verifier, code_challenge_method)
|
||||||
|
authentication_request_params.update({
|
||||||
|
'code_challenge_method': code_challenge_method,
|
||||||
|
'code_challenge': code_challenge
|
||||||
|
})
|
||||||
|
request.session['oidc_auth_code_verifier'] = code_verifier
|
||||||
|
|
||||||
# States should be used! They are recommended in order to maintain state between the
|
# States should be used! They are recommended in order to maintain state between the
|
||||||
# authentication request and the callback.
|
# authentication request and the callback.
|
||||||
if settings.AUTH_OPENID_USE_STATE:
|
if settings.AUTH_OPENID_USE_STATE:
|
||||||
|
@ -138,8 +164,9 @@ class OIDCAuthCallbackView(View):
|
||||||
|
|
||||||
# Authenticates the end-user.
|
# Authenticates the end-user.
|
||||||
next_url = request.session.get('oidc_auth_next_url', None)
|
next_url = request.session.get('oidc_auth_next_url', None)
|
||||||
|
code_verifier = request.session.get('oidc_auth_code_verifier', None)
|
||||||
logger.debug(log_prompt.format('Process authenticate'))
|
logger.debug(log_prompt.format('Process authenticate'))
|
||||||
user = auth.authenticate(nonce=nonce, request=request)
|
user = auth.authenticate(nonce=nonce, request=request, code_verifier=code_verifier)
|
||||||
if user and user.is_valid:
|
if user and user.is_valid:
|
||||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||||
auth.login(self.request, user)
|
auth.login(self.request, user)
|
||||||
|
|
|
@ -270,6 +270,8 @@ class Config(dict):
|
||||||
'AUTH_OPENID_USER_ATTR_MAP': {
|
'AUTH_OPENID_USER_ATTR_MAP': {
|
||||||
'name': 'name', 'username': 'preferred_username', 'email': 'email'
|
'name': 'name', 'username': 'preferred_username', 'email': 'email'
|
||||||
},
|
},
|
||||||
|
'AUTH_OPENID_PKCE': False,
|
||||||
|
'AUTH_OPENID_CODE_CHALLENGE_METHOD': 'S256',
|
||||||
|
|
||||||
# OpenID 新配置参数 (version >= 1.5.9)
|
# OpenID 新配置参数 (version >= 1.5.9)
|
||||||
'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
|
'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
|
||||||
|
|
|
@ -77,6 +77,8 @@ AUTH_OPENID_USE_NONCE = CONFIG.AUTH_OPENID_USE_NONCE
|
||||||
AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
|
AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
|
||||||
AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
|
AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
|
||||||
AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
|
AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
|
||||||
|
AUTH_OPENID_PKCE = CONFIG.AUTH_OPENID_PKCE
|
||||||
|
AUTH_OPENID_CODE_CHALLENGE_METHOD = CONFIG.AUTH_OPENID_CODE_CHALLENGE_METHOD
|
||||||
AUTH_OPENID_USER_ATTR_MAP = CONFIG.AUTH_OPENID_USER_ATTR_MAP
|
AUTH_OPENID_USER_ATTR_MAP = CONFIG.AUTH_OPENID_USER_ATTR_MAP
|
||||||
AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
|
AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
|
||||||
AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
|
AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
|
||||||
|
|
|
@ -38,6 +38,11 @@ class CommonSettingSerializer(serializers.Serializer):
|
||||||
help_text=_('User attr map present how to map OpenID user attr to '
|
help_text=_('User attr map present how to map OpenID user attr to '
|
||||||
'jumpserver, username,name,email is jumpserver attr')
|
'jumpserver, username,name,email is jumpserver attr')
|
||||||
)
|
)
|
||||||
|
AUTH_OPENID_PKCE = serializers.BooleanField(required=False, label=_('Enable PKCE'))
|
||||||
|
AUTH_OPENID_CODE_CHALLENGE_METHOD = serializers.ChoiceField(
|
||||||
|
default='S256', label=_('Code challenge method'),
|
||||||
|
choices=(('S256', 'HS256'), ('plain', 'Plain'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class KeycloakSettingSerializer(CommonSettingSerializer):
|
class KeycloakSettingSerializer(CommonSettingSerializer):
|
||||||
|
|
Loading…
Reference in New Issue