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.
- verge_auth_sdk/middleware.py +234 -89
- verge_auth_sdk/verge_routes.py +1 -50
- {verge_auth_sdk-0.1.18.dist-info → verge_auth_sdk-0.1.27.dist-info}/METADATA +42 -14
- verge_auth_sdk-0.1.27.dist-info/RECORD +9 -0
- verge_auth_sdk-0.1.18.dist-info/RECORD +0 -9
- {verge_auth_sdk-0.1.18.dist-info → verge_auth_sdk-0.1.27.dist-info}/WHEEL +0 -0
- {verge_auth_sdk-0.1.18.dist-info → verge_auth_sdk-0.1.27.dist-info}/licenses/LICENSE +0 -0
- {verge_auth_sdk-0.1.18.dist-info → verge_auth_sdk-0.1.27.dist-info}/top_level.txt +0 -0
verge_auth_sdk/middleware.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
28
|
-
#
|
|
106
|
+
# -------------------------------------------------------
|
|
107
|
+
# INTERNAL VERGE ROUTES
|
|
108
|
+
# -------------------------------------------------------
|
|
29
109
|
app.include_router(verge_routes_router)
|
|
30
110
|
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
111
|
+
# -------------------------------------------------------
|
|
112
|
+
# MICROSERVICE BOOTSTRAP ON STARTUP
|
|
113
|
+
# -------------------------------------------------------
|
|
34
114
|
@app.on_event("startup")
|
|
35
|
-
async def
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
79
|
-
|
|
131
|
+
if not path:
|
|
132
|
+
continue
|
|
80
133
|
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
#
|
|
236
|
+
# ---------------------------------------------------
|
|
237
|
+
# LOCAL JWT VERIFICATION
|
|
238
|
+
# ---------------------------------------------------
|
|
115
239
|
try:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
131
|
-
|
|
276
|
+
required_key = f"{SERVICE_NAME}:{route_path}:{method}".lower()
|
|
277
|
+
normalized_permissions = [p.lower() for p in permissions]
|
|
132
278
|
|
|
133
|
-
|
|
134
|
-
|
|
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)
|
verge_auth_sdk/verge_routes.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
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
|
-
✓
|
|
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
|
-
|
|
183
|
-
|
|
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
|
-
|
|
204
|
+
####################################################################################################
|
|
193
205
|
|
|
194
|
-
|
|
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
|
-
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|