verge-auth-sdk 0.1.18__py3-none-any.whl → 0.1.38__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.

Potentially problematic release.


This version of verge-auth-sdk might be problematic. Click here for more details.

@@ -2,7 +2,7 @@
2
2
  """
3
3
  verge_auth_sdk package public API.
4
4
  Exports:
5
- - add_central_auth(app) -> middleware to attach to FastAPI app
5
+ - add_central_auth(app) -> middleware to attach to Python app
6
6
  - get_secret(name) -> secret retrieval helper
7
7
  """
8
8
  from .middleware import add_central_auth
@@ -2,139 +2,352 @@ from fastapi import FastAPI, Request
2
2
  from fastapi.responses import RedirectResponse, JSONResponse
3
3
  import httpx
4
4
  import os
5
-
5
+ import asyncio
6
+ import jwt
7
+ from typing import List
8
+ from urllib.parse import quote
6
9
  from .secret_provider import get_secret
7
10
  from .verge_routes import router as verge_routes_router
8
11
 
9
- REGISTERED_ROUTES = []
12
+ REGISTERED_ROUTES: List = []
10
13
 
14
+ # ============================================================
15
+ # GLOBAL JWT CACHE
16
+ # ============================================================
17
+ JWT_PUBLIC_KEY: str | None = None
18
+ JWT_KEY_ID: str | None = None
19
+ JWT_ALGORITHMS = ["RS256"]
11
20
 
12
- def add_central_auth(app: FastAPI):
13
21
 
14
- AUTH_INTROSPECT_URL = os.getenv("AUTH_INTROSPECT_URL")
15
- AUTH_LOGIN_URL = os.getenv("AUTH_LOGIN_URL")
22
+ # -----------------------------------------------------------
23
+ # HTTP helper with retries
24
+ # -----------------------------------------------------------
25
+ async def _post_with_retries(
26
+ client,
27
+ url,
28
+ json=None,
29
+ headers=None,
30
+ timeout=10,
31
+ retries=8,
32
+ backoff=1,
33
+ ):
34
+ last_exc = None
35
+ for attempt in range(1, retries + 1):
36
+ try:
37
+ resp = await client.post(
38
+ url,
39
+ json=json,
40
+ headers=headers,
41
+ timeout=timeout,
42
+ )
43
+ return resp
44
+ except Exception as e:
45
+ last_exc = e
46
+ print(
47
+ f"❗ Retry {attempt}/{retries} failed for {url}: "
48
+ f"{type(e).__name__}: {e}"
49
+ )
50
+ await asyncio.sleep(backoff * attempt)
51
+ raise last_exc
16
52
 
17
- CLIENT_ID = os.getenv("VERGE_CLIENT_ID")
18
- CLIENT_SECRET = os.getenv("VERGE_CLIENT_SECRET")
19
53
 
54
+ # -----------------------------------------------------------
55
+ # PUBLIC KEY DISCOVERY
56
+ # -----------------------------------------------------------
57
+ async def load_public_key(force: bool = False):
58
+ """
59
+ Loads and caches the JWT public key from auth-service.
60
+ Safe to call multiple times.
61
+ """
62
+ global JWT_PUBLIC_KEY, JWT_KEY_ID
63
+
64
+ if JWT_PUBLIC_KEY and not force:
65
+ return
66
+
67
+ AUTH_PUBLIC_KEY_URL = os.getenv("AUTH_PUBLIC_KEY_URL")
68
+ if not AUTH_PUBLIC_KEY_URL:
69
+ print("❌ AUTH_PUBLIC_KEY_URL not set! Please Set it.")
70
+ return
71
+
72
+ try:
73
+ async with httpx.AsyncClient(timeout=5) as client:
74
+ resp = await client.get(AUTH_PUBLIC_KEY_URL)
75
+ resp.raise_for_status()
76
+ data = resp.json()
77
+
78
+ JWT_PUBLIC_KEY = data.get("public_key")
79
+ JWT_KEY_ID = data.get("kid")
80
+
81
+ if JWT_PUBLIC_KEY:
82
+ print("✅ Security Key Loaded Successfully")
83
+ else:
84
+ print("❌ Security Key Loading Failed")
85
+
86
+ except Exception as e:
87
+ print("❌ Failed to load Security Key:", str(e))
88
+
89
+
90
+ # -----------------------------------------------------------
91
+ # MAIN INTEGRATION
92
+ # -----------------------------------------------------------
93
+ def add_central_auth(app: FastAPI):
94
+ AUTH_BASE_URL = os.getenv("AUTH_BASE_URL")
95
+ AUTH_LOGIN_URL = os.getenv("AUTH_LOGIN_URL")
20
96
  SERVICE_NAME = os.getenv("SERVICE_NAME")
