diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f69ae66..1a7c2fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v24.20 ### Pre-releases +- `v24.20-alpha14` - `v24.20-alpha13` - `v24.20-alpha12` - `v24.20-alpha11` @@ -23,6 +24,7 @@ - Default password criteria are more restrictive (#372, `v24.20-alpha1`, Compatible with Seacat Auth Webui v24.19-alpha and later, Seacat Account Webui v24.08-beta and later) ### Fix +- Fix nginx oauth introspection for self-encoded (algorithmic) sessions (#405, `v24.20-alpha14`) - Fix token request for self-encoded (algorithmic) sessions (#404, `v24.20-alpha13`) - Fix credential search performance (#391, `v24.20-alpha12`) - Fix AttributeError in credentials update (#399, `v24.20-alpha11`) diff --git a/seacatauth/cookie/service.py b/seacatauth/cookie/service.py index ffe44d64..26dedc83 100644 --- a/seacatauth/cookie/service.py +++ b/seacatauth/cookie/service.py @@ -8,7 +8,6 @@ import asab import asab.storage import asab.exceptions -import jwcrypto.jws from .. import exceptions from ..session.adapter import SessionAdapter, CookieData @@ -106,17 +105,9 @@ async def get_session_by_session_cookie_value(self, cookie_value: str): """ Get session by cookie value. """ - # First try interpreting the token as an algorithmic session if "." in cookie_value: - try: - return await self.SessionService.Algorithmic.deserialize(cookie_value) - except asab.exceptions.NotAuthenticatedError as e: - # The JWToken is invalid or expired - raise exceptions.SessionNotFoundError( - "Invalid algorithmic session token", query={"cookie_value": cookie_value}) from e - except jwcrypto.jws.InvalidJWSObject: - # Not a JWT token - pass + # If there is ".", the value is not pure base64. It must be a JWT of an algorithmic session. + return await self.SessionService.Algorithmic.deserialize(cookie_value) # Then try looking for the session in the database try: diff --git a/seacatauth/openidconnect/service.py b/seacatauth/openidconnect/service.py index 767cce35..452d8b80 100644 --- a/seacatauth/openidconnect/service.py +++ b/seacatauth/openidconnect/service.py @@ -552,6 +552,10 @@ async def get_session_by_access_token(self, token_value: str): """ Retrieve session by its access token. """ + if "." in token_value: + # If there is ".", the value is not pure base64. It must be a JWT of an algorithmic session. + return await self.SessionService.Algorithmic.deserialize(token_value) + try: token_bytes = base64.urlsafe_b64decode(token_value.encode("ascii")) except binascii.Error as e: diff --git a/seacatauth/session/algorithmic.py b/seacatauth/session/algorithmic.py index e62d5797..3ecc2d4f 100644 --- a/seacatauth/session/algorithmic.py +++ b/seacatauth/session/algorithmic.py @@ -7,12 +7,11 @@ import uuid import json import datetime - -import asab.exceptions import asab.web.rest import asab.metrics from .adapter import SessionAdapter +from .. import exceptions from ..authz import build_credentials_authz L = logging.getLogger(__name__) @@ -114,12 +113,14 @@ async def deserialize(self, token_value) -> SessionAdapter | None: """ try: token = jwcrypto.jwt.JWT(jwt=token_value, key=self.PrivateKey) - except ValueError: - # This is not a JWToken - return None - except (jwcrypto.jws.InvalidJWSSignature, jwcrypto.jwt.JWTExpired) as e: - # JWToken invalid - raise asab.exceptions.NotAuthenticatedError() from e + except (ValueError, jwcrypto.jws.InvalidJWSObject) as e: + L.error("Corrupt algorithmic session token.") + raise exceptions.SessionNotFoundError("Corrupt algorithmic session token.") from e + except jwcrypto.jws.InvalidJWSSignature as e: + L.error("Invalid algorithmic session token signature.") + raise exceptions.SessionNotFoundError("Invalid algorithmic session token signature.") from e + except jwcrypto.jwt.JWTExpired as e: + raise exceptions.SessionNotFoundError("Expired algorithmic session token.") from e data_dict = json.loads(token.claims) client_dict = await self.ClientService.get(data_dict["azp"]) @@ -130,7 +131,10 @@ async def deserialize(self, token_value) -> SessionAdapter | None: client_dict=client_dict, scope=data_dict["scope"]) except Exception as e: - raise asab.exceptions.NotAuthenticatedError() from e + L.error( + "Failed to build session from algorithmic session token claims.", struct_data=data_dict) + raise exceptions.SessionNotFoundError( + "Failed to build session from algorithmic session token claims.") from e return session