Skip to content

Commit

Permalink
Merge pull request #40 from Authress/optimize-jwks
Browse files Browse the repository at this point in the history
Optimize jwks
  • Loading branch information
wparad authored Sep 27, 2024
2 parents a9f4ec2 + 714285a commit c40920d
Show file tree
Hide file tree
Showing 20 changed files with 24 additions and 59 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ This is the changelog for [Authress SDK](readme.md).

## 3.1 ##
* [Breaking] Throw validation error on setting a property that doesn't exist in any of the Authress DTO Models.
* Optimize JWKs fetching using the keyId

## 3.0 ##
* [Breaking] Added type checking everywhere - This means most models have breaking changes.
Expand Down
1 change: 1 addition & 0 deletions authress/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from authress.authress_client import AuthressClient
from authress.http_client import HttpClient
from authress.rest import ApiException
from authress.utils.service_client_token_provider import ServiceClientTokenProvider

# import apis into sdk package
from authress.api.access_records_api import AccessRecordsApi
Expand Down
3 changes: 0 additions & 3 deletions authress/api/access_records_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from authress.models.access_request_response import AccessRequestResponse
from authress.models.claim_request import ClaimRequest

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -53,8 +52,6 @@ class AccessRecordsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/accounts_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from authress.models.identity_collection import IdentityCollection
from authress.models.identity_request import IdentityRequest

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -53,8 +52,6 @@ class AccountsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/applications_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

from authress.models.application_delegation import ApplicationDelegation

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -46,8 +45,6 @@ class ApplicationsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/connections_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
from authress.models.connection_collection import ConnectionCollection
from authress.models.user_connection_credentials import UserConnectionCredentials

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -48,8 +47,6 @@ class ConnectionsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/extensions_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
from authress.models.o_auth_token_request import OAuthTokenRequest
from authress.models.o_auth_token_response import OAuthTokenResponse

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -52,8 +51,6 @@ class ExtensionsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/groups_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
from authress.models.group import Group
from authress.models.group_collection import GroupCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -49,8 +48,6 @@ class GroupsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/invites_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from authress.models.account import Account
from authress.models.invite import Invite

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -47,8 +46,6 @@ class InvitesApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/resource_permissions_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from authress.models.permissioned_resource_collection import PermissionedResourceCollection
from authress.models.resource_users_collection import ResourceUsersCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -50,8 +49,6 @@ class ResourcePermissionsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/roles_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from authress.models.role import Role
from authress.models.role_collection import RoleCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -44,8 +43,6 @@ class RolesApi(object):
"""

def __init__(self, api_client=None) -> None:
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/service_clients_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from authress.models.client_access_key import ClientAccessKey
from authress.models.client_collection import ClientCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -50,8 +49,6 @@ class ServiceClientsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/tenants_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from authress.models.tenant_collection import TenantCollection
from authress.models.tenant_user import TenantUser

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -43,8 +42,6 @@ class TenantsApi:
"""

def __init__(self, api_client=None) -> None:
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
12 changes: 7 additions & 5 deletions authress/api/token_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
from authress.utils import service_client_token_provider, PackageVersionProvider

class TokenVerifier(object):
def __init__(self):
def __init__(self, http_client=None):
self.http_client = http_client
if http_client is None:
self.http_client = rest.RESTClientObject()

self.keyMap = dict()
self.package_version_provider = PackageVersionProvider()

Expand Down Expand Up @@ -72,14 +76,12 @@ def get_public_key(self, jwkKeyListUrl, kid):
self.keyMap[hashKey] = self.get_key_uncached(jwkKeyListUrl, kid)
return self.keyMap[hashKey]

def get_key_uncached(self, jwkKeyListUrl, kid):
rest_client = rest.RESTClientObject()

def get_key_uncached(self, jwkKeyListUrl, kid):
version = self.package_version_provider.get_version()
headers = {
'User-Agent': f'Authress SDK; Python; {version};'
}
result = rest_client.get_request(jwkKeyListUrl, headers=headers)
result = self.http_client.request_with_retries('GET', jwkKeyListUrl, headers=headers)

for index, key in enumerate(json.loads(result.data)['keys']):
if key['kid'] == kid:
Expand Down
3 changes: 0 additions & 3 deletions authress/api/user_permissions_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from authress.models.user_resources_collection import UserResourcesCollection
from authress.models.user_role_collection import UserRoleCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -50,8 +49,6 @@ class UserPermissionsApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
3 changes: 0 additions & 3 deletions authress/api/users_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from authress.models.user_identity import UserIdentity
from authress.models.user_identity_collection import UserIdentityCollection

