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

@@ -1,140 +1,285 @@
1
1
  from fastapi import FastAPI, Request
2
2
  from fastapi.responses import RedirectResponse, JSONResponse
3
- import httpx
4
- import os
5
-
6
3
  from .secret_provider import get_secret
7
4
  from .verge_routes import router as verge_routes_router
5
+ import httpx
6
+ import os
7
+ import asyncio
8
+ import jwt
8
9
 
9
10
  REGISTERED_ROUTES = []
10
11
 
12
+ # ============================================================
13
+ # GLOBAL JWT CACHE
14
+ # ============================================================
15
+ JWT_PUBLIC_KEY: str | None = None
16
+ JWT_KEY_ID: str | None = None
17
+ JWT_ALGORITHMS = ["RS256"]
18
+
19
+
20
+ # -----------------------------------------------------------
21
+ # HTTP helper with retries
22
+ # -----------------------------------------------------------
23
+ async def _post_with_retries(
24
+ client,
25
+ url,
26
+ json=None,
27
+ headers=None,
28
+ timeout=10,
29
+ retries=8,
30
+ backoff=1,
31
+ ):
32
+ last_exc = None
33
+ for attempt in range(1, retries + 1):
34
+ try:
35
+ resp = await client.post(
36
+ url,
37
+ json=json,
38
+ headers=headers,
39
+ timeout=timeout,
40
+ )
41
+ return resp
42
+ except Exception as e:
43
+ last_exc = e
44
+ print(
45
+ f"❗ Retry {attempt}/{retries} failed for {url}: "
46
+ f"{type(e).__name__}: {e}"
47
+ )
48
+ await asyncio.sleep(backoff * attempt)
49
+ raise last_exc
50
+
51
+
52
+ # -----------------------------------------------------------
53
+ # PUBLIC KEY DISCOVERY
54
+ # -----------------------------------------------------------
55
+ async def load_public_key(force: bool = False):
56
+ """
57
+ Loads and caches the JWT public key from auth-service.
58
+ Safe to call multiple times.
59
+ """
60
+ global JWT_PUBLIC_KEY, JWT_KEY_ID
11
61
 
62
+ if JWT_PUBLIC_KEY and not force:
63
+ return
64
+
65
+ AUTH_PUBLIC_KEY_URL = os.getenv("AUTH_PUBLIC_KEY_URL")
66
+ if not AUTH_PUBLIC_KEY_URL:
67
+ print("❌ AUTH_PUBLIC_KEY_URL not set")
68
+ return
69
+
70
+ try:
71
+ async with httpx.AsyncClient(timeout=5) as client:
72
+ resp = await client.get(AUTH_PUBLIC_KEY_URL)
73
+ resp.raise_for_status()
74
+ data = resp.json()
75
+
76
+ JWT_PUBLIC_KEY = data.get("public_key")
77
+ JWT_KEY_ID = data.get("kid")
78
+
79
+ if JWT_PUBLIC_KEY:
80
+ print("✅ JWT public key loaded from auth service")
81
+ else:
82
+ print("❌ JWT public key missing in response")
83
+
84
+ except Exception as e:
85
+ print("❌ Failed to load JWT public key:", str(e))
86
+
87
+
88
+ # -----------------------------------------------------------
89
+ # MAIN INTEGRATION
90
+ # -----------------------------------------------------------
12
91
  def add_central_auth(app: FastAPI):
13
92
 
14
- AUTH_INTROSPECT_URL = os.getenv("AUTH_INTROSPECT_URL")
15
93
  AUTH_LOGIN_URL = os.getenv("AUTH_LOGIN_URL")
16
94
 
95
+ SERVICE_NAME = os.getenv("SERVICE_NAME")
96
+ SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")
97
+
17
98
  CLIENT_ID = os.getenv("VERGE_CLIENT_ID")
18
99
  CLIENT_SECRET = os.getenv("VERGE_CLIENT_SECRET")
19
100
 
20
- SERVICE_NAME = os.getenv("SERVICE_NAME")
21
- SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")
22
- AUTH_REGISTER_URL = os.getenv("AUTH_REGISTER_URL")
101
+ VERGE_SERVICE_SECRET = get_secret("VERGE_SERVICE_SECRET")
23
102
 