21
97
  SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")
98
+ CLIENT_ID = os.getenv("VERGE_CLIENT_ID")
99
+ CLIENT_SECRET = os.getenv("VERGE_CLIENT_SECRET")
100
+ VERGE_SERVICE_SECRET = get_secret("VERGE_SERVICE_SECRET")
22
101
  AUTH_REGISTER_URL = os.getenv("AUTH_REGISTER_URL")
102
+ AUTH_ROUTE_SYNC_URL = os.getenv("AUTH_ROUTE_SYNC_URL")
103
+ INTROSPECT_URL = os.getenv("AUTH_INTROSPECT_URL")
23
104
 
24
- VERGE_SECRET = get_secret("VERGE_SERVICE_SECRET")
105
+ # -------------------------------------------------------
106
+ # INTERNAL VERGE ROUTES
107
+ # -------------------------------------------------------
25
108
 
26
- # ---------------------------------------
27
- # INTERNAL ROUTES (MUST LOAD FIRST)
28
- # ---------------------------------------
29
109
  app.include_router(verge_routes_router)
30
110
 
31
- # ---------------------------------------
32
- # STARTUP ONLY COLLECT ROUTES (NO REGISTER)
33
- # ---------------------------------------
111
+ # -------------------------------------------------------
112
+ # MICROSERVICE BOOTSTRAP ON STARTUP
113
+ # -------------------------------------------------------
34
114
  @app.on_event("startup")
35
- async def collect_routes():
36
- print("🔥 Verge bootstrap started")
115
+ async def verge_bootstrap():
116
+ print("🔥 Verge Auth started")
117
+
118
+ # 🔐 Load JWT public key FIRST (retry-safe)
119
+ await load_public_key(force=True)
120
+
121
+ await asyncio.sleep(2)
37
122
  REGISTERED_ROUTES.clear()
38
123
 
124
+ print("📌 Collecting routes...")
125
+
39
126
  for route in app.routes:
40
- path = getattr(route, "path", None)
41
- methods = getattr(route, "methods", [])
42
-
43
- if not path:
44
- continue
45
-
46
- if path.startswith(("/docs", "/openapi", "/__verge__")):
47
- continue
48
-
49
- for m in methods:
50
- if m in ("GET", "POST", "PUT", "PATCH", "DELETE"):
51
- REGISTERED_ROUTES.append({"path": path, "method": m})
52
-
53
- print("✅ Collected Routes:", REGISTERED_ROUTES)
54
-
55
- # ---------------------------------------
56
- # SUPER ADMIN TRIGGERED REGISTRATION
57
- # ---------------------------------------
58
- async def register_with_auth():
59
- print("🔧 Triggered register_with_auth()")
60
- print("📍 AUTH_REGISTER_URL =", AUTH_REGISTER_URL)
61
- print("📍 SERVICE_NAME =", SERVICE_NAME)
62
- print("📍 SERVICE_BASE_URL =", SERVICE_BASE_URL)
63
- print("📍 CLIENT_ID =", CLIENT_ID)
64
- print("📍 ROUTES =", REGISTERED_ROUTES)
65
- try:
66
- async with httpx.AsyncClient(timeout=20) as client:
67
- resp = await client.post(
68
- AUTH_REGISTER_URL,
69
- params={
70
- "name": SERVICE_NAME,
71
- "base_url": SERVICE_BASE_URL,
72
- "client_id": CLIENT_ID,
73
- "client_secret": CLIENT_SECRET
74
- },
75
- headers={"X-Verge-Service-Secret": VERGE_SECRET}
76
- )
127
+ try:
128
+ path = getattr(route, "path", None)
129
+ methods = getattr(route, "methods", [])
77
130
 
