Skip to content

Commit

Permalink
fix: hotfix_3.13.9 decode oidc tokens per issuer (#244)
Browse files Browse the repository at this point in the history
Co-authored-by: hicham <hicham.taroq-ext@aphp.fr>
  • Loading branch information
thicham43 and hicham authored Aug 8, 2023
1 parent 2e14048 commit 4b04a22
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .conf/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ JWT_ALGORITHMS=
ID_CHECKER_URL=

OIDC_SERVER_MASTER_URL=
OIDC_SERVER_APPLICATIFS_URL=
OIDC_SERVER_APHP_URL=
OIDC_AUDIENCES=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
Expand Down
2 changes: 1 addition & 1 deletion .conf/.test.env
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ JWT_ALGORITHMS=
ID_CHECKER_URL=

OIDC_SERVER_MASTER_URL=
OIDC_SERVER_APPLICATIFS_URL=
OIDC_SERVER_APHP_URL=
OIDC_AUDIENCES=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
Expand Down
3 changes: 1 addition & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ deploy-job:
stage: deploy
script:
- export VERSION=$(cat ./admin_cohort/__init__.py | grep __version__ | head -1 | awk -F'=' '{ print $2 }' | sed "s/[' ]//g")
- cat ${CI_PROJECT_DIR}/Dockerfile | sed -e 's|^FROM |FROM harbor.eds.aphp.fr/cohort360/|g' > ${CI_PROJECT_DIR}/Dockerfile_fixed
- mkdir -p /kaniko/.docker
- |-
KANIKOPROXYARGS=""
Expand All @@ -81,7 +80,7 @@ deploy-job:
- >-
/kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile_fixed"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
$KANIKOPROXYARGS
--destination "${CI_REGISTRY_IMAGE}:${VERSION}"
only:
Expand Down
2 changes: 1 addition & 1 deletion admin_cohort/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = 'Portail/Cohort360 API'
__version__ = '3.13.8'
__version__ = '3.13.9'
__author__ = 'Assistance Publique - Hopitaux de Paris, Département I&D'


Expand Down
4 changes: 2 additions & 2 deletions admin_cohort/auth/auth_class.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from rest_framework.authentication import BaseAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken

from admin_cohort.auth.utils import get_userinfo_from_token, get_auth_data
from admin_cohort.models import User
from admin_cohort.settings import JWT_AUTH_MODE
from admin_cohort.types import TokenVerificationError


class Authentication(BaseAuthentication):
Expand All @@ -19,6 +19,6 @@ def authenticate(self, request):
try:
user_info = get_userinfo_from_token(token=access_token, auth_method=auth_method)
user = User.objects.get(provider_username=user_info.username)
except (TokenVerificationError, User.DoesNotExist):
except (InvalidToken, ValueError, User.DoesNotExist):
return None
return user, access_token
40 changes: 21 additions & 19 deletions admin_cohort/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@

OIDC_SERVER_MASTER_URL = env("OIDC_SERVER_MASTER_URL")

OIDC_SERVER_APPLICATIFS_URL = env("OIDC_SERVER_APPLICATIFS_URL")
OIDC_SERVER_TOKEN_URL = f"{OIDC_SERVER_APPLICATIFS_URL}/protocol/openid-connect/token"
OIDC_SERVER_USERINFO_URL = f"{OIDC_SERVER_APPLICATIFS_URL}/protocol/openid-connect/userinfo"
OIDC_SERVER_LOGOUT_URL = f"{OIDC_SERVER_APPLICATIFS_URL}/protocol/openid-connect/logout"
OIDC_SERVER_APHP_URL = env("OIDC_SERVER_APHP_URL")
OIDC_SERVER_TOKEN_URL = f"{OIDC_SERVER_APHP_URL}/protocol/openid-connect/token"
OIDC_SERVER_USERINFO_URL = f"{OIDC_SERVER_APHP_URL}/protocol/openid-connect/userinfo"
OIDC_SERVER_LOGOUT_URL = f"{OIDC_SERVER_APHP_URL}/protocol/openid-connect/logout"

OIDC_AUDIENCES = env("OIDC_AUDIENCES").split(';')
OIDC_CLIENT_ID = env("OIDC_CLIENT_ID")
Expand Down Expand Up @@ -130,12 +130,13 @@ def decode_oidc_token(token: str, issuer: str):
audience=OIDC_AUDIENCES)


