diff options
Diffstat (limited to 'gn_auth/auth/authentication/oauth2/models')
3 files changed, 47 insertions, 18 deletions
diff --git a/gn_auth/auth/authentication/oauth2/models/jwt_bearer_token.py b/gn_auth/auth/authentication/oauth2/models/jwt_bearer_token.py new file mode 100644 index 0000000..2606ac6 --- /dev/null +++ b/gn_auth/auth/authentication/oauth2/models/jwt_bearer_token.py @@ -0,0 +1,15 @@ +"""Implement model for JWTBearerToken""" +import uuid + +from authlib.oauth2.rfc7523 import JWTBearerToken as _JWTBearerToken + +from gn_auth.auth.db.sqlite3 import with_db_connection +from gn_auth.auth.authentication.users import user_by_id + +class JWTBearerToken(_JWTBearerToken): + """Overrides default JWTBearerToken class.""" + + def __init__(self, payload, header, options=None, params=None): + super().__init__(payload, header, options, params) + self.user = with_db_connection( + lambda conn:user_by_id(conn, uuid.UUID(payload["sub"]))) diff --git a/gn_auth/auth/authentication/oauth2/models/jwtrefreshtoken.py b/gn_auth/auth/authentication/oauth2/models/jwtrefreshtoken.py index 31c9147..46515c8 100644 --- a/gn_auth/auth/authentication/oauth2/models/jwtrefreshtoken.py +++ b/gn_auth/auth/authentication/oauth2/models/jwtrefreshtoken.py @@ -142,7 +142,7 @@ def link_child_token(conn: db.DbConnection, parenttoken: str, childtoken: str): "WHERE token=:parenttoken"), {"parenttoken": parent.token, "childtoken": childtoken}) - def __check_child__(parent): + def __check_child__(parent):#pylint: disable=[unused-variable] with db.cursor(conn) as cursor: cursor.execute( ("SELECT * FROM jwt_refresh_tokens WHERE token=:parenttoken"), @@ -154,15 +154,17 @@ def link_child_token(conn: db.DbConnection, parenttoken: str, childtoken: str): "activity detected.") return Right(parent) - def __revoke_and_raise_error__(_error_msg_): + def __revoke_and_raise_error__(_error_msg_):#pylint: disable=[unused-variable] load_refresh_token(conn, parenttoken).then( lambda _tok: revoke_refresh_token(conn, _tok)) raise InvalidGrantError(_error_msg_) + def __handle_not_found__(_error_msg_): + raise InvalidGrantError(_error_msg_) + load_refresh_token(conn, parenttoken).maybe( - Left("Token not found"), Right).then( - __check_child__).either(__revoke_and_raise_error__, - __link_to_child__) + Left("Token not found"), Right).either( + __handle_not_found__, __link_to_child__) def is_refresh_token_valid(token: JWTRefreshToken, client: OAuth2Client) -> bool: diff --git a/gn_auth/auth/authentication/oauth2/models/oauth2client.py b/gn_auth/auth/authentication/oauth2/models/oauth2client.py index d31faf6..8fac648 100644 --- a/gn_auth/auth/authentication/oauth2/models/oauth2client.py +++ b/gn_auth/auth/authentication/oauth2/models/oauth2client.py @@ -1,13 +1,14 @@ """OAuth2 Client model.""" import json +import logging import datetime -from pathlib import Path - from uuid import UUID from dataclasses import dataclass from functools import cached_property from typing import Sequence, Optional +import requests +from requests.exceptions import JSONDecodeError from authlib.jose import KeySet, JsonWebKey from authlib.oauth2.rfc6749 import ClientMixin from pymonad.maybe import Just, Maybe, Nothing @@ -57,16 +58,30 @@ class OAuth2Client(ClientMixin): """ return self.client_metadata.get("client_type", "public") - @cached_property + def jwks(self) -> KeySet: """Return this client's KeySet.""" - def __parse_key__(keypath: Path) -> JsonWebKey: - with open(keypath) as _key:# pylint: disable=[unspecified-encoding] - return JsonWebKey.import_key(_key.read()) + jwksuri = self.client_metadata.get("public-jwks-uri") + if not bool(jwksuri): + logging.debug("No Public JWKs URI set for client!") + return KeySet([]) + try: + ## IMPORTANT: This can cause a deadlock if the client is working in + ## single-threaded mode, i.e. can only serve one request + ## at a time. + return KeySet([JsonWebKey.import_key(key) + for key in requests.get(jwksuri).json()["jwks"]]) + except requests.ConnectionError as _connerr: + logging.debug( + "Could not connect to provided URI: %s", jwksuri, exc_info=True) + except JSONDecodeError as _jsonerr: + logging.debug( + "Could not convert response to JSON", exc_info=True) + except Exception as _exc:# pylint: disable=[broad-except] + logging.debug( + "Error retrieving the JWKs for the client.", exc_info=True) + return KeySet([]) - return KeySet([ - __parse_key__(Path(pth)) - for pth in self.client_metadata.get("public_keys", [])]) def check_endpoint_auth_method(self, method: str, endpoint: str) -> bool: """ @@ -77,12 +92,9 @@ class OAuth2Client(ClientMixin): * client_secret_post: Client uses the HTTP POST parameters * client_secret_basic: Client uses HTTP Basic """ - if endpoint == "token": + if endpoint in ("token", "revoke", "introspection"): return (method in self.token_endpoint_auth_method and method == "client_secret_post") - if endpoint in ("introspection", "revoke"): - return (method in self.token_endpoint_auth_method - and method == "client_secret_basic") return False @cached_property |