24
- VERGE_SECRET = get_secret("VERGE_SERVICE_SECRET")
103
+ AUTH_REGISTER_URL = os.getenv("AUTH_REGISTER_URL")
104
+ AUTH_ROUTE_SYNC_URL = os.getenv("AUTH_ROUTE_SYNC_URL")
25
105
 
26
- # ---------------------------------------
27
- # INTERNAL ROUTES (MUST LOAD FIRST)
28
- # ---------------------------------------
106
+ # -------------------------------------------------------
107
+ # INTERNAL VERGE ROUTES
108
+ # -------------------------------------------------------
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():
115
+ async def verge_bootstrap():
36
116
  print("🔥 Verge bootstrap 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
+ )
142
+
143
+ except Exception as e:
144
+ print("❌ Error collecting route:", e)
145
+
146
+ print("\n📡 Registering service with Auth Service...")
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)
83
171
 
84
- # ---------------------------------------
85
- # MIDDLEWARE AUTH
86
- # ---------------------------------------
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"}:
204
+ SKIP_PATHS = {
205
+ "/health",
206
+ "/docs",
207
+ "/openapi.json",
208
+ "/favicon.ico",
209
+ "/service-registry/register",
210
+ "/route-sync",
211
+ "/__verge__",
212
+ }
213
+
214
+ if path in SKIP_PATHS or path.startswith("/__verge__"):
93
215
  return await call_next(request)
94
216
 
95
- # Extract token
96
217
  token = None
97
218
  auth_header = request.headers.get("authorization")
98
219
 
99
220
  if auth_header and auth_header.lower().startswith("bearer "):
100
- token = auth_header.split(" ")[1]
221
+ token = auth_header.split(" ", 1)[1].strip()
101
222
 
102
223
  if not token:
103
224
  token = request.cookies.get("access_token")
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
+ if not token and "session" in request.scope:
227
+ token = request.scope["session"].get("access_token")
110
228
 
111
229
  if not token:
230
+ if "text/html" in request.headers.get("accept", ""):
231
+ return RedirectResponse(
232
+ f"{AUTH_LOGIN_URL}?redirect_url={request.url}"
233
+ )
112
234
  return JSONResponse({"detail": "Unauthorized"}, status_code=401)
113
235
 
114
- # Validate token
236
+ # ---------------------------------------------------
237
+ # LOCAL JWT VERIFICATION
238
+ # ---------------------------------------------------
115
239
  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
- },
240
+ if not JWT_PUBLIC_KEY:
241
+ await load_public_key(force=True)
242
+
243
+ if not JWT_PUBLIC_KEY:
244
+ return JSONResponse(
245
+ {"detail": "Auth key not ready"},
246
+ status_code=503,
124
247
  )
125
- data = res.json()
126
248
 
127
- except Exception:
128
- return JSONResponse({"detail": "Auth service unreachable"}, status_code=503)
249
+ payload = jwt.decode(
250
+ token,
251
+ JWT_PUBLIC_KEY,
252
+ algorithms=JWT_ALGORITHMS,
253
+ options={"require": ["exp", "iat"]},
254
+ )
255
+
256
+ except jwt.ExpiredSignatureError:
257
+ return JSONResponse({"detail": "Token expired"}, status_code=401)
258
+ except jwt.InvalidTokenError:
259
+ return JSONResponse({"detail": "Invalid token"}, status_code=401)
260
+ except Exception as e:
261
+ return JSONResponse(
262
+ {"detail": "Auth verification failed", "error": str(e)},
263
+ status_code=401,
264
+ )
265
+
266
+ # ---------------------------------------------------
267
+ # PERMISSION CHECK
268
+ # ---------------------------------------------------
269
+ request.state.user = payload
270
+ permissions = payload.get("roles") or []
271
+
272
+ route_obj = request.scope.get("route")
273
+ route_path = route_obj.path if route_obj else path
274
+ method = request.method
129
275
 
130
- if not data.get("active"):
131
- return JSONResponse({"detail": "Session expired"}, status_code=401)
276
+ required_key = f"{SERVICE_NAME}:{route_path}:{method}".lower()
277
+ normalized_permissions = [p.lower() for p in permissions]
132
278
 
133
- user = data.get("user", {})
134
- request.state.user = user
279
+ if required_key not in normalized_permissions:
280
+ return JSONResponse(
281
+ {"detail": "Contact admin for access"},
282
+ status_code=403,
283
+ )
135
284
 
