kinde-python-sdk 2.0.7__py3-none-any.whl → 2.0.9__py3-none-any.whl
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.
- kinde_fastapi/examples/example_app.py +33 -0
- kinde_fastapi/examples/management_token_example.py +119 -0
- {kinde_python_sdk-2.0.7.dist-info → kinde_python_sdk-2.0.9.dist-info}/METADATA +1 -1
- {kinde_python_sdk-2.0.7.dist-info → kinde_python_sdk-2.0.9.dist-info}/RECORD +10 -9
- kinde_sdk/auth/claims.py +2 -2
- kinde_sdk/auth/token_manager.py +37 -8
- kinde_sdk/management/management_token_manager.py +64 -7
- {kinde_python_sdk-2.0.7.dist-info → kinde_python_sdk-2.0.9.dist-info}/WHEEL +0 -0
- {kinde_python_sdk-2.0.7.dist-info → kinde_python_sdk-2.0.9.dist-info}/licenses/LICENSE +0 -0
- {kinde_python_sdk-2.0.7.dist-info → kinde_python_sdk-2.0.9.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,8 @@ import logging
|
|
|
9
9
|
from kinde_sdk.auth.oauth import OAuth
|
|
10
10
|
from kinde_sdk.auth import claims, feature_flags, permissions, tokens
|
|
11
11
|
from kinde_sdk.management import ManagementClient;
|
|
12
|
+
from kinde_sdk.management.management_token_manager import ManagementTokenManager
|
|
13
|
+
import requests
|
|
12
14
|
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
@@ -78,6 +80,7 @@ async def home(request: Request):
|
|
|
78
80
|
<p>tokens: {tokens.get_token_manager().get_access_token()}</p>
|
|
79
81
|
<p>users: {user_count} user(s) found</p>
|
|
80
82
|
<p>You are logged in.</p>
|
|
83
|
+
<a href="/call_management_users">Call Management Users</a>
|
|
81
84
|
<a href="/logout">Logout</a>
|
|
82
85
|
</body>
|
|
83
86
|
</html>
|
|
@@ -92,6 +95,36 @@ async def home(request: Request):
|
|
|
92
95
|
</html>
|
|
93
96
|
"""
|
|
94
97
|
|
|
98
|
+
@app.get("/call_management_users")
|
|
99
|
+
async def call_management_users():
|
|
100
|
+
if not kinde_oauth.is_authenticated():
|
|
101
|
+
return {"error": "Not authenticated"}
|
|
102
|
+
|
|
103
|
+
domain = os.getenv("KINDE_DOMAIN")
|
|
104
|
+
client_id = os.getenv("KINDE_MANAGEMENT_CLIENT_ID")
|
|
105
|
+
client_secret = os.getenv("KINDE_MANAGEMENT_CLIENT_SECRET")
|
|
106
|
+
|
|
107
|
+
if not all([domain, client_id, client_secret]):
|
|
108
|
+
return {"error": "Missing management credentials"}
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
token_manager = ManagementTokenManager(
|
|
112
|
+
domain=domain,
|
|
113
|
+
client_id=client_id,
|
|
114
|
+
client_secret=client_secret
|
|
115
|
+
)
|
|
116
|
+
access_token = token_manager.get_access_token()
|
|
117
|
+
|
|
118
|
+
headers = {
|
|
119
|
+
"Authorization": f"Bearer {access_token}"
|
|
120
|
+
}
|
|
121
|
+
response = requests.get("http://localhost:8000/management/users", headers=headers)
|
|
122
|
+
response.raise_for_status()
|
|
123
|
+
return response.json()
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"Failed to call management users: {e}")
|
|
126
|
+
return {"error": str(e)}
|
|
127
|
+
|
|
95
128
|
if __name__ == "__main__":
|
|
96
129
|
import uvicorn
|
|
97
130
|
uvicorn.run(app, host="127.0.0.1", port=5000)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from fastapi import FastAPI, Depends, HTTPException, status
|
|
2
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
3
|
+
import os
|
|
4
|
+
import requests
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from kinde_sdk.management.management_token_manager import ManagementTokenManager
|
|
7
|
+
from kinde_sdk.management import ManagementClient
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
# Load environment variables
|
|
14
|
+
load_dotenv()
|
|
15
|
+
|
|
16
|
+
# Setup logging
|
|
17
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Initialize FastAPI app
|
|
21
|
+
app = FastAPI(title="Kinde Management Token Example with Introspection")
|
|
22
|
+
|
|
23
|
+
# Security scheme for bearer token
|
|
24
|
+
security = HTTPBearer()
|
|
25
|
+
|
|
26
|
+
# Extract, introspect, and validate management token from header
|
|
27
|
+
def get_management_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> ManagementTokenManager:
|
|
28
|
+
logger.debug("Starting token validation")
|
|
29
|
+
bearer_token = credentials.credentials
|
|
30
|
+
logger.debug(f"Received bearer token (first 20 chars): {bearer_token[:20]}...")
|
|
31
|
+
|
|
32
|
+
# SDK config from env
|
|
33
|
+
domain = os.getenv("KINDE_HOST", "https://app.kinde.com")
|
|
34
|
+
logger.debug(f"Raw domain from env: {domain}")
|
|
35
|
+
if domain.startswith(('http://', 'https://')):
|
|
36
|
+
domain = domain.split('://', 1)[1]
|
|
37
|
+
logger.debug(f"Normalized domain: {domain}")
|
|
38
|
+
client_id = os.getenv("KINDE_MANAGEMENT_CLIENT_ID")
|
|
39
|
+
client_secret = os.getenv("KINDE_MANAGEMENT_CLIENT_SECRET")
|
|
40
|
+
logger.debug(f"Client ID: {client_id}")
|
|
41
|
+
# Not logging secret for security
|
|
42
|
+
|
|
43
|
+
if not all([domain, client_id, client_secret]):
|
|
44
|
+
logger.error("Missing Kinde management credentials")
|
|
45
|
+
raise HTTPException(
|
|
46
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
47
|
+
detail="Missing Kinde management credentials in environment"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
token_manager = ManagementTokenManager(
|
|
52
|
+
domain=domain,
|
|
53
|
+
client_id=client_id,
|
|
54
|
+
client_secret=client_secret
|
|
55
|
+
)
|
|
56
|
+
logger.debug(f"ManagementTokenManager instantiated {bearer_token}")
|
|
57
|
+
|
|
58
|
+
introspection_result = token_manager.validate_and_set_via_introspection(bearer_token)
|
|
59
|
+
logger.debug(f"Introspection result: {introspection_result}")
|
|
60
|
+
|
|
61
|
+
access_token = token_manager.get_access_token()
|
|
62
|
+
if not access_token:
|
|
63
|
+
logger.error("No access token after introspection")
|
|
64
|
+
raise ValueError("Invalid management token after introspection")
|
|
65
|
+
logger.debug("Access token obtained successfully")
|
|
66
|
+
|
|
67
|
+
return token_manager
|
|
68
|
+
|
|
69
|
+
except ValueError as e:
|
|
70
|
+
logger.error(f"ValueError in token validation: {str(e)}")
|
|
71
|
+
raise HTTPException(
|
|
72
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
73
|
+
detail=str(e),
|
|
74
|
+
headers={"WWW-Authenticate": "Bearer"}
|
|
75
|
+
)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(f"Exception in token validation: {str(e)}", exc_info=True)
|
|
78
|
+
raise HTTPException(
|
|
79
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
80
|
+
detail=f"Token introspection failed: {str(e)}",
|
|
81
|
+
headers={"WWW-Authenticate": "Bearer"}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Example route using the validated management token
|
|
85
|
+
@app.get("/management/users")
|
|
86
|
+
async def get_users(token_manager: ManagementTokenManager = Depends(get_management_token)):
|
|
87
|
+
logger.debug("Entering get_users endpoint")
|
|
88
|
+
try:
|
|
89
|
+
# Create ManagementClient with the token manager
|
|
90
|
+
management_client = ManagementClient(
|
|
91
|
+
domain=token_manager.domain,
|
|
92
|
+
client_id=token_manager.client_id,
|
|
93
|
+
client_secret=token_manager.client_secret
|
|
94
|
+
)
|
|
95
|
+
logger.debug("ManagementClient created")
|
|
96
|
+
|
|
97
|
+
# Fetch users (example API call)
|
|
98
|
+
users_response = management_client.get_users()
|
|
99
|
+
|
|
100
|
+
# Get the user count from the response
|
|
101
|
+
user_count = len(users_response.users) if users_response.users else 0
|
|
102
|
+
logger.debug(f"Fetched {user_count} users")
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
"message": "Users fetched successfully",
|
|
106
|
+
"user_count": user_count,
|
|
107
|
+
"users": users_response.users if users_response.users else [] # In production, filter sensitive data
|
|
108
|
+
}
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.error(f"Error in get_users: {str(e)}", exc_info=True)
|
|
111
|
+
raise HTTPException(
|
|
112
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
113
|
+
detail=f"Failed to fetch users: {str(e)}"
|
|
114
|
+
) from e
|
|
115
|
+
|
|
116
|
+
# Run the app
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
import uvicorn
|
|
119
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
kinde_fastapi/__init__.py,sha256=FzB0zDBbzaLLvGZBPrBZN94I6LRZdkT4RSmGUsbh3mA,470
|
|
2
|
-
kinde_fastapi/examples/example_app.py,sha256=
|
|
2
|
+
kinde_fastapi/examples/example_app.py,sha256=c29SsAytpDs75CVJuiaInLK8lnqMLmoDDvExtRI-SYM,4270
|
|
3
|
+
kinde_fastapi/examples/management_token_example.py,sha256=keO-eKBrrMUTcIom154Mmn7BLRSZ45jYvQPwT-Jeoqw,4580
|
|
3
4
|
kinde_fastapi/examples/session.py,sha256=WblGM7TM2JCKdSFsd2OmSP0daexZ7E6ZS1xgTQY_etY,2472
|
|
4
5
|
kinde_fastapi/framework/__init__.py,sha256=tpTI84q7HCHMlREslMrngIlKwC8CDog9bEMI_ydhCUs,181
|
|
5
6
|
kinde_fastapi/framework/fastapi_framework.py,sha256=-MiBFddfY3NiB9BE_b8BBYAxfGbhOQKO-RXUsCu4a3U,6767
|
|
@@ -13,7 +14,7 @@ kinde_flask/framework/flask_framework.py,sha256=I4dVWmkyd0FBqjAkOyftebm7_qpI9uxk
|
|
|
13
14
|
kinde_flask/framework/flask_framework_factory.py,sha256=ALpsj-cHBox3ktRxi-CyZ5Q6EOvacfdu4pvlTYkuviA,802
|
|
14
15
|
kinde_flask/middleware/framework_middleware.py,sha256=XFK6kgMDuHkrawDJ0_MMgLO7es0YQVjlVDDRDgwlCJ0,1012
|
|
15
16
|
kinde_flask/storage/flask_storage_factory.py,sha256=MitOUbfCbfvSQFFoUbbnbXLEOL_qajS29Mlsn0TyTsY,2562
|
|
16
|
-
kinde_python_sdk-2.0.
|
|
17
|
+
kinde_python_sdk-2.0.9.dist-info/licenses/LICENSE,sha256=iT6AIO6NJn_mo0kDD5mpz2zp9GpzH6YdhqOmkCBg-kQ,1385
|
|
17
18
|
kinde_sdk/__init__.py,sha256=Mh3eVcnJDk-KdQfxVUsF19NgBYexoJCwG5iAbD1LEdQ,1052
|
|
18
19
|
kinde_sdk/api_client.py,sha256=GEb1BIjvIrUe8mFIr7hppp2PO604BiSlAr0_TYBfuy4,58496
|
|
19
20
|
kinde_sdk/configuration.py,sha256=oqZGvKEf4kNNURbI1pip2ZVVcDyRTSmmnjXWziH4s80,15765
|
|
@@ -109,7 +110,7 @@ kinde_sdk/apis/tags/users_api.py,sha256=06qeWo8ZNNZvKQ5hl0ImtPvNSbzDKkceZj7ACrlu
|
|
|
109
110
|
kinde_sdk/apis/tags/webhooks_api.py,sha256=AmLDLeW6aLz3lnqUGGd2dpVxuVKxW1KgIW2rdtUNJmw,925
|
|
110
111
|
kinde_sdk/auth/__init__.py,sha256=czQs-NJ3R22EXkcnszl3YJKmjsB-mVpJ4T11zbRb_4Y,379
|
|
111
112
|
kinde_sdk/auth/base_auth.py,sha256=hV-xjaGa35YMDFpmEWQgTNM7w6ZHskLm_p5cnXSSLJY,1253
|
|
112
|
-
kinde_sdk/auth/claims.py,sha256=
|
|
113
|
+
kinde_sdk/auth/claims.py,sha256=sS4cvtEYQyTlT4WacibyKzp-5eTTH4JbMnDabMTX31w,1560
|
|
113
114
|
kinde_sdk/auth/config_loader.py,sha256=L01kqmzUMKQ0X_PA9MYcWSeJiVoDfrB4RANQGWS76UA,879
|
|
114
115
|
kinde_sdk/auth/enums.py,sha256=Xbk7jwXtq_TViZSfBy6h9lon8VEA5v6ssUl-3yKp8Zk,538
|
|
115
116
|
kinde_sdk/auth/feature_flags.py,sha256=VSqfyqNmYLo3Q74nyo4L8W8jt8msZ_v-45Y7pW_48VA,3810
|
|
@@ -117,7 +118,7 @@ kinde_sdk/auth/login_options.py,sha256=3Fvbo-rjLzbV14Np48Ogw0HieThjlRtfP4VHztJtG
|
|
|
117
118
|
kinde_sdk/auth/oauth.py,sha256=bXWM6Mo7H8EgrD2GSOfhibvwz6MVMPe7-wM1PaL6_6o,26136
|
|
118
119
|
kinde_sdk/auth/permissions.py,sha256=A9BmGt3OokuvS-4twveCZdCPFY-QobyzqlgGVo8eSoI,1913
|
|
119
120
|
kinde_sdk/auth/portals.py,sha256=Z3goLvio-Xw7VjTRrQZXKOFkTMvtF78nKzFF5-u4rkY,4530
|
|
120
|
-
kinde_sdk/auth/token_manager.py,sha256=
|
|
121
|
+
kinde_sdk/auth/token_manager.py,sha256=HSfyKmc4kyoQz2NeRPVWtpY4E4rtf2w6SWArRlQ7-5s,8278
|
|
121
122
|
kinde_sdk/auth/tokens.py,sha256=eZKJTTw764FrzvxurWNee1_ZX1Q2FQjJUXBlsMlp2A0,2367
|
|
122
123
|
kinde_sdk/auth/user_session.py,sha256=CmFotC9ONzHwPMRJy4SOT7tOI_63dHv_ttpdNHssH8Y,7657
|
|
123
124
|
kinde_sdk/core/__init__.py,sha256=ejO0P_oXuiO5V-MJVRqKu6y0fAWKyi4iL2UIRGXhLy0,358
|
|
@@ -141,7 +142,7 @@ kinde_sdk/management/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9
|
|
|
141
142
|
kinde_sdk/management/configuration.py,sha256=icAfQBYmokmspDa_ELha2Qfeq_IkgAp6COdmw1ZpL2Q,18909
|
|
142
143
|
kinde_sdk/management/exceptions.py,sha256=I_xzvCXjHszPUrUr4dNsE9Pg-nH58YszF075NvjSRMA,6780
|
|
143
144
|
kinde_sdk/management/management_client.py,sha256=_GErLV96nTbWw58fscCrDe_UOVk25u4Vlx0amFp3g6Y,31467
|
|
144
|
-
kinde_sdk/management/management_token_manager.py,sha256=
|
|
145
|
+
kinde_sdk/management/management_token_manager.py,sha256=KP1UVjTFdjJlLMRYn2fzJOS2QgEuaQaTg56EmQs22uE,12106
|
|
145
146
|
kinde_sdk/management/rest.py,sha256=7kMCS7NAOYCI4hrThABWpV-AN0Y-LuCjMnJ404CJdq8,9783
|
|
146
147
|
kinde_sdk/management/schemas.py,sha256=6tg1tEh8IL-9UuZiWFVe-0Pu-TD-CYW37soeQ6AyspA,97671
|
|
147
148
|
kinde_sdk/management/api/__init__.py,sha256=-LP_IUIY_BztruZ5aTT7WoTgolTlSxOgppthL4I2l8A,2018
|
|
@@ -722,7 +723,7 @@ kinde_sdk/test/test_models/test_user_profile_v2.py,sha256=QzYjvjtTGa1ktE5ruyGBm_
|
|
|
722
723
|
kinde_sdk/test/test_models/test_users.py,sha256=wJQR5K7dsfHQQsOQp4mrHWQynFPHwL-gDtbgVifNfMs,537
|
|
723
724
|
kinde_sdk/test/test_models/test_users_response.py,sha256=BCcKxe9GctDrOXOzw0Q7O1ijKy_93Oj3E2wHW00y624,869
|
|
724
725
|
kinde_sdk/test/test_models/test_webhook.py,sha256=KNfR8sKAK5H2vX_cXYGT5EfgaVP7_egC39uY6-s-4qo,544
|
|
725
|
-
kinde_python_sdk-2.0.
|
|
726
|
-
kinde_python_sdk-2.0.
|
|
727
|
-
kinde_python_sdk-2.0.
|
|
728
|
-
kinde_python_sdk-2.0.
|
|
726
|
+
kinde_python_sdk-2.0.9.dist-info/METADATA,sha256=-T2f9gM_AfnugRp4TestVW7eWIeB3Te9rsXRr57CNDs,23482
|
|
727
|
+
kinde_python_sdk-2.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
728
|
+
kinde_python_sdk-2.0.9.dist-info/top_level.txt,sha256=TUU3EVjV6O4brF-mooQr6pnTk-jXJphmIWRHCIdyIgI,36
|
|
729
|
+
kinde_python_sdk-2.0.9.dist-info/RECORD,,
|
kinde_sdk/auth/claims.py
CHANGED
|
@@ -24,7 +24,7 @@ class Claims(BaseAuth):
|
|
|
24
24
|
"value": None
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
claims = token_manager.get_claims()
|
|
27
|
+
claims = token_manager.get_claims(token_type)
|
|
28
28
|
value = claims.get(claim_name)
|
|
29
29
|
|
|
30
30
|
return {
|
|
@@ -46,7 +46,7 @@ class Claims(BaseAuth):
|
|
|
46
46
|
if not token_manager:
|
|
47
47
|
return {}
|
|
48
48
|
|
|
49
|
-
return token_manager.get_claims()
|
|
49
|
+
return token_manager.get_claims(token_type)
|
|
50
50
|
|
|
51
51
|
# Create a singleton instance
|
|
52
52
|
claims = Claims()
|
kinde_sdk/auth/token_manager.py
CHANGED
|
@@ -153,17 +153,46 @@ class TokenManager:
|
|
|
153
153
|
"""Get the ID token if available."""
|
|
154
154
|
return self.tokens.get("id_token")
|
|
155
155
|
|
|
156
|
-
def get_claims(self):
|
|
157
|
-
"""Get the claims from the
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
def get_claims(self, token_type: str = "access_token"):
|
|
157
|
+
"""Get the claims from the specified token type.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
token_type (str): The type of token to get claims from.
|
|
161
|
+
Valid values are "access_token" or "id_token".
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
dict: The claims from the specified token, or empty dict if not available.
|
|
165
|
+
"""
|
|
166
|
+
# Validate token type
|
|
167
|
+
valid_token_types = ["access_token", "id_token"]
|
|
168
|
+
if token_type not in valid_token_types:
|
|
169
|
+
logging.warning(f"Invalid token_type '{token_type}'. Valid types are: {valid_token_types}")
|
|
170
|
+
return {}
|
|
171
|
+
|
|
172
|
+
# Use f-string for safer string formatting
|
|
173
|
+
claims_key = f"{token_type}_claims"
|
|
174
|
+
claims = self.tokens.get(claims_key, {})
|
|
175
|
+
|
|
160
176
|
if not claims:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if not claims:
|
|
164
|
-
logging.warning("No claims available in token manager")
|
|
177
|
+
logging.warning(f"No claims available for token type: {token_type}")
|
|
178
|
+
|
|
165
179
|
return claims
|
|
166
180
|
|
|
181
|
+
def get_claim(self, key: str, token_type: str = "access_token"):
|
|
182
|
+
"""Get a specific claim from the specified token type.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
key (str): The claim key to retrieve
|
|
186
|
+
token_type (str): The type of token to get the claim from.
|
|
187
|
+
Valid values are "access_token" or "id_token".
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
dict: Dictionary containing the claim name and value, or empty dict if not found.
|
|
191
|
+
"""
|
|
192
|
+
claims = self.get_claims(token_type)
|
|
193
|
+
value = claims.get(key)
|
|
194
|
+
return {"name": key, "value": value}
|
|
195
|
+
|
|
167
196
|
def revoke_token(self):
|
|
168
197
|
""" Revoke the current access token. """
|
|
169
198
|
if "access_token" not in self.tokens:
|
|
@@ -209,13 +209,15 @@ class ManagementTokenManager:
|
|
|
209
209
|
def set_tokens(self, token_data: Dict[str, Any]):
|
|
210
210
|
""" Store tokens with expiration. """
|
|
211
211
|
with self.lock:
|
|
212
|
-
# Handle None values by using default
|
|
213
|
-
expires_in = token_data.get("expires_in")
|
|
212
|
+
# Handle None values by using default, but allow 0
|
|
213
|
+
expires_in = token_data.get("expires_in")
|
|
214
|
+
if expires_in is None:
|
|
215
|
+
expires_in = 3600
|
|
214
216
|
token_type = token_data.get("token_type") or "Bearer"
|
|
215
217
|
self.tokens = {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
"access_token": token_data.get("access_token"),
|
|
219
|
+
"expires_at": time.time() + expires_in,
|
|
220
|
+
"token_type": token_type
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
def get_access_token(self):
|
|
@@ -265,9 +267,64 @@ class ManagementTokenManager:
|
|
|
265
267
|
except requests.exceptions.Timeout:
|
|
266
268
|
raise Exception(f"Token request timed out after 30 seconds for domain {self.domain}")
|
|
267
269
|
except requests.exceptions.RequestException as e:
|
|
268
|
-
raise Exception(f"Token request failed for domain {self.domain}: {str(e)}")
|
|
270
|
+
raise Exception(f"Token request failed for domain {self.domain}: {str(e)}") from e
|
|
269
271
|
|
|
270
272
|
def clear_tokens(self):
|
|
271
273
|
""" Clear stored tokens. """
|
|
272
274
|
with self.lock:
|
|
273
|
-
self.tokens = {}
|
|
275
|
+
self.tokens = {}
|
|
276
|
+
|
|
277
|
+
def validate_and_set_via_introspection(self, bearer_token: str) -> Dict[str, Any]:
|
|
278
|
+
"""
|
|
279
|
+
Validate a bearer token via introspection and set it if valid.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
bearer_token: The bearer token to introspect and validate.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Dict: The introspection result if valid.
|
|
286
|
+
|
|
287
|
+
Raises:
|
|
288
|
+
Exception: If introspection fails or token is invalid.
|
|
289
|
+
"""
|
|
290
|
+
# First, get a management token using client credentials
|
|
291
|
+
management_token = self.get_access_token()
|
|
292
|
+
|
|
293
|
+
introspection_url = f"https://{self.domain}/oauth2/introspect"
|
|
294
|
+
introspect_data = {
|
|
295
|
+
"token": bearer_token
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
response = requests.post(
|
|
300
|
+
introspection_url,
|
|
301
|
+
data=introspect_data,
|
|
302
|
+
headers={
|
|
303
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
304
|
+
"Authorization": f"Bearer {management_token}",
|
|
305
|
+
"Kinde-SDK": self._generate_tracking_header()
|
|
306
|
+
},
|
|
307
|
+
timeout=30
|
|
308
|
+
)
|
|
309
|
+
response.raise_for_status()
|
|
310
|
+
introspection_result = response.json()
|
|
311
|
+
|
|
312
|
+
if not introspection_result.get("active", False):
|
|
313
|
+
raise ValueError("Token is inactive or invalid")
|
|
314
|
+
|
|
315
|
+
# Set the validated token - calculate proper expiration
|
|
316
|
+
exp_time = introspection_result.get("exp")
|
|
317
|
+
expires_in = max(0, exp_time - int(time.time())) if exp_time else 3600
|
|
318
|
+
|
|
319
|
+
token_data = {
|
|
320
|
+
"access_token": bearer_token,
|
|
321
|
+
"expires_in": expires_in
|
|
322
|
+
}
|
|
323
|
+
self.set_tokens(token_data)
|
|
324
|
+
|
|
325
|
+
return introspection_result
|
|
326
|
+
|
|
327
|
+
except requests.exceptions.Timeout:
|
|
328
|
+
raise Exception(f"Introspection request timed out after 30 seconds for domain {self.domain}") from None
|
|
329
|
+
except requests.exceptions.RequestException as e:
|
|
330
|
+
raise Exception(f"Introspection request failed for domain {self.domain}: {str(e)}") from e
|
|
File without changes
|
|
File without changes
|
|
File without changes
|