def verify_oidc_token_for_issuer(token: str, issuer: str):
try:
return decode_oidc_token(token=token, issuer=issuer)
except Exception as e:
_logger_err.error(f"Error decoding token for issuer `{issuer}` - {e}")
return None
def get_token_issuer(token: str) -> str:
decoded = jwt.decode(jwt=token,
algorithms=JWT_ALGORITHMS,
options={'verify_signature': False})
issuer = decoded.get("iss")
assert issuer in (OIDC_SERVER_APHP_URL, OIDC_SERVER_MASTER_URL), f"Unknown issuer: `{issuer}`"
return issuer


def get_userinfo_from_token(token: str, auth_method: str) -> Union[None, UserInfo]:
Expand All @@ -159,15 +160,16 @@ def get_userinfo_from_token(token: str, auth_method: str) -> Union[None, UserInf
except User.DoesNotExist as e:
raise ServerError(f"Error verifying token. User not found - {e}")
elif auth_method == OIDC_AUTH_MODE:
for issuer in (OIDC_SERVER_APPLICATIFS_URL, OIDC_SERVER_MASTER_URL):
decoded = verify_oidc_token_for_issuer(token=token,
issuer=issuer)
if decoded:
return UserInfo(username=decoded.get('preferred_username'),
firstname=decoded.get('name'),
lastname=decoded.get('family_name'),
email=decoded.get('email'))
raise InvalidToken("Invalid OIDC Token: unknown issuer")
try:
issuer = get_token_issuer(token=token)
decoded = decode_oidc_token(token=token, issuer=issuer)
return UserInfo(username=decoded.get('preferred_username'),
firstname=decoded.get('name'),
lastname=decoded.get('family_name'),
email=decoded.get('email'))
except Exception as e:
_logger_err.error(f"Error decoding token: {e} - `{token}`")
raise InvalidToken()
else:
raise ValueError(f"Invalid authentication method : {auth_method}")

Expand Down
7 changes: 4 additions & 3 deletions admin_cohort/tests/tests_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from requests import Response
from rest_framework import status
from rest_framework.test import APITestCase, APIRequestFactory
from rest_framework_simplejwt.exceptions import InvalidToken

from admin_cohort.models import User
from admin_cohort.settings import JWT_AUTH_MODE, OIDC_AUTH_MODE
from admin_cohort.types import JwtTokens, LoginError, ServerError, UserInfo, TokenVerificationError
from admin_cohort.types import JwtTokens, LoginError, ServerError, UserInfo
from admin_cohort.views import UserViewSet, token_refresh_view


Expand Down Expand Up @@ -118,7 +119,7 @@ def test_authenticate_without_token(self):

@mock.patch("admin_cohort.auth.auth_class.get_userinfo_from_token")
def test_authenticate_error(self, mock_get_userinfo: MagicMock):
mock_get_userinfo.side_effect = TokenVerificationError()
mock_get_userinfo.side_effect = InvalidToken()
request = self.factory.get(path=self.protected_url, **self.headers)
response = self.protected_view.as_view({'get': 'list'})(request)
mock_get_userinfo.assert_called()
Expand All @@ -128,7 +129,7 @@ def test_authenticate_error(self, mock_get_userinfo: MagicMock):
@mock.patch("admin_cohort.auth.auth_class.get_auth_data")
def test_authenticate_error_with_bytes_token(self, mock_get_auth_data: MagicMock, mock_get_userinfo: MagicMock):
mock_get_auth_data.return_value = (b"SoMERaNdoMbYteS", None)
mock_get_userinfo.side_effect = TokenVerificationError()
mock_get_userinfo.side_effect = InvalidToken()
request = self.factory.get(path=self.protected_url)
response = self.protected_view.as_view({'get': 'list'})(request)
mock_get_auth_data.assert_called()
Expand Down
4 changes: 0 additions & 4 deletions admin_cohort/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ class MissingDataError(Exception):
pass


class TokenVerificationError(Exception):
pass


class PersonIdentity:
def __init__(self, firstname, lastname, user_id, email):
self.firstname = firstname
Expand Down

0 comments on commit 4b04a22

Please sign in to comment.