78
- print(f"📡 Registration: {resp.status_code}")
79
- print(resp.text)
131
+ if not path:
132
+ continue
80
133
 
81
- except Exception as e:
82
- print("❌ Registration failed:", e)
134
+ if path.startswith(("/docs", "/openapi", "/__verge__")):
135
+ continue
136
+
137
+ for m in methods:
138
+ if m in ("GET", "POST", "PUT", "PATCH", "DELETE"):
139
+ REGISTERED_ROUTES.append(
140
+ {"path": path, "method": m}
141
+ )
83
142
 
84
- # ---------------------------------------
85
- # MIDDLEWARE AUTH
86
- # ---------------------------------------
143
+ except Exception as e:
144
+ print("❌ Error collecting route:", e)
145
+
146
+ print("\n📡 Registering service with Verge Auth...")
147
+
148
+ async with httpx.AsyncClient() as client:
149
+ if AUTH_REGISTER_URL:
150
+ try:
151
+ resp = await _post_with_retries(
152
+ client,
153
+ AUTH_REGISTER_URL,
154
+ json={
155
+ "service_name": SERVICE_NAME,
156
+ "base_url": SERVICE_BASE_URL,
157
+ },
158
+ headers={
159
+ "X-Client-Id": CLIENT_ID or "",
160
+ "X-Client-Secret": CLIENT_SECRET or "",
161
+ "X-Verge-Service-Secret": VERGE_SERVICE_SECRET or "",
162
+ },
163
+ )
164
+ print(
165
+ "📡 Registration response:",
166
+ resp.status_code,
167
+ resp.text,
168
+ )
169
+ except Exception as e:
170
+ print("❌ Registration failed:", e)
171
+
172
+ if AUTH_ROUTE_SYNC_URL:
173
+ try:
174
+ resp = await _post_with_retries(
175
+ client,
176
+ AUTH_ROUTE_SYNC_URL,
177
+ json={
178
+ "service_name": SERVICE_NAME,
179
+ "base_url": SERVICE_BASE_URL,
180
+ "routes": REGISTERED_ROUTES,
181
+ },
182
+ headers={
183
+ "X-Client-Id": CLIENT_ID or "",
184
+ "X-Client-Secret": CLIENT_SECRET or "",
185
+ "X-Verge-Service-Secret": VERGE_SERVICE_SECRET or "",
186
+ },
187
+ timeout=20,
188
+ )
189
+ print(
190
+ "📡 Route sync response:",
191
+ resp.status_code,
192
+ resp.text,
193
+ )
194
+ except Exception as e:
195
+ print("❌ Route sync failed:", e)
196
+
197
+ # -------------------------------------------------------
198
+ # CENTRAL AUTHZ MIDDLEWARE
199
+ # -------------------------------------------------------
87
200
  @app.middleware("http")
88
201
  async def central_auth(request: Request, call_next):
89
202
  path = request.url.path
90
203
 
91
- # Whitelisted paths
92
- if path.startswith("/__verge__") or path in {"/docs", "/openapi.json", "/health"}:
93
- return await call_next(request)
204
+ SKIP_PATHS = {
205
+ "/health",
206
+ # "/docs",
207
+ # "/redoc",
208
+ "/openapi.json",
209
+ "/favicon.ico",
210
+ "/service-registry/register",
211
+ "/route-sync",
212
+ "/__verge__",
213
+ }
94
214
 
95
- # Extract token
96
- token = None
97
- auth_header = request.headers.get("authorization")
215
+ if path in SKIP_PATHS or path.startswith("/__verge__"):
216
+ return await call_next(request)
98
217
 
99
- if auth_header and auth_header.lower().startswith("bearer "):
100
- token = auth_header.split(" ")[1]
218
+ token = request.cookies.get("verge_access")
101
219
 
220
+ # Then Authorization header
102
221
  if not token:
103
- token = request.cookies.get("access_token")
222
+ auth_header = request.headers.get("authorization")
223
+ if auth_header and auth_header.lower().startswith("bearer "):
224
+ token = auth_header.split(" ", 1)[1].strip()
104
225
 
