Skip to content

Commit

Permalink
Adds an optional authorization function for the OIDC authentication.
Browse files Browse the repository at this point in the history
* custom oidc auth module allows an optional parameter OIDC_AUTHORIZATION_FUNCTION
* askbot/deps/django_authopenid/protocols/__init__.py:
  - get_protocol: passes the oidc_authorization_function to the OIDC protocol
* askbot/deps/django_authopenid/protocols/oidc/protocol.py:
  - __init__method gets the authorization function and stores it on self
  - adds method is_user_authorized to check if the user is authorized to access the resource
* askbot/deps/django_authopenid/util.py:
  - LoginMethod: reads the OIDC_AUTHORIZATION_FUNCTION or sets a dummy lambda function returning True
    the function takes the decoded token as an argument and returns a boolean
* askbot/deps/django_authopenid/protocols/oidc/views.py:
  - complete_oidc_signin: checks if the user is authorized to access the resource
    by calling the oidc protocols is_user_authorized method
    allows to continue or return a Bad Request response
  • Loading branch information
evgenyfadeev committed Nov 25, 2024
1 parent 0af5323 commit d2cba17
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 1 deletion.
1 change: 1 addition & 0 deletions askbot/deps/django_authopenid/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def get_protocol(provider_name):
client_id=params['oidc_client_id'],
client_secret=params['oidc_client_secret'],
provider_url=params['oidc_provider_url'],
authorization_function=params['oidc_authorization_function'],
trust_email=params['trust_email'])

raise NotImplementedError(f'Not implemented for protocol {protocol_type}')
11 changes: 11 additions & 0 deletions askbot/deps/django_authopenid/protocols/oidc/protocol.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""OpenId Connect protocol"""
import asyncio
import logging
import requests
from django.core.cache import cache
from askbot.deps.django_authopenid.protocols.oidc.jwt_verifier import JwtVerifier
Expand All @@ -22,12 +23,14 @@ def __init__(self, # pylint: disable=too-many-arguments
client_secret=None,
provider_url=None,
trust_email=False,
authorization_function=None,
audience=None):
self.protocol_type = 'oidc'
self.audience = audience
self.client_id = client_id
self.client_secret = client_secret
self.provider_url = provider_url
self.authorization_function = authorization_function
self.trust_email = trust_email
discovery = self.load_discovery_data()
self.authenticate_url = discovery['authorization_endpoint']
Expand Down Expand Up @@ -84,6 +87,14 @@ def get_email(self, id_token):
payload = get_jwt_payload(id_token)
return payload['email']

def is_user_authorized(self, id_token):
payload = get_jwt_payload(id_token)
try:
return self.authorization_function(payload)
except Exception as error:
logging.critical('Error in authorization function: %s', error)
return False

def is_access_token_valid(self, access_token):
"""Validates the OIDC access token"""
jwt_verifier = JwtVerifier(issuer=self.provider_url,
Expand Down
3 changes: 3 additions & 0 deletions askbot/deps/django_authopenid/protocols/oidc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def complete_oidc_signin(request): #pylint: disable=too-many-return-statements
if not oidc.is_id_token_valid(id_token, auth_csrf_token):
return HttpResponseBadRequest("ID token is invalid")

if not oidc.is_user_authorized(id_token):
return HttpResponseBadRequest("You do not have access to this resource")

user_id = oidc.get_user_id(id_token)

email = oidc.get_email(id_token)
Expand Down
6 changes: 5 additions & 1 deletion askbot/deps/django_authopenid/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ def read_params(self):
self.oidc_client_id = self.get_required_attr('OIDC_CLIENT_ID', for_what)
self.oidc_client_secret = self.get_required_attr('OIDC_CLIENT_SECRET', for_what)
self.oidc_audience = self.get_required_attr('OIDC_AUDIENCE', for_what)
if hasattr(self.mod, 'OIDC_AUTHORIZATION_FUNCTION'):
self.oidc_authorization_function = self.mod.OIDC_AUTHORIZATION_FUNCTION
else:
self.oidc_authorization_function = lambda parsed_token: True

if self.login_type.startswith('openid'):
self.openid_endpoint = self.get_required_attr('OPENID_ENDPOINT', 'custom OpenID login')
Expand All @@ -357,7 +361,7 @@ def as_dict(self):
'check_password', 'auth_endpoint', 'token_endpoint',
'resource_endpoint', 'response_parser', 'token_transport',
'trust_email', 'oidc_provider_url', 'oidc_client_id',
'oidc_client_secret', 'oidc_audience'
'oidc_client_secret', 'oidc_audience', 'oidc_authorization_function'
)
#some parameters in the class have different names from those
#in the dictionary
Expand Down

0 comments on commit d2cba17

Please sign in to comment.