perf: OAuth2 authentication supports state security parameters.

This commit is contained in:
Gerry.tan
2026-02-11 09:36:44 +08:00
committed by Jiangjie Bai
parent 957829fa18
commit 1e2dc97156
4 changed files with 32 additions and 2 deletions

View File

@@ -69,13 +69,25 @@ class OAuth2Backend(RedirectAuthBackend):
response_data = response_data['data']
return response_data
def authenticate(self, request, code=None):
def authenticate(self, request, code=None, state=None):
log_prompt = "Process authenticate [OAuth2Backend]: {}"
logger.debug(log_prompt.format('Start'))
if code is None:
logger.error(log_prompt.format('code is missing'))
return None
if settings.AUTH_OAUTH2_USE_STATE:
if state is None:
logger.error(log_prompt.format('state is missing'))
return None
session_state = request.session.get('oauth2_state')
if not session_state or session_state != state:
logger.error(log_prompt.format('state parameter mismatch'))
return None
request.session.pop('oauth2_state', None)
query_dict = {
'grant_type': 'authorization_code', 'code': code,
'redirect_uri': build_absolute_uri(

View File

@@ -1,3 +1,4 @@
import uuid
from django.conf import settings
from django.contrib import auth
from django.http import HttpResponseRedirect
@@ -37,6 +38,11 @@ class OAuth2AuthRequestView(View):
'redirect_uri': redirect_uri
}
if settings.AUTH_OAUTH2_USE_STATE:
state = uuid.uuid4()
request.session['oauth2_state'] = state
query_dict['state'] = state
if '?' in settings.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT:
separator = '&'
else:
@@ -60,9 +66,19 @@ class OAuth2AuthCallbackView(View, FlashMessageMixin):
logger.debug(log_prompt.format('Start'))
callback_params = request.GET
state = None
if settings.AUTH_OAUTH2_USE_STATE:
state = callback_params.get('state')
session_state = request.session.get('oauth2_state')
if not state or not session_state or session_state != state:
logger.error("Invalid state parameter")
response = self.get_failed_response('/', title=_('OAuth2 Error'), msg="Invalid state parameter")
return response
request.session.pop('oauth2_state', None)
if 'code' in callback_params:
logger.debug(log_prompt.format('Process authenticate'))
user = authenticate(code=callback_params['code'], request=request)
user = authenticate(code=callback_params['code'], request=request, state=state)
if user:
logger.debug(log_prompt.format('Login: {}'.format(user)))

View File

@@ -446,6 +446,7 @@ class Config(dict):
'AUTH_OAUTH2': False,
'AUTH_OAUTH2_LOGO_PATH': 'img/login_oauth2_logo.png',
'AUTH_OAUTH2_PROVIDER': 'OAuth2',
'AUTH_OAUTH2_USE_STATE': False,
'AUTH_OAUTH2_ALWAYS_UPDATE_USER': True,
'AUTH_OAUTH2_CLIENT_ID': 'client-id',
'AUTH_OAUTH2_SCOPE': '',

View File

@@ -203,6 +203,7 @@ SAML2_LOGOUT_URL_NAME = "authentication:saml2:saml2-logout"
AUTH_OAUTH2 = CONFIG.AUTH_OAUTH2
AUTH_OAUTH2_LOGO_PATH = CONFIG.AUTH_OAUTH2_LOGO_PATH
AUTH_OAUTH2_PROVIDER = CONFIG.AUTH_OAUTH2_PROVIDER
AUTH_OAUTH2_USE_STATE = CONFIG.AUTH_OAUTH2_USE_STATE
AUTH_OAUTH2_ALWAYS_UPDATE_USER = CONFIG.AUTH_OAUTH2_ALWAYS_UPDATE_USER
AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT = CONFIG.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT
AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT = CONFIG.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT