lumberfi-authorization 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lumberfi_authorization-0.1.0/LICENSE +21 -0
- lumberfi_authorization-0.1.0/MANIFEST.in +7 -0
- lumberfi_authorization-0.1.0/PKG-INFO +33 -0
- lumberfi_authorization-0.1.0/authorization/__init__.py +25 -0
- lumberfi_authorization-0.1.0/authorization/__version__.py +3 -0
- lumberfi_authorization-0.1.0/authorization/authorization.py +73 -0
- lumberfi_authorization-0.1.0/authorization/descope_client.py +92 -0
- lumberfi_authorization-0.1.0/authorization/exceptions.py +214 -0
- lumberfi_authorization-0.1.0/lumberfi_authorization.egg-info/PKG-INFO +33 -0
- lumberfi_authorization-0.1.0/lumberfi_authorization.egg-info/SOURCES.txt +13 -0
- lumberfi_authorization-0.1.0/lumberfi_authorization.egg-info/dependency_links.txt +1 -0
- lumberfi_authorization-0.1.0/lumberfi_authorization.egg-info/requires.txt +8 -0
- lumberfi_authorization-0.1.0/lumberfi_authorization.egg-info/top_level.txt +1 -0
- lumberfi_authorization-0.1.0/pyproject.toml +74 -0
- lumberfi_authorization-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lumberfi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lumberfi-authorization
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK for Descope token authorization and validation
|
|
5
|
+
Author: Lumberfi
|
|
6
|
+
Maintainer: Lumberfi
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/lumberfi/lumberfi-authorization
|
|
9
|
+
Project-URL: Documentation, https://github.com/lumberfi/lumberfi-authorization#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/lumberfi/lumberfi-authorization
|
|
11
|
+
Project-URL: Issues, https://github.com/lumberfi/lumberfi-authorization/issues
|
|
12
|
+
Keywords: descope,authorization,authentication,sdk,token,validation
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: descope==1.6.7
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest==8.3.2; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: black==24.8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: flake8==6.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .descope_client import init_client, validate_session
|
|
2
|
+
from .authorization import validate_token_optional, validate_token_required
|
|
3
|
+
from .exceptions import (
|
|
4
|
+
SDKError,
|
|
5
|
+
ClientNotConfigured,
|
|
6
|
+
TokenMissing,
|
|
7
|
+
TokenInvalid,
|
|
8
|
+
PermissionDenied,
|
|
9
|
+
NotAuthorized,
|
|
10
|
+
ValidationError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"init_client",
|
|
15
|
+
"validate_session",
|
|
16
|
+
"validate_token_optional",
|
|
17
|
+
"validate_token_required",
|
|
18
|
+
"SDKError",
|
|
19
|
+
"ClientNotConfigured",
|
|
20
|
+
"TokenMissing",
|
|
21
|
+
"TokenInvalid",
|
|
22
|
+
"PermissionDenied",
|
|
23
|
+
"NotAuthorized",
|
|
24
|
+
"ValidationError",
|
|
25
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Tuple, Optional, Protocol, Mapping
|
|
3
|
+
from .descope_client import validate_session
|
|
4
|
+
from .exceptions import NotAuthorized, PermissionDenied, SDKError
|
|
5
|
+
import logging
|
|
6
|
+
AUTHORIZATION_HEADER_NAME = "Authorization"
|
|
7
|
+
BEARER_RE = re.compile(r"^[Bb]earer (.+)$")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RequestLike(Protocol):
|
|
11
|
+
headers: Mapping[str, str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TokenValidator:
|
|
15
|
+
"""Token validator that only authorizes tokens by validating with Descope.
|
|
16
|
+
|
|
17
|
+
Methods return the validated session dict (from Descope) and the raw token.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def parse_bearer(self, request: RequestLike) -> Optional[str]:
|
|
21
|
+
headers = getattr(request, "headers", {}) or {}
|
|
22
|
+
if isinstance(headers, dict):
|
|
23
|
+
auth_header_value = headers.get(AUTHORIZATION_HEADER_NAME) or headers.get(AUTHORIZATION_HEADER_NAME.lower())
|
|
24
|
+
else:
|
|
25
|
+
try:
|
|
26
|
+
auth_header_value = headers.get(AUTHORIZATION_HEADER_NAME)
|
|
27
|
+
except Exception:
|
|
28
|
+
auth_header_value = None
|
|
29
|
+
if not auth_header_value:
|
|
30
|
+
return None
|
|
31
|
+
match = BEARER_RE.match(auth_header_value)
|
|
32
|
+
if not match:
|
|
33
|
+
return None
|
|
34
|
+
return match.group(1)
|
|
35
|
+
|
|
36
|
+
def validate_optional(self, request: RequestLike) -> Tuple[Optional[dict], Optional[str]]:
|
|
37
|
+
token = self.parse_bearer(request)
|
|
38
|
+
if not token:
|
|
39
|
+
return (None, None)
|
|
40
|
+
try:
|
|
41
|
+
session = validate_session(token)
|
|
42
|
+
return (session, token)
|
|
43
|
+
except SDKError:
|
|
44
|
+
# For optional validation, silently return None on any SDK error
|
|
45
|
+
return (None, None)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
# Catch any unexpected errors and return None
|
|
48
|
+
logging.debug(f"Unexpected error during optional token validation: {e}")
|
|
49
|
+
return (None, None)
|
|
50
|
+
|
|
51
|
+
def validate_required(self, request: RequestLike) -> Tuple[dict, str]:
|
|
52
|
+
token = self.parse_bearer(request)
|
|
53
|
+
if not token:
|
|
54
|
+
raise NotAuthorized(f'Request does not contain "{AUTHORIZATION_HEADER_NAME}" header')
|
|
55
|
+
try:
|
|
56
|
+
logging.debug("Validating token with Descope")
|
|
57
|
+
session = validate_session(token)
|
|
58
|
+
return (session, token)
|
|
59
|
+
except SDKError:
|
|
60
|
+
# Re-raise SDK errors as-is (ClientNotConfigured, etc.)
|
|
61
|
+
raise
|
|
62
|
+
except Exception as error:
|
|
63
|
+
# Wrap unexpected errors in PermissionDenied for invalid tokens
|
|
64
|
+
raise PermissionDenied(f"Token validation failed: {str(error)}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Convenience functions
|
|
68
|
+
def validate_token_optional(request: RequestLike) -> Tuple[Optional[dict], Optional[str]]:
|
|
69
|
+
return TokenValidator().validate_optional(request)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def validate_token_required(request: RequestLike) -> Tuple[dict, str]:
|
|
73
|
+
return TokenValidator().validate_required(request)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional, Any
|
|
3
|
+
from .exceptions import ClientNotConfigured, ValidationError
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from descope import DescopeClient
|
|
7
|
+
except ImportError:
|
|
8
|
+
DescopeClient = None
|
|
9
|
+
|
|
10
|
+
# internally stored client instance
|
|
11
|
+
_descope_client: Optional[Any] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_client(
|
|
15
|
+
project_id: Optional[str] = None,
|
|
16
|
+
management_key: Optional[str] = None,
|
|
17
|
+
client: Optional[Any] = None
|
|
18
|
+
) -> Any:
|
|
19
|
+
"""Initialize the descope client for SDK use.
|
|
20
|
+
|
|
21
|
+
You can either pass an existing `client` instance or provide `project_id` and
|
|
22
|
+
`management_key` to construct one. If the Descope SDK isn't installed, attempting
|
|
23
|
+
to build a client will raise ImportError.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
project_id: Descope project ID. If not provided, will try to use
|
|
27
|
+
DESCOPE_PROJECT_ID environment variable.
|
|
28
|
+
management_key: Descope management key. If not provided, will try to use
|
|
29
|
+
DESCOPE_MANAGEMENT_KEY environment variable.
|
|
30
|
+
client: Optional existing DescopeClient instance to use instead of creating one.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The configured DescopeClient instance.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ImportError: If descope package is not available and client is not provided.
|
|
37
|
+
RuntimeError: If project_id and management_key are not provided and client is None.
|
|
38
|
+
"""
|
|
39
|
+
global _descope_client
|
|
40
|
+
|
|
41
|
+
if client is not None:
|
|
42
|
+
_descope_client = client
|
|
43
|
+
return _descope_client
|
|
44
|
+
|
|
45
|
+
if DescopeClient is None:
|
|
46
|
+
raise ImportError("descope package is not available. Install it with: pip install descope")
|
|
47
|
+
|
|
48
|
+
pid = project_id or os.getenv("DESCOPE_PROJECT_ID")
|
|
49
|
+
mkey = management_key or os.getenv("DESCOPE_MANAGEMENT_KEY")
|
|
50
|
+
|
|
51
|
+
if not pid or not mkey:
|
|
52
|
+
raise RuntimeError(
|
|
53
|
+
"project_id and management_key are required to create DescopeClient. "
|
|
54
|
+
"Either pass them as arguments or set DESCOPE_PROJECT_ID and "
|
|
55
|
+
"DESCOPE_MANAGEMENT_KEY environment variables."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
_descope_client = DescopeClient(project_id=pid, management_key=mkey)
|
|
59
|
+
return _descope_client
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_session(session_token: str) -> dict:
|
|
63
|
+
"""Validate a session token using the configured Descope client.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
session_token: The session token to validate.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The validated session dictionary from Descope.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ClientNotConfigured: If the client is not configured. Call init_client(...) first.
|
|
73
|
+
ValidationError: If token validation fails due to an unexpected error.
|
|
74
|
+
"""
|
|
75
|
+
global _descope_client
|
|
76
|
+
|
|
77
|
+
if _descope_client is None:
|
|
78
|
+
# Try to auto-initialize from environment variables
|
|
79
|
+
try:
|
|
80
|
+
init_client()
|
|
81
|
+
except (ImportError, RuntimeError):
|
|
82
|
+
raise ClientNotConfigured(
|
|
83
|
+
"Descope client not configured. Call init_client(...) to configure the SDK"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
return _descope_client.validate_session(session_token=session_token)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
# Wrap Descope client errors in ValidationError for better error handling
|
|
90
|
+
if isinstance(e, ClientNotConfigured):
|
|
91
|
+
raise
|
|
92
|
+
raise ValidationError(f"Token validation failed: {str(e)}") from e
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Custom exceptions for the lumberfi-authorization SDK.
|
|
2
|
+
|
|
3
|
+
These exceptions are framework-agnostic and can be used in any Python environment
|
|
4
|
+
(Django, Flask, FastAPI, etc.). All exceptions inherit from SDKError for easy
|
|
5
|
+
exception handling.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
try:
|
|
9
|
+
session, token = validate_token_required(request)
|
|
10
|
+
except NotAuthenticated:
|
|
11
|
+
# Handle missing or malformed token
|
|
12
|
+
pass
|
|
13
|
+
except PermissionDenied:
|
|
14
|
+
# Handle invalid or expired token
|
|
15
|
+
pass
|
|
16
|
+
except SDKError:
|
|
17
|
+
# Catch any SDK-related error
|
|
18
|
+
pass
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import List, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SDKError(Exception):
|
|
25
|
+
"""Base exception for all SDK-related errors.
|
|
26
|
+
|
|
27
|
+
All exceptions in this module inherit from this class, making it easy to
|
|
28
|
+
catch any SDK-related error with a single except clause.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
message: Human-readable error message.
|
|
32
|
+
error_code: Optional error code for programmatic error handling.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
try:
|
|
36
|
+
# SDK operations
|
|
37
|
+
pass
|
|
38
|
+
except SDKError as e:
|
|
39
|
+
# Handle any SDK error
|
|
40
|
+
print(f"SDK Error: {e}")
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str, error_code: Optional[str] = None):
|
|
44
|
+
"""Initialize SDKError.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
message: Human-readable error message.
|
|
48
|
+
error_code: Optional error code for programmatic handling.
|
|
49
|
+
"""
|
|
50
|
+
super().__init__(message)
|
|
51
|
+
self.message = message
|
|
52
|
+
self.error_code = error_code
|
|
53
|
+
|
|
54
|
+
def __str__(self) -> str:
|
|
55
|
+
"""Return string representation of the error."""
|
|
56
|
+
if self.error_code:
|
|
57
|
+
return f"[{self.error_code}] {self.message}"
|
|
58
|
+
return self.message
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ClientNotConfigured(SDKError):
|
|
62
|
+
"""Raised when the Descope client has not been initialized.
|
|
63
|
+
|
|
64
|
+
This exception is raised when attempting to validate a token before
|
|
65
|
+
initializing the SDK with init_client().
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
# Without calling init_client() first
|
|
69
|
+
try:
|
|
70
|
+
validate_session("token")
|
|
71
|
+
except ClientNotConfigured:
|
|
72
|
+
init_client(project_id="...", management_key="...")
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, message: str = "Descope client not configured. Call init_client(...) to configure the SDK"):
|
|
76
|
+
"""Initialize ClientNotConfigured.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
message: Custom error message. Defaults to a helpful message.
|
|
80
|
+
"""
|
|
81
|
+
super().__init__(message, error_code="CLIENT_NOT_CONFIGURED")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TokenMissing(SDKError):
|
|
85
|
+
"""Raised when a required token is missing from the request.
|
|
86
|
+
|
|
87
|
+
This exception indicates that the Authorization header is completely
|
|
88
|
+
missing from the request, or the header format is incorrect.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
try:
|
|
92
|
+
session, token = validate_token_required(request)
|
|
93
|
+
except TokenMissing:
|
|
94
|
+
# Request doesn't have Authorization header
|
|
95
|
+
return {"error": "Authentication required"}, 401
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self, message: str = "Authorization token is missing from the request"):
|
|
99
|
+
"""Initialize TokenMissing.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
message: Custom error message. Defaults to a helpful message.
|
|
103
|
+
"""
|
|
104
|
+
super().__init__(message, error_code="TOKEN_MISSING")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class TokenInvalid(SDKError):
|
|
108
|
+
"""Raised when a token is invalid according to the authorization provider.
|
|
109
|
+
|
|
110
|
+
This exception is raised when the token exists but is invalid, expired,
|
|
111
|
+
or malformed according to Descope's validation.
|
|
112
|
+
|
|
113
|
+
Attributes:
|
|
114
|
+
token: The token that failed validation (if available).
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
try:
|
|
118
|
+
session = validate_session("invalid_token")
|
|
119
|
+
except TokenInvalid as e:
|
|
120
|
+
# Token is invalid or expired
|
|
121
|
+
print(f"Invalid token: {e}")
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __init__(self, message: str = "Token is invalid or expired", token: Optional[str] = None):
|
|
125
|
+
"""Initialize TokenInvalid.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
message: Custom error message. Defaults to a helpful message.
|
|
129
|
+
token: The invalid token (optional, for logging purposes).
|
|
130
|
+
"""
|
|
131
|
+
super().__init__(message, error_code="TOKEN_INVALID")
|
|
132
|
+
self.token = token
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class PermissionDenied(SDKError):
|
|
136
|
+
"""Raised when an operation is not permitted due to authorization failure.
|
|
137
|
+
|
|
138
|
+
This exception is raised when token validation fails, indicating that
|
|
139
|
+
the user does not have permission to access the resource. This can
|
|
140
|
+
happen due to invalid tokens, expired tokens, or insufficient permissions.
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
try:
|
|
144
|
+
session, token = validate_token_required(request)
|
|
145
|
+
except PermissionDenied:
|
|
146
|
+
# Token validation failed
|
|
147
|
+
return {"error": "Access denied"}, 403
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, message: str = "Permission denied"):
|
|
151
|
+
"""Initialize PermissionDenied.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
message: Custom error message. Defaults to a helpful message.
|
|
155
|
+
"""
|
|
156
|
+
super().__init__(message, error_code="PERMISSION_DENIED")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class NotAuthorized(SDKError):
|
|
160
|
+
"""Raised when an authentication/authorization token is missing or malformed.
|
|
161
|
+
|
|
162
|
+
This exception is raised when the Authorization header is missing,
|
|
163
|
+
malformed, or doesn't contain a valid Bearer token format.
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
try:
|
|
167
|
+
session, token = validate_token_required(request)
|
|
168
|
+
except NotAuthorized:
|
|
169
|
+
# Missing or malformed Authorization header
|
|
170
|
+
return {"error": "Authentication required"}, 401
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
def __init__(self, message: str = "Authentication required"):
|
|
174
|
+
"""Initialize NotAuthorized.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
message: Custom error message. Defaults to a helpful message.
|
|
178
|
+
"""
|
|
179
|
+
super().__init__(message, error_code="NOT_AUTHENTICATED")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class ValidationError(SDKError):
|
|
183
|
+
"""Raised when token validation fails due to an unexpected error.
|
|
184
|
+
|
|
185
|
+
This exception is raised when token validation encounters an unexpected
|
|
186
|
+
error during the validation process (e.g., network issues, service
|
|
187
|
+
unavailability).
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
try:
|
|
191
|
+
session = validate_session(token)
|
|
192
|
+
except ValidationError as e:
|
|
193
|
+
# Unexpected error during validation
|
|
194
|
+
logger.error(f"Validation error: {e}")
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, message: str = "Token validation failed"):
|
|
198
|
+
"""Initialize ValidationError.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
message: Custom error message. Defaults to a helpful message.
|
|
202
|
+
"""
|
|
203
|
+
super().__init__(message, error_code="VALIDATION_ERROR")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
__all__: List[str] = [
|
|
207
|
+
"SDKError",
|
|
208
|
+
"ClientNotConfigured",
|
|
209
|
+
"TokenMissing",
|
|
210
|
+
"TokenInvalid",
|
|
211
|
+
"PermissionDenied",
|
|
212
|
+
"NotAuthorized",
|
|
213
|
+
"ValidationError",
|
|
214
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lumberfi-authorization
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK for Descope token authorization and validation
|
|
5
|
+
Author: Lumberfi
|
|
6
|
+
Maintainer: Lumberfi
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/lumberfi/lumberfi-authorization
|
|
9
|
+
Project-URL: Documentation, https://github.com/lumberfi/lumberfi-authorization#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/lumberfi/lumberfi-authorization
|
|
11
|
+
Project-URL: Issues, https://github.com/lumberfi/lumberfi-authorization/issues
|
|
12
|
+
Keywords: descope,authorization,authentication,sdk,token,validation
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: descope==1.6.7
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest==8.3.2; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: black==24.8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: flake8==6.1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
pyproject.toml
|
|
4
|
+
authorization/__init__.py
|
|
5
|
+
authorization/__version__.py
|
|
6
|
+
authorization/authorization.py
|
|
7
|
+
authorization/descope_client.py
|
|
8
|
+
authorization/exceptions.py
|
|
9
|
+
lumberfi_authorization.egg-info/PKG-INFO
|
|
10
|
+
lumberfi_authorization.egg-info/SOURCES.txt
|
|
11
|
+
lumberfi_authorization.egg-info/dependency_links.txt
|
|
12
|
+
lumberfi_authorization.egg-info/requires.txt
|
|
13
|
+
lumberfi_authorization.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
authorization
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lumberfi-authorization"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "SDK for Descope token authorization and validation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Lumberfi"}
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
16
|
+
{name = "Lumberfi"}
|
|
17
|
+
]
|
|
18
|
+
keywords = ["descope", "authorization", "authentication", "sdk", "token", "validation"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
"Topic :: Security",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
dependencies = [
|
|
33
|
+
"descope==1.6.7",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest==8.3.2",
|
|
39
|
+
"pytest-cov==5.0.0",
|
|
40
|
+
"black==24.8.0",
|
|
41
|
+
"flake8==6.1.0",
|
|
42
|
+
"mypy>=1.0.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/lumberfi/lumberfi-authorization"
|
|
47
|
+
Documentation = "https://github.com/lumberfi/lumberfi-authorization#readme"
|
|
48
|
+
Repository = "https://github.com/lumberfi/lumberfi-authorization"
|
|
49
|
+
Issues = "https://github.com/lumberfi/lumberfi-authorization/issues"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
packages = ["authorization"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.dynamic]
|
|
55
|
+
version = {attr = "authorization.__version__.__version__"}
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.package-data]
|
|
58
|
+
authorization = ["py.typed"]
|
|
59
|
+
|
|
60
|
+
[tool.black]
|
|
61
|
+
line-length = 100
|
|
62
|
+
target-version = ['py39']
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.9"
|
|
66
|
+
warn_return_any = true
|
|
67
|
+
warn_unused_configs = true
|
|
68
|
+
disallow_untyped_defs = false
|
|
69
|
+
|
|
70
|
+
[tool.pytest.ini_options]
|
|
71
|
+
testpaths = ["tests"]
|
|
72
|
+
python_files = ["test_*.py"]
|
|
73
|
+
python_classes = ["Test*"]
|
|
74
|
+
python_functions = ["test_*"]
|