136
- # ----------------------------------------------
137
- # ⭐ ANY authenticated user triggers auto-registration
138
- # ----------------------------------------------
139
- print("🔐 Authenticated user → auto-registering service")
140
- await register_with_auth()
285
+ 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.27
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,54 @@ 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 THESE VALUES #################################
179
186
 
180
- ######################################################################
187
+ AUTH_SESSION_URL=https://auth.vergeinfosoft.com/session
188
+ AUTH_INTROSPECT_URL=https://auth.vergeinfosoft.com/introspect
189
+ AUTH_REGISTER_URL=https://auth.vergeinfosoft.com/service-registry/register
190
+ AUTH_ROUTE_SYNC_URL=https://auth.vergeinfosoft.com/route-sync
191
+ AUTH_PUBLIC_KEY_URL=https:/auth.vergeinfosoft.com/auth/keys/public
192
+ AUTH_LOGIN_URL=https://auth-ui.vergeinfosoft.com/login/
181
193
 
182
- AUTH_INTROSPECT_URL=<auth-server-introspection-endpoint>
183
- AUTH_LOGIN_URL=<auth-server-login-ui>
194
+ ############## DO NOT CHANGE THESE VALUES #################################
195
+
196
+
197
+ ################# CHANGE THESE AS PER DETAILS PROVIDED #############################################
184
198
 
185
199
  VERGE_CLIENT_ID=<client-id>
186
200
  VERGE_CLIENT_SECRET=<client-secret>
187
-
188
201
  VERGE_SERVICE_SECRET=<service-integration-secret>
189
-
190
202
  # These are provided by Verge Infosoft during onboarding.
191
203
 
192
- # Optional secret provider:
204
+ ####################################################################################################
193
205
 
194
- SECRETS_PROVIDER=env # azure | aws
206
+
207
+ # Select Optional secret provider:
208
+
209
+ SECRETS_PROVIDER=env | AZURE | AWS | GCP | ORACLE # Supported cloud providers for secret management
210
+
211
+ env=env # if you want to load from your local ENV
212
+ azure=<AZURE_URL>
213
+ aws=<AWS_URL>
214
+ gcp=<GCP_URL>
215
+ oracle=<ORACLE_URL>
216
+
217
+ ########################################################################
218
+
219
+ SERVICE_NAME=<SERVICE_NAME> # example billing service or hr service
220
+ SERVICE_BASE_URL=<SERVICE_BASE_URL> example https://hr.yourdomain.com
195
221
 
196
222
  ########################################################################
197
223
 
224
+
225
+
198
226
  🛡 Middleware Responsibilities
199
227
 
200
228
  The SDK transparently handles:
@@ -215,7 +243,7 @@ You do not need to implement any auth or RBAC logic manually.
215
243
 
216
244
  🔐 Security Highlights
217
245
 
218
- RSA-based JWT verification
246
+ Industry-standard asymmetric token verification with key rotation support
219
247
 
220
248
  Centralized session & token lifecycle management
221
249
 
@@ -227,7 +255,7 @@ HTTPS-only communication
227
255
 
228
256
  Support for cloud key vaults
229
257
 
230
- 💼 Ideal For
258
+ 💼 Ideal For (including but not limited to):
231
259
 
232
260
  HRMS, ERP, CRM, Billing platforms
233
261
 
@@ -0,0 +1,9 @@
1
+ verge_auth_sdk/__init__.py,sha256=n76KSaK3CohCP7dzrUXWlTPNPGzWesLTlYl-C7rfW60,411
2
+ verge_auth_sdk/middleware.py,sha256=K9xebqoR_wa8KZMY1WgwtgQ4nFSZxp3eH4RTGCIK7cM,9837
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.27.dist-info/licenses/LICENSE,sha256=OLuFd1VUvl1QKIJJ_qJ8zR4ypcDhzeRkEywc7PX2ABQ,1090
6
+ verge_auth_sdk-0.1.27.dist-info/METADATA,sha256=dj105An051Rc6f-lekRXc9tCaceB464T0cp5Rm3vcmo,7048
7
+ verge_auth_sdk-0.1.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ verge_auth_sdk-0.1.27.dist-info/top_level.txt,sha256=0ik2K2CJZrP11DvuR7USTYJkX_kv2DSJS5wyhP1yAfA,15
9
+ verge_auth_sdk-0.1.27.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,,