from authress.http_client import HttpClient
from authress.api_response import ApiResponse
from authress.exceptions import ( # noqa: F401
ApiTypeError,
Expand All @@ -50,8 +49,6 @@ class UsersApi(object):
"""

def __init__(self, api_client=None):
if api_client is None:
api_client = HttpClient.get_default()
self.api_client = api_client

@validate_arguments
Expand Down
7 changes: 4 additions & 3 deletions authress/authress_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
from authress.api import token_verifier

class AuthressClient(object):
def __init__(self, authress_api_url=None, service_client_access_key=None):
def __init__(self, authress_api_url=None, service_client_access_key=None, user_agent=None):
self._host = authress_api_url if authress_api_url.startswith('http') else f"https://{authress_api_url}"
self._host = re.sub(r'/+$', '', self._host)
self._token_verifier = token_verifier.TokenVerifier()
self._http_client = HttpClient(host=self._host, access_key=service_client_access_key)

self._http_client = HttpClient(host=self._host, access_key=service_client_access_key, user_agent=user_agent)
self._token_verifier = token_verifier.TokenVerifier(http_client=self._http_client)

def set_token(self, token: str):
self._http_client.set_token(token)
Expand Down
4 changes: 2 additions & 2 deletions authress/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class HttpClient(object):
}
_pool = None

def __init__(self, host=None, access_key=None):
def __init__(self, host=None, access_key=None, user_agent=None):
self.host = host if host is not None and host.startswith('http') else f"https://{host}"
self.access_key = access_key
self.pool_threads = 1
Expand All @@ -60,7 +60,7 @@ def __init__(self, host=None, access_key=None):
self.service_client_token_provider = service_client_token_provider.ServiceClientTokenProvider(self.access_key, self.host)

version = PackageVersionProvider().get_version()
self.default_headers['User-Agent'] = f'Authress SDK; Python; {version};'
self.default_headers['User-Agent'] = f'Authress SDK; Python; {version}; {user_agent or ""}'

def set_token(self, token):
self.default_headers['Authorization'] = f'Bearer {token.replace("Bearer", "").strip()}'
Expand Down
17 changes: 8 additions & 9 deletions test/test_service_client_token_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from authress.models import *
from authress.utils import ServiceClientTokenProvider, JwtManager
from authress import AuthressClient
from authress.http_client import HttpClient

import unittest
from unittest.mock import patch
Expand All @@ -27,10 +26,10 @@ def test_get_token(self):

access_key = 'eyJrZXlJZCI6ImNjYjFjZGJmLTM0NzYtNGNiNy05Njc1LTVlMzNmYjI5NTNjMyIsInByaXZhdGVLZXkiOiItLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS1cbk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRRFFidWFBd0V6VkJHZnZcbjBEL2JKSjRHa2JoV2oyRy9lYTF3UUZNeWxiK0ZzTDM1dGJvVHNIUGdvbUtGMDNNQkphWmRBVHhwcnhYa2xvYWNcblFhYkE4eXcyQ2lFbitHNjlMcUFnUVlLSmZzL1psWEo4MVJ0TkR0TkZRUTdPS0xpSGJrU1I1cFU3R0lKNENQZTZcbkxHM3UzUkVUbFFndmlhV2M2bzVOUkRMWTBIbzhya2w0Yk8rZjMycXg2SGV6dzBsUnZOK1I4L24vZUxLbCtGVlpcbk1yYzFkcmh5NDdtVU80ZnJKWW1LSzg2ZGZacVk4RVh2aGpjaFZzdHhSTXdtMlRDbUtWT2xsQWUrandmNTR5Q2NcbnUzcTNsRUxzcnhXTlRsVC9mRHBQQSt6MDh2MFdDa2ozdWo5SEh3REE1VjhZaUtJOEFheWRLSG9nYnA5eHp1amdcbnZwQmVacnRuQWdNQkFBRUNnZ0VBZnRpL0Z1UHcza0tNTG5vQ0lvK3FURDBxZmlOTVRZYnpjamp6YVBtUlVQODZcbjNsa21JUTFsdC9PYkdlNlJNc1dDOVY3bk1Ub0lqTkMrb3lHaEpoUFhlQnU2Q2VVN0g0N2NqRVRSK0hOZ2N2NXNcbmFtUVc5VkpzYU4wcThYUCt1UXoyVmdTS0ZTalpYY3UzVjJucWpVK2tNTktsNUtoVVRhYkJhMnh4dFZsS3l0bjlcbld6QnIzZExLVXBwYzdoZXFaa2diSHE2amZXd3h2Vk56cmhkNUZ1Tm5EeWI2R3QrUXhzYi83dmdhdnRsNmtXM0hcbnU5ZUtGcjJhdER4VHhaTDArRVIxWjVyV21MNzdUK3owQitYVkZJZ05ia2FlSURBZjEzRjBPSzE3YjJ4NmlSSjVcbnJCRTdCc0ZhMGVuOXBjSWN2UUxhbGFMREVOL1d2YWd4dnowSWZ4M1R3UUtCZ1FEbjVMSGhFczBzeWxYVUdrVlFcbmhFU1ZacGh5QzdFRHBZYm44UW5DNkI3ZDFtWkR1d1JuSkMyZXVxT1lNMFlVcWlGT3Q5RW9UMllNQy9jT2pUVTFcbndnMm9GQ0hqWUI4cGptNHVkb3B3MHRHcGRyb1piQVNkOHkzR1RuNitMK25tVGVzNjYxTGN6ekhSekhrTnh5OUJcbkNVNTRzWXhnK1M5bnhSTjdEVERxeTE2M1dRS0JnUURtR2Q1Snl4VnNyS3ZVSmFVbDM5ekZRcTh3cnpTZ0xZVVBcbjF0a1dHYWhIanFvRjYvSnM2YUZnZlYraXl5THk1dm03WEN0dUw2RGtEQW93NnVpS1NiRlhicnVCYW5GSDBnWktcbkRaVmVQcU9mbTVIYWJWalB1VmdPdW5HWHBOMnZ4QTdwNkg0SkMxOVkvUkg0MkY5bHE4aUtkejZXWHJYMjNPRHFcbjQrcHZtdzF3dndLQmdRQ0YyajlHNExobjR6OFptRFJzWG56TUZCVm90eER0UHUyWkVrd0ZJa0UyNFp2VCtxNTJcbjdxNGFramIrRXBLZ09QZlMzVTJ3eSt2bWhqMk1PN3Y4Rk5BWE5jKzkxRzBJYXJ0MHZGMzY4K1dyd09sNDVSM2hcbklrNUl5bVJrV1huVXd5TkZ0akgxWE8rdjN5djg1UDJFdDkrQTBWTnJZa3FYeG0wUk9UTUVSSEdldVFLQmdEZmdcbnNrMkRSc21rU1BuMHhsMGpOdTZrV2Z6ZG4wOENudHlRMVJqNzFCVEVmVitBdzlkVkNQNXdrOGZwd3F2d0VWZEJcbmM3NkhURy8weUlqR2t2LzZFMW5qSngrdlpLRUhUTVd3OU1QMVBERG5TNDBhbnNXYkFkcFp4bm9IN0ZuaHA2bC9cbjd4TnRNcE5lcVgyZnRkTHYyM3hjcHROSFhyTDdRcGRvRDZkWXBQUHJBb0dCQU5CN2QyME5kY1EzaTBmWGJ6dGhcbk1RUFIwK3NEVkViMUZjSUdXbDdPeXNvYy9UZ2prT3NhVDRTL2hXODg1RGR5ZnZHbjdpRmpyMDBPQVVyVjE5NlRcbmFwdDJNS0EvWVdWeG9Ud2kwZCs0UHZ5Mnk3SXBnMk9tcEE0bVliYnBXQ0NPS3dtczlEQ0E4MVVGeEJiMHdUbTdcbjlXVStVbGZMWDAvcGNkSFNEZkExbXVjZVxuLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLVxuIiwiYXVkaWVuY2UiOiIyMmJiYzUwMi0zYjdhLTRjNTQtOGE2ZS1jMDRhM2NhNGRmNWYuYWNjb3VudHMuYXV0aHJlc3MuaW8iLCJjbGllbnRJZCI6IjIzYjRiY2Q1LWMwYzEtNDYwMi05NGU1LThkYTgyNzNkMGRiMCJ9'

http_client = HttpClient("", access_key)
token1 = http_client._get_client_token()
service_client_token_provider = ServiceClientTokenProvider(access_key, "")
token1 = service_client_token_provider.get_client_token()
time.sleep(2)
token2 = http_client._get_client_token()
token2 = service_client_token_provider.get_client_token()
assert token1 == token2
pass

Expand All @@ -42,10 +41,10 @@ def test_get_token_for_eddsa(self):

access_key = 'CLIENT.KEY.ACCOUNT.MC4CAQAwBQYDK2VwBCIEIIM7npIckfT431rYzEeF+hCqvHogpOllmVSgINwqQv+g'

http_client = HttpClient("", access_key)
token1 = http_client._get_client_token()
service_client_token_provider = ServiceClientTokenProvider(access_key, "")
token1 = service_client_token_provider.get_client_token()
time.sleep(2)
token2 = http_client._get_client_token()
token2 = service_client_token_provider.get_client_token()
assert token1 == token2
pass

Expand All @@ -55,8 +54,8 @@ def test_get_token_without_access_key(self):
Ignores access keys that are None
"""

http_client = HttpClient("")
token1 = http_client._get_client_token()
service_client_token_provider = ServiceClientTokenProvider("")
token1 = service_client_token_provider.get_client_token()
assert token1 == None
pass

Expand Down
2 changes: 1 addition & 1 deletion test/test_token_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_get_token_for_eddsa(self):
token_verifier_instance.get_key_uncached = mock_get_key_uncached
identity = token_verifier_instance.verify_token(authressCustomDomain=f"https://{customDomain}", token=access_key)

mock_get_key_uncached.assert_called_once_with(f"https://{customDomain}/v1/clients/CLIENT/.well-known/openid-configuration/jwks", "KEY")
mock_get_key_uncached.assert_called_once_with(f"https://{customDomain}/v1/clients/CLIENT/.well-known/openid-configuration/jwks?kid=KEY", "KEY")
assert identity['iss'] == f'https://{customDomain}/v1/clients/CLIENT'
assert identity['sub'] == "CLIENT"

Expand Down

0 comments on commit c40920d

Please sign in to comment.