Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Contents:
:maxdepth: 2

intro
move
message/index
server/index
client/index
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"

[metadata]
name = "idpyoidc"
version = "1.0.9"
version = "1.0.10"
author = "Roland Hedberg"
author_email = "roland@catalogix.se"
description = "Everything OAuth2 and OIDC"
Expand Down
2 changes: 1 addition & 1 deletion src/idpyoidc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__author__ = "Roland Hedberg"
__version__ = "1.0.9"
__version__ = "1.0.10"

import os
from typing import Dict
Expand Down
5 changes: 4 additions & 1 deletion src/idpyoidc/server/oauth2/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,10 @@ def _unwrap_identity(self, identity):
except BadSyntax:
return identity
else:
_id = b64d(as_bytes(identity))
try:
_id = b64d(as_bytes(identity))
except BadSyntax:
return identity

return json.loads(as_unicode(_id))

Expand Down
10 changes: 9 additions & 1 deletion src/idpyoidc/server/oauth2/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from idpyoidc.message import Message
from idpyoidc.message.oauth2 import AccessTokenResponse
from idpyoidc.message.oauth2 import ResponseMessage
from idpyoidc.message.oauth2 import TokenExchangeRequest
from idpyoidc.message.oidc import TokenErrorResponse
from idpyoidc.server.endpoint import Endpoint
from idpyoidc.server.exception import ProcessError
from idpyoidc.server.oauth2.token_helper import AccessTokenHelper
from idpyoidc.server.oauth2.token_helper import RefreshTokenHelper
from idpyoidc.server.session.token import TOKEN_TYPES_MAPPING
from idpyoidc.util import importer

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -125,8 +127,14 @@ def process_request(self, request: Optional[Union[Message, dict]] = None, **kwar

_access_token = response_args["access_token"]
_context = self.server_get("endpoint_context")

if isinstance(request, TokenExchangeRequest):
_handler_key = TOKEN_TYPES_MAPPING[request["requested_token_type"]]
else:
_handler_key = "access_token"

_session_info = _context.session_manager.get_session_info_by_token(
_access_token, grant=True
_access_token, grant=True, handler_key=_handler_key
)

_cookie = _context.new_cookie(
Expand Down
33 changes: 25 additions & 8 deletions src/idpyoidc/server/oauth2/token_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Optional
from typing import Union

from cryptojwt import BadSyntax
from cryptojwt.exception import JWKESTException

from idpyoidc.exception import ImproperlyConfigured
Expand Down Expand Up @@ -120,7 +121,9 @@ def process_request(self, req: Union[Message, dict], **kwargs):
except KeyError: # Missing code parameter - absolutely fatal
return self.error_cls(error="invalid_request", error_description="Missing code")

_session_info = _mngr.get_session_info_by_token(_access_code, grant=True)
_session_info = _mngr.get_session_info_by_token(
_access_code, grant=True, handler_key="authorization_code"
)
client_id = _session_info["client_id"]
if client_id != req["client_id"]:
logger.debug("{} owner of token".format(client_id))
Expand Down Expand Up @@ -208,7 +211,9 @@ def post_parse_request(

_mngr = self.endpoint.server_get("endpoint_context").session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["code"], grant=True)
_session_info = _mngr.get_session_info_by_token(
request["code"], grant=True, handler_key="authorization_code"
)
except (KeyError, UnknownToken):
logger.error("Access Code invalid")
return self.error_cls(error="invalid_grant", error_description="Unknown code")
Expand Down Expand Up @@ -241,7 +246,9 @@ def process_request(self, req: Union[Message, dict], **kwargs):
return self.error_cls(error="invalid_request", error_description="Wrong grant_type")

token_value = req["refresh_token"]
_session_info = _mngr.get_session_info_by_token(token_value, grant=True)
_session_info = _mngr.get_session_info_by_token(
token_value, grant=True, handler_key="refresh_token"
)
logger.debug("Session info: {}".format(_session_info))

if _session_info["client_id"] != req["client_id"]:
Expand Down Expand Up @@ -335,7 +342,9 @@ def post_parse_request(

_mngr = _context.session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["refresh_token"], grant=True)
_session_info = _mngr.get_session_info_by_token(
request["refresh_token"], grant=True, handler_key="refresh_token"
)
except (KeyError, UnknownToken):
logger.error("Refresh token invalid")
return self.error_cls(error="invalid_grant", error_description="Invalid refresh token")
Expand Down Expand Up @@ -414,13 +423,18 @@ def post_parse_request(self, request, client_id="", **kwargs):

_mngr = _context.session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["subject_token"], grant=True)
except (KeyError, UnknownToken):
logger.error("Subject token invalid.")
# token exchange is about minting one token based on another
_handler_key = self.token_types_mapping[request["subject_token_type"]]
_session_info = _mngr.get_session_info_by_token(
request["subject_token"], grant=True, handler_key=_handler_key
)
except (KeyError, UnknownToken, BadSyntax) as err:
logger.error(f"Subject token invalid ({err}).")
return self.error_cls(
error="invalid_request", error_description="Subject token invalid"
)

# Find the token instance based on the token value
token = _mngr.find_token(_session_info["session_id"], request["subject_token"])
if token.is_active() is False:
return self.error_cls(
Expand Down Expand Up @@ -511,7 +525,10 @@ def process_request(self, request, **kwargs):
_context = self.endpoint.server_get("endpoint_context")
_mngr = _context.session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["subject_token"], grant=True)
_handler_key = self.token_types_mapping[request["subject_token_type"]]
_session_info = _mngr.get_session_info_by_token(
request["subject_token"], grant=True, handler_key=_handler_key
)
except ToOld:
logger.error("Subject token has expired.")
return self.error_cls(
Expand Down
2 changes: 1 addition & 1 deletion src/idpyoidc/server/oidc/add_on/pkce.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def post_token_parse(request, client_id, endpoint_context, **kwargs):

try:
_session_info = endpoint_context.session_manager.get_session_info_by_token(
request["code"], grant=True
request["code"], grant=True, handler_key="authorization_code"
)
except KeyError:
return TokenErrorResponse(error="invalid_grant", error_description="Unknown access grant")
Expand Down
22 changes: 16 additions & 6 deletions src/idpyoidc/server/oidc/token_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Optional
from typing import Union

from cryptojwt import BadSyntax
from cryptojwt.jwe.exception import JWEException
from cryptojwt.jws.exception import NoSuitableSigningKeys
from cryptojwt.jwt import utc_time_sans_frac
Expand Down Expand Up @@ -29,7 +30,9 @@ def _get_session_info(self, request, session_manager):
except KeyError: # Missing code parameter - absolutely fatal
return self.error_cls(error="invalid_request", error_description="Missing code")

_session_info = session_manager.get_session_info_by_token(_access_code, grant=True)
_session_info = session_manager.get_session_info_by_token(
_access_code, grant=True, handler_key="authorization_code"
)
logger.debug(f"Session info: {_session_info}")
return _session_info, _access_code

Expand Down Expand Up @@ -174,7 +177,9 @@ def post_parse_request(

_mngr = self.endpoint.server_get("endpoint_context").session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["code"], grant=True)
_session_info = _mngr.get_session_info_by_token(
request["code"], grant=True, handler_key="authorization_code"
)
except (KeyError, UnknownToken):
logger.error("Access Code invalid")
return self.error_cls(error="invalid_grant", error_description="Unknown code")
Expand Down Expand Up @@ -211,7 +216,10 @@ def process_request(self, req: Union[Message, dict], **kwargs):
return self.error_cls(error="invalid_request", error_description="Wrong grant_type")

token_value = req["refresh_token"]
_session_info = _mngr.get_session_info_by_token(token_value, grant=True)

_session_info = _mngr.get_session_info_by_token(
token_value, handler_key="refresh_token", grant=True
)
if _session_info["client_id"] != req["client_id"]:
logger.debug("{} owner of token".format(_session_info["client_id"]))
logger.warning("{} using token it was not given".format(req["client_id"]))
Expand Down Expand Up @@ -299,7 +307,7 @@ def process_request(self, req: Union[Message, dict], **kwargs):
):
revoke_refresh = _context.cdb[req["client_id"]].get("revoke_refresh_on_issue")
else:
revoke_refresh = revoke_refresh = self.endpoint.revoke_refresh_on_issue
revoke_refresh = self.endpoint.revoke_refresh_on_issue

if revoke_refresh:
token.revoke()
Expand Down Expand Up @@ -328,8 +336,10 @@ def post_parse_request(

_mngr = _context.session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["refresh_token"], grant=True)
except (KeyError, UnknownToken):
_session_info = _mngr.get_session_info_by_token(
request["refresh_token"], handler_key="refresh_token", grant=True
)
except (KeyError, UnknownToken, BadSyntax):
logger.error("Refresh token invalid")
return self.error_cls(error="invalid_grant", error_description="Invalid refresh token")

Expand Down
8 changes: 6 additions & 2 deletions src/idpyoidc/server/oidc/userinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ def __init__(self, server_get: Callable, add_claims_by_scope: Optional[bool] = T
self.allowed_targets.append("")

def get_client_id_from_token(self, endpoint_context, token, request=None):
_info = endpoint_context.session_manager.get_session_info_by_token(token)
_info = endpoint_context.session_manager.get_session_info_by_token(
token, handler_key="access_token"
)
return _info["client_id"]

def do_response(
Expand Down Expand Up @@ -113,7 +115,9 @@ def do_response(
def process_request(self, request=None, **kwargs):
_mngr = self.server_get("endpoint_context").session_manager
try:
_session_info = _mngr.get_session_info_by_token(request["access_token"], grant=True)
_session_info = _mngr.get_session_info_by_token(
request["access_token"], grant=True, handler_key="access_token"
)
except (KeyError, ValueError):
return self.error_cls(error="invalid_token", error_description="Invalid Token")

Expand Down
18 changes: 12 additions & 6 deletions src/idpyoidc/server/session/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from idpyoidc.server.authn_event import AuthnEvent
from idpyoidc.server.exception import ConfigurationError
from idpyoidc.server.session.database import NoSuchClientSession
from idpyoidc.server.token import Token
from idpyoidc.util import rndstr

from ..token import UnknownToken
Expand Down Expand Up @@ -559,13 +560,18 @@ def _compatible_sid(self, sid):
def get_session_info_by_token(
self,
token_value: str,
user_session_info: bool = False,
client_session_info: bool = False,
grant: bool = False,
authentication_event: bool = False,
authorization_request: bool = False,
user_session_info: Optional[bool] = False,
client_session_info: Optional[bool] = False,
grant: Optional[bool] = False,
authentication_event: Optional[bool] = False,
authorization_request: Optional[bool] = False,
handler_key: Optional[str] = "",
) -> dict:
_token_info = self.token_handler.info(token_value)
if handler_key:
_token_info = self.token_handler.handler[handler_key].info(token_value)
else:
_token_info = self.token_handler.info(token_value)

sid = _token_info.get("sid")
# If the token is an ID Token then the sid will not be in the
# _token_info
Expand Down
6 changes: 6 additions & 0 deletions src/idpyoidc/server/session/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,9 @@ def __init__(
"refresh_token": RefreshToken,
"id_token": IDToken,
}

TOKEN_TYPES_MAPPING = {
"urn:ietf:params:oauth:token-type:access_token": "access_token",
"urn:ietf:params:oauth:token-type:refresh_token": "refresh_token",
"urn:ietf:params:oauth:token-type:id_token": "id_token",
}
35 changes: 18 additions & 17 deletions src/idpyoidc/server/token/jwt_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@

from idpyoidc.encrypter import init_encrypter
from idpyoidc.server.exception import ToOld
from . import is_expired

from ..constant import DEFAULT_TOKEN_LIFETIME
from . import Token
from . import is_expired
from .exception import UnknownToken
from .exception import WrongTokenClass
from ..constant import DEFAULT_TOKEN_LIFETIME


class JWTToken(Token):
def __init__(
self,
token_class,
# keyjar: KeyJar = None,
issuer: str = None,
aud: Optional[list] = None,
alg: str = "ES256",
lifetime: int = DEFAULT_TOKEN_LIFETIME,
server_get: Callable = None,
token_type: str = "Bearer",
**kwargs
self,
token_class,
# keyjar: KeyJar = None,
issuer: str = None,
aud: Optional[list] = None,
alg: str = "ES256",
lifetime: int = DEFAULT_TOKEN_LIFETIME,
server_get: Callable = None,
token_type: str = "Bearer",
**kwargs
):
Token.__init__(self, token_class, **kwargs)
self.token_type = token_type
Expand All @@ -45,11 +46,11 @@ def load_custom_claims(self, payload: dict = None):
return payload

def __call__(
self,
session_id: Optional[str] = "",
token_class: Optional[str] = "",
usage_rules: Optional[dict] = None,
**payload
self,
session_id: Optional[str] = "",
token_class: Optional[str] = "",
usage_rules: Optional[dict] = None,
**payload
) -> str:
"""
Return a token.
Expand Down
2 changes: 1 addition & 1 deletion tests/request123456.jwt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6IlNVc3dOaTFNUkZsRFQwWTJZalUxWjFSZlFsbzJTM2RFYTNGVFRrVjNMVGhGY25oRFRIRjVlbGsyVlEifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJvWkpBNTRnZTVaUndNalkwOVVLVnpwYkx5MEdNUEwwaCIsICJjbGllbnRfaWQiOiAiY2xpZW50X2lkIiwgImlzcyI6ICJjbGllbnRfaWQiLCAiaWF0IjogMTYzMzU5NTc4OSwgImF1ZCI6IFsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfQ.KVMPK6leJ5pEXnJ0jXiXu21U176IU9iwkT4FkQV_33jGYTsgdqCqXw5XHR1ciixdcH2cWf0SzTPOgIzGsI4NJiPNdR9xOusYRyYKZciXHq85nrM7fr7dEPaVntWCU6uadH0MNHWCcq2FyBdz2YYDuiFPUXoxkFbfWZoo_jVMAWLxGQtGEitniI49qo0zbeSFck4hBmEtQTUOrGQvg_CjkSZb5oNb5rt_X5T-ZSK9y3AeKru4HLSQRkWj-oD-Fgd60Sm3XqfLQXrx26lk4a8ORah01BMmMsi5jeIUbOTthhhglZhMwoI9xCZ57I4SF7870-PrinIByW8d2keA1-LipQ
eyJhbGciOiJSUzI1NiIsImtpZCI6IlNIRXlZV2N3TlZrMExUZFJPVFp6WjJGVVduZElWWGRhY2sweFdVTTVTRXB3Y1MwM2RWVXhXVTR6UlEifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJGYVVaSm02TDdTc251d2JEbEQ0Z20wbkg2eHpBTTVlR19nWG5sRmlhX19zIiwgImNsaWVudF9pZCI6ICJjbGllbnRfaWQiLCAiaXNzIjogImNsaWVudF9pZCIsICJpYXQiOiAxNjUwMzkzMDQ3LCAiYXVkIjogWyJodHRwczovL2V4YW1wbGUuY29tIl19.ARTBLchcBOpUX5kuvZzVyfw-ad6skc498Ll93sRwGoNrxZosNdHVTP25FtrDx8GVcBoA1OSYFq7Zmx9d7DJib-uukylEQl-5widvWmC0s-14uSRfLiSulqtB43FrCji9dXl6T5uAOgGxzoNo5dVSwfeIcenjqBuiJslxuHy4AQ7S-gRi02E_uEaqWOytkUoOwaIKcMiujbpo4VWOzDv9pK4C6C89uuHSMxfWpwi27T2vFLa6icfuQuXcxOZabs1lJUJt84Aclh_mz58E1YDlowRAuUu8RkpICAnQwxgzlCGwj3mzJxxFVVUCnRVnrB-BG7e3XXQBcf536BtYOyOfCQ
4 changes: 3 additions & 1 deletion tests/test_server_10_session_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@ def test_get_session_info_by_token(self):

grant = self.session_manager.get_grant(_session_id)
code = self._mint_token("authorization_code", grant, _session_id)
_session_info = self.session_manager.get_session_info_by_token(code.value)
_session_info = self.session_manager.get_session_info_by_token(
code.value, handler_key="authorization_code"
)

assert set(_session_info.keys()) == {
"client_id",
Expand Down
6 changes: 4 additions & 2 deletions tests/test_server_13_user_authn.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,13 @@ def test_userpassjinja2(self):
def test_basic_auth(self):
basic_auth = base64.b64encode(b"diana:krall").decode()
ba = BasicAuthn(pwd={"diana": "krall"}, server_get=self.server.server_get)
ba.authenticated_as(client_id="", authorization=f"Basic {basic_auth}")
_info, _time_stamp = ba.authenticated_as(client_id="", authorization=f"Basic {basic_auth}")
assert _info

def test_no_auth(self):
basic_auth = base64.b64encode(
b"D\xfd\x8a\x85\xa6\xd1\x16\xe4\\6\x1e\x9ds~\xc3\t\x95\x99\x83\x91\x1f\xfb:iviviviv"
)
ba = SymKeyAuthn(symkey=b"0" * 32, ttl=600, server_get=self.server.server_get)
ba.authenticated_as(client_id="", authorization=basic_auth)
_info, _time_stamp = ba.authenticated_as(client_id="", authorization=basic_auth)
assert _info
Loading