105
- # redirect if HTML request
106
- if not token and "text/html" in request.headers.get("accept", ""):
107
- return RedirectResponse(
108
- f"{AUTH_LOGIN_URL}?redirect_url={request.url}"
109
- )
226
+ # Then session (optional fallback)
227
+ if not token and "session" in request.scope:
228
+ token = request.scope["session"].get("access_token")
110
229
 
111
- if not token:
112
- return JSONResponse({"detail": "Unauthorized"}, status_code=401)
230
+ # ---------------------------------------------------
231
+ # AUTH CODE EXCHANGE
232
+ # ---------------------------------------------------
233
+ if not token and "code" in request.query_params:
234
+ code = request.query_params.get("code")
235
+
236
+ try:
237
+ async with httpx.AsyncClient(timeout=5) as client:
238
+ resp = await client.post(
239
+ f"{AUTH_BASE_URL}/auth/exchange",
240
+ json={"code": code},
241
+ headers={
242
+ "X-Client-Id": os.getenv("VERGE_CLIENT_ID") or "",
243
+ "X-Client-Secret": os.getenv("VERGE_CLIENT_SECRET") or "",
244
+ },
245
+ )
246
+ resp.raise_for_status()
247
+ token = resp.json().get("access_token")
248
+
249
+ if not token:
250
+ return JSONResponse(
251
+ {"detail": "Authorization failed: no token returned"},
252
+ status_code=401,
253
+ )
254
+
255
+ # Persist token + clean URL
256
+ clean_url = str(request.url.remove_query_params("code"))
257
+ response = RedirectResponse(clean_url)
258
+ response.set_cookie(
259
+ "verge_access",
260
+ token,
261
+ httponly=True,
262
+ secure=True,
263
+ samesite="lax",
264
+ path="/",
265
+ )
266
+ return response
113
267
 
114
- # Validate token
268
+ except Exception as e:
269
+ return JSONResponse(
270
+ {"detail": "Authorization failed", "error": str(e)},
271
+ status_code=401,
272
+ )
273
+
274
+ # ---------------------------------------------------
275
+ # LOCAL JWT VERIFICATION
276
+ # ---------------------------------------------------
115
277
  try:
116
- async with httpx.AsyncClient(timeout=4) as client:
117
- res = await client.post(
118
- AUTH_INTROSPECT_URL,
119
- headers={
120
- "Authorization": f"Bearer {token}",
121
- "X-Client-Id": CLIENT_ID,
122
- "X-Client-Secret": CLIENT_SECRET,
123
- },
278
+ if not JWT_PUBLIC_KEY:
279
+ await load_public_key(force=True)
280
+
281
+ if not JWT_PUBLIC_KEY:
282
+ return JSONResponse(
283
+ {"detail": "Auth key not ready"},
284
+ status_code=503,
124
285
  )
125
- data = res.json()
126
286
 
127
- except Exception:
128
- return JSONResponse({"detail": "Auth service unreachable"}, status_code=503)
287
+ payload = jwt.decode(
288
+ token,
289
+ JWT_PUBLIC_KEY,
290
+ algorithms=JWT_ALGORITHMS,
291
+ options={"require": ["exp", "iat"]},
292
+ )
293
+
294
+ except jwt.ExpiredSignatureError:
295
+ return JSONResponse({"detail": "Token expired"}, status_code=401)
296
+ except jwt.InvalidTokenError:
297
+ return JSONResponse({"detail": "Invalid token"}, status_code=401)
298
+ except Exception as e:
299
+ return JSONResponse(
300
+ {"detail": "Auth verification failed", "error": str(e)},
301
+ status_code=401,
302
+ )
303
+
304
+ # ---------------------------------------------------
305
+ # CENTRAL INTROSPECTION CHECK (session + revocation)
306
+ # ---------------------------------------------------
307
+ try:
308
+ async with httpx.AsyncClient(timeout=5) as client:
309
+ resp = await client.post(INTROSPECT_URL,
310
+ headers={
311
+ "Authorization": f"Bearer {token}",
312
+ "X-Client-Id": os.getenv("VERGE_CLIENT_ID") or "",
313
+ "X-Client-Secret": os.getenv("VERGE_CLIENT_SECRET") or "",
314
+ },
315
+ )
316
+
317
+ data = resp.json()
318
+ if not data.get("active"):
319
+ return JSONResponse(
320
+ {"detail": "Session inactive",
321
+ "reason": data.get("reason")},
322
+ status_code=401,
323
+ )
129
324
 
130
- if not data.get("active"):
131
- return JSONResponse({"detail": "Session expired"}, status_code=401)
325
+ # optionally enrich request with user info
326
+ request.state.introspect = data
132
327
 
133
- user = data.get("user", {})
134
- request.state.user = user
328
+ except Exception as e:
329
+ return JSONResponse(
330
+ {"detail": "Auth introspection failed", "error": str(e)},
331
+ status_code=401,
332
+ )
333
+
334
+ # ---------------------------------------------------
335
+ # PERMISSION CHECK
336
+ # ---------------------------------------------------
337
+ request.state.user = payload
338
+ permissions = payload.get("roles") or []
339
+
340
+ route_obj = request.scope.get("route")
341
+ route_path = route_obj.path if route_obj else path
342
+ method = request.method
343
+
344
+ required_key = f"{SERVICE_NAME}:{route_path}:{method}".lower()
345
+ normalized_permissions = [p.lower() for p in permissions]
346
+
347
+ if required_key not in normalized_permissions:
348
+ return JSONResponse(
349
+ {"detail": "Contact admin for access"},
350
+ status_code=403,
351
+ )
135
352
 
136
- # ----------------------------------------------
137
- # ⭐ ANY authenticated user triggers auto-registration
138
- # ----------------------------------------------
139
- print("🔐 Authenticated user → auto-registering service")
140
- await register_with_auth()
353
+ return await call_next(request)
@@ -1,12 +1,8 @@
1
1
  from fastapi import APIRouter, Request, HTTPException
2
2
  from .secret_provider import get_secret
3
- import os
4
3
 
5
4
  router = APIRouter()
6
5
 
7
- SERVICE_NAME = os.getenv("SERVICE_NAME")
8
- SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")
9
-
10
6
 
11
7
  @router.get("/__verge__/routes", include_in_schema=False)
12
8
  async def verge_internal_routes(request: Request):
@@ -14,8 +10,8 @@ async def verge_internal_routes(request: Request):
14
10
  expected_secret = get_secret("VERGE_SERVICE_SECRET")
15
11
  received_secret = request.headers.get("X-Verge-Service-Secret")
16
12
 
17
- # Enforce exact secret match
18
13
  if not expected_secret or expected_secret != received_secret:
14
+
19
15
  raise HTTPException(status_code=403, detail="Forbidden")
20
16
 
21
17
  collected = []
@@ -51,48 +47,3 @@ async def verge_internal_routes(request: Request):
51
47
  collected.sort(key=lambda r: (r["path"], r["method"]))
52
48
 
53
49
  return collected
54
-
55
-
56
- @router.post("/sync/service-routes", include_in_schema=False)
57
- async def sync_service_routes(request: Request):
58
-
59
- expected_secret = get_secret("VERGE_SERVICE_SECRET")
60
- received_secret = request.headers.get("X-Verge-Service-Secret")
61
-
62
- if not expected_secret or expected_secret != received_secret:
63
- raise HTTPException(status_code=403, detail="Forbidden")
64
-
65
- collected = []
66
-
67
- INTERNAL_PREFIXES = (
68
- "/__verge__",
69
- "/openapi",
70
- "/docs",
71
- "/redoc"
72
- )
73
-
74
- # Collect routes from the app
75
- for route in request.app.routes:
76
- path = getattr(route, "path", None)
77
- methods = getattr(route, "methods", [])
78
-
79
- if not path:
80
- continue
81
-
82
- if path.startswith(INTERNAL_PREFIXES):
83
- continue
84
-
85
- for method in methods:
86
- if method in ("GET", "POST", "PUT", "PATCH", "DELETE"):
87
- collected.append({
88
- "path": path,
89
- "method": method
90
- })
91
-
92
- collected.sort(key=lambda r: (r["path"], r["method"]))
93
-
94
- return {
95
- "service": SERVICE_NAME,
96
- "base_url": SERVICE_BASE_URL,
97
- "routes": collected
98
- }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verge-auth-sdk
3
- Version: 0.1.18
3
+ Version: 0.1.38
4
4
  Summary: Secure centralized authentication SDK for FastAPI microservices
5
5
  Author-email: Verge Infosoft <contactus@vergeinfosoft.com>
6
6
  License: MIT
@@ -14,7 +14,8 @@ Requires-Dist: python-dotenv
14
14
  Dynamic: license-file
15
15
 
16
16
  🔐 Verge Auth SDK
17
- Secure Identity & Access Management for FastAPI Microservices
17
+
18
+ Secure Identity & Access Management for FastAPI Microservices and all Python-based frameworks Monolithic and Microservice Architectures
18
19
 
19
20
  Verge Auth SDK is a lightweight integration library that connects your FastAPI microservices to the Verge Auth Platform — a centralized identity, role management, and access-control system built for modern SaaS applications.
20
21
 
@@ -53,8 +54,8 @@ Users sign up with their organization details, company domain, and email.
53
54
 
54
55
  2. Email Verification
55
56
 
56
- A verification link is sent from no-reply@vergeinfosoft.com
57
- .
57
+ A verification email is sent to the registered address.
58
+
58
59
  Once verified, the user is redirected to the Verge Auth platform.
59
60
 
60
61
  3. Login
@@ -155,6 +156,7 @@ Add users into the group
155
156
  This makes onboarding smoother and keeps role management scalable.
156
157
 
157
158
  🔌 Integrating the SDK Into a Microservice
159
+
158
160
  Install from PyPI
159
161
  pip install verge_auth_sdk
160
162
 
@@ -163,6 +165,8 @@ from fastapi import FastAPI
163
165
  from verge_auth_sdk import add_central_auth
164
166
 
165
167
  app = FastAPI()
168
+
169
+ # call this at the last line of your apps main
166
170
  add_central_auth(app)
167
171
 
168
172
  That’s it.
@@ -171,30 +175,55 @@ The service will now:
171
175
  ✓ Authenticate incoming requests
172
176
  ✓ Communicate securely with Verge Auth
173
177
  ✓ Provide user identity + roles
174
- Automatically register its routes for RBAC assignment
178
+ Secure synchronization of service access metadata for centralized permission governance.
175
179
 
176
180
  ⚙ Environment Configuration
177
181
 
178
182
  Each service requires a minimal set of environment variables:
183
+ Exact endpoint configurations and integration details may vary by deployment and are abstracted by the SDK.
184
+
185
+ ############## DO NOT CHANGE THIS #################################
179
186
 
180
- ######################################################################
187
+ AUTH_BASE_URL=https://auth.vergeinfosoft.com
188
+ AUTH_SESSION_URL=https://auth.vergeinfosoft.com/session
189
+ AUTH_INTROSPECT_URL=https://auth.vergeinfosoft.com/introspect
190
+ AUTH_REGISTER_URL=https://auth.vergeinfosoft.com/service-registry/register
191
+ AUTH_ROUTE_SYNC_URL=https://auth.vergeinfosoft.com/route-sync
192
+ AUTH_PUBLIC_KEY_URL=https://auth.vergeinfosoft.com/auth/keys/public
193
+ AUTH_LOGIN_URL=https://auth.vergeinfosoft.com/login
181
194
 
182
- AUTH_INTROSPECT_URL=<auth-server-introspection-endpoint>
183
- AUTH_LOGIN_URL=<auth-server-login-ui>
195
+ ############## DO NOT CHANGE THIS #################################
196
+
197
+
198
+ ################# CHANGE THESE AS PER DETAILS PROVIDED #############################################
184
199
 
185
200
  VERGE_CLIENT_ID=<client-id>
186
201
  VERGE_CLIENT_SECRET=<client-secret>
187
-
188
202
  VERGE_SERVICE_SECRET=<service-integration-secret>
189
-
190
203
  # These are provided by Verge Infosoft during onboarding.
191
204
 
192
- # Optional secret provider:
205
+ ####################################################################################################
193
206
 
194
- SECRETS_PROVIDER=env # azure | aws
207
+
208
+ # Select Optional secret provider:
209
+
210
+ SECRETS_PROVIDER=env | AZURE | AWS | GCP | ORACLE # Supported cloud providers for secret management
211
+
212
+ env=env # if you want to load from your local ENV
213
+ azure=<AZURE_URL>
214
+ aws=<AWS_URL>
215
+ gcp=<GCP_URL>
216
+ oracle=<ORACLE_URL>
217
+
218
+ ########################################################################
219
+
220
+ SERVICE_NAME=<SERVICE_NAME> # example billing service or hr service
221
+ SERVICE_BASE_URL=<SERVICE_BASE_URL> example https://hr.yourdomain.com
195
222
 
196
223
  ########################################################################
197
224
 
225
+
226
+
198
227
  🛡 Middleware Responsibilities
199
228
 
200
229
  The SDK transparently handles:
@@ -215,7 +244,7 @@ You do not need to implement any auth or RBAC logic manually.
215
244
 
216
245
  🔐 Security Highlights
217
246
 
218
- RSA-based JWT verification
247
+ Industry-standard asymmetric token verification with key rotation support
219
248
 
220
249
  Centralized session & token lifecycle management
221
250
 
@@ -227,7 +256,7 @@ HTTPS-only communication
227
256
 
228
257
  Support for cloud key vaults
229
258
 
230
- 💼 Ideal For
259
+ 💼 Ideal For (including but not limited to):
231
260
 
232
261
  HRMS, ERP, CRM, Billing platforms
233
262
 
@@ -0,0 +1,9 @@
1
+ verge_auth_sdk/__init__.py,sha256=fna_P4l-tc3ZLKwERFEdOpT4hqPQoJSikrbt_ze5yME,410
2
+ verge_auth_sdk/middleware.py,sha256=a7B0pu2lIg3qmB89iefdnkZRr3bhj1QWx1WbVOmsNdY,12975
3
+ verge_auth_sdk/secret_provider.py,sha256=j89rj9LZHl4qTI2fdf0qnn-mgDD3oTbdP3imsm0S9n8,1653
4
+ verge_auth_sdk/verge_routes.py,sha256=GAFS8HdSNznuh6two6DAHZZeq2t29l3IlzDJjBcOGKA,1413
5
+ verge_auth_sdk-0.1.38.dist-info/licenses/LICENSE,sha256=OLuFd1VUvl1QKIJJ_qJ8zR4ypcDhzeRkEywc7PX2ABQ,1090
6
+ verge_auth_sdk-0.1.38.dist-info/METADATA,sha256=US1QT6gu-6OrM1TzYXRKyZ6RDol4hlbVOLaOg6mGlT8,7075
7
+ verge_auth_sdk-0.1.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ verge_auth_sdk-0.1.38.dist-info/top_level.txt,sha256=0ik2K2CJZrP11DvuR7USTYJkX_kv2DSJS5wyhP1yAfA,15
9
+ verge_auth_sdk-0.1.38.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- verge_auth_sdk/__init__.py,sha256=n76KSaK3CohCP7dzrUXWlTPNPGzWesLTlYl-C7rfW60,411
2
- verge_auth_sdk/middleware.py,sha256=8bwOOVELZs_yqGuG_OjQnW3P7T0zaF1_Fj_Cvyk0Y_w,5037
3
- verge_auth_sdk/secret_provider.py,sha256=j89rj9LZHl4qTI2fdf0qnn-mgDD3oTbdP3imsm0S9n8,1653
4
- verge_auth_sdk/verge_routes.py,sha256=hRPHe32tBEdiAOj7qbfaa4m-obOBfATLSrx6z70xvIY,2773
5
- verge_auth_sdk-0.1.18.dist-info/licenses/LICENSE,sha256=OLuFd1VUvl1QKIJJ_qJ8zR4ypcDhzeRkEywc7PX2ABQ,1090
6
- verge_auth_sdk-0.1.18.dist-info/METADATA,sha256=-ETqtoG-z8Rk1fThTUXhO-vv9TKb9XfePAnDGnZLbR0,5702
7
- verge_auth_sdk-0.1.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- verge_auth_sdk-0.1.18.dist-info/top_level.txt,sha256=0ik2K2CJZrP11DvuR7USTYJkX_kv2DSJS5wyhP1yAfA,15
9
- verge_auth_sdk-0.1.18.dist-info/RECORD,,