verge-auth-sdk 0.1.21__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.

@@ -0,0 +1,12 @@
1
+ # verge_auth_sdk/__init__.py
2
+ """
3
+ verge_auth_sdk package public API.
4
+ Exports:
5
+ - add_central_auth(app) -> middleware to attach to FastAPI app
6
+ - get_secret(name) -> secret retrieval helper
7
+ """
8
+ from .middleware import add_central_auth
9
+ from .secret_provider import get_secret
10
+ from .verge_routes import verge_internal_routes
11
+
12
+ __all__ = ["add_central_auth", "get_secret", "verge_internal_routes"]
@@ -0,0 +1,216 @@
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import RedirectResponse, JSONResponse
3
+ from .secret_provider import get_secret
4
+ from .verge_routes import router as verge_routes_router
5
+ import httpx
6
+ import os
7
+ import asyncio
8
+
9
+ REGISTERED_ROUTES = []
10
+
11
+
12
+ async def _post_with_retries(client, url, json=None, headers=None, timeout=10, retries=8, backoff=1):
13
+ last_exc = None
14
+ for attempt in range(1, retries + 1):
15
+ try:
16
+ resp = await client.post(
17
+ url,
18
+ json=json,
19
+ headers=headers,
20
+ timeout=timeout,
21
+ )
22
+ return resp # success
23
+ except Exception as e:
24
+ last_exc = e
25
+ print(
26
+ f"❗ Retry {attempt}/{retries} failed for {url}: {type(e).__name__}: {e}")
27
+ await asyncio.sleep(backoff * attempt) # linear backoff
28
+ raise last_exc
29
+
30
+
31
+ def add_central_auth(app: FastAPI):
32
+
33
+ AUTH_INTROSPECT_URL = os.getenv("AUTH_INTROSPECT_URL")
34
+ AUTH_LOGIN_URL = os.getenv("AUTH_LOGIN_URL")
35
+
36
+ SERVICE_NAME = os.getenv("SERVICE_NAME")
37
+ SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")
38
+
39
+ CLIENT_ID = os.getenv("VERGE_CLIENT_ID")
40
+ CLIENT_SECRET = os.getenv("VERGE_CLIENT_SECRET")
41
+
42
+ VERGE_SERVICE_SECRET = get_secret("VERGE_SERVICE_SECRET")
43
+
44
+ AUTH_REGISTER_URL = os.getenv("AUTH_REGISTER_URL")
45
+ AUTH_ROUTE_SYNC_URL = os.getenv("AUTH_ROUTE_SYNC_URL")
46
+
47
+ # -----------------------------------------------------------
48
+ # INTERNAL VERGE ROUTES
49
+ # -----------------------------------------------------------
50
+ app.include_router(verge_routes_router)
51
+
52
+ # -----------------------------------------------------------
53
+ # MICROSERVICE BOOTSTRAP ON STARTUP
54
+ # -----------------------------------------------------------
55
+ @app.on_event("startup")
56
+ async def verge_bootstrap():
57
+ print("πŸ”₯ Verge bootstrap started")
58
+
59
+ await asyncio.sleep(2)
60
+
61
+ REGISTERED_ROUTES.clear()
62
+
63
+ print("πŸ“Œ Collecting routes...")
64
+
65
+ for route in app.routes:
66
+ try:
67
+ path = getattr(route, "path", None)
68
+ methods = getattr(route, "methods", [])
69
+
70
+ if not path:
71
+ continue
72
+
73
+ if path.startswith(("/docs", "/openapi", "/__verge__")):
74
+ continue
75
+
76
+ for m in methods:
77
+ if m in ("GET", "POST", "PUT", "PATCH", "DELETE"):
78
+ REGISTERED_ROUTES.append({"path": path, "method": m})
79
+
80
+ except Exception as e:
81
+ print("❌ Error collecting route:", e)
82
+
83
+ print("βœ… Collected routes:", REGISTERED_ROUTES)
84
+
85
+ print("\nπŸ“‘ Registering service with Auth Service...")
86
+ # print("SERVICE_NAME =", SERVICE_NAME)
87
+ # print("SERVICE_BASE_URL =", SERVICE_BASE_URL)
88
+ # print("AUTH_REGISTER_URL =", AUTH_REGISTER_URL)
89
+ # print("AUTH_ROUTE_SYNC_URL =", AUTH_ROUTE_SYNC_URL)
90
+ # print("VERGE_SERVICE_SECRET =",
91
+ # VERGE_SERVICE_SECRET if VERGE_SERVICE_SECRET else "MISSING")
92
+ # print("CLIENT_ID =", CLIENT_ID if CLIENT_ID else "MISSING")
93
+ # print("CLIENT_SECRET =",
94
+ # CLIENT_SECRET if CLIENT_SECRET else "MISSING")
95
+
96
+ async with httpx.AsyncClient() as client:
97
+ if AUTH_REGISTER_URL:
98
+ try:
99
+ resp = await _post_with_retries(
100
+ client,
101
+ AUTH_REGISTER_URL,
102
+ json={"service_name": SERVICE_NAME,
103
+ "base_url": SERVICE_BASE_URL},
104
+ headers={
105
+ "X-Client-Id": CLIENT_ID or "",
106
+ "X-Client-Secret": CLIENT_SECRET or "",
107
+ "X-Verge-Service-Secret": VERGE_SERVICE_SECRET or "",
108
+ },
109
+ timeout=10,
110
+ retries=8,
111
+ backoff=1
112
+ )
113
+ text = resp.text if hasattr(resp, "text") else await resp.text()
114
+ print("πŸ“‘ Registration response:", resp.status_code, text)
115
+
116
+ except Exception as e:
117
+ print("❌ Registration ultimately failed:",
118
+ type(e).__name__, str(e))
119
+
120
+ else:
121
+ print("⚠️ AUTH_REGISTER_URL missing")
122
+
123
+ if AUTH_ROUTE_SYNC_URL:
124
+ try:
125
+ resp = await _post_with_retries(
126
+ client,
127
+ AUTH_ROUTE_SYNC_URL,
128
+ json={
129
+ "service_name": SERVICE_NAME,
130
+ "base_url": SERVICE_BASE_URL,
131
+ "routes": REGISTERED_ROUTES,
132
+ },
133
+ headers={
134
+ "X-Client-Id": CLIENT_ID or "",
135
+ "X-Client-Secret": CLIENT_SECRET or "",
136
+ "X-Verge-Service-Secret": VERGE_SERVICE_SECRET or "",
137
+ },
138
+ timeout=20,
139
+ retries=8,
140
+ backoff=1
141
+ )
142
+ text = resp.text if hasattr(resp, "text") else await resp.text()
143
+ print("πŸ“‘ Route sync response:", resp.status_code, text)
144
+
145
+ except Exception as e:
146
+ print("❌ Route sync ultimately failed:",
147
+ type(e).__name__, str(e))
148
+
149
+ else:
150
+ print("⚠️ AUTH_ROUTE_SYNC_URL missing")
151
+
152
+ # -----------------------------------------------------------
153
+ # CENTRAL AUTHZ MIDDLEWARE (unchanged)
154
+ # -----------------------------------------------------------
155
+ @app.middleware("http")
156
+ async def central_auth(request: Request, call_next):
157
+ path = request.url.path
158
+
159
+ SKIP_PATHS = {
160
+ "/health",
161
+ "/docs",
162
+ "/openapi.json",
163
+ "/favicon.ico",
164
+ "/service-registry/register",
165
+ "/route-sync",
166
+ "/__verge__",
167
+ }
168
+
169
+ if path in SKIP_PATHS or path.startswith("/__verge__"):
170
+ return await call_next(request)
171
+
172
+ token = None
173
+ auth_header = request.headers.get("authorization")
174
+
175
+ if auth_header and auth_header.lower().startswith("bearer "):
176
+ token = auth_header.split(" ", 1)[1].strip()
177
+
178
+ if not token:
179
+ token = request.cookies.get("access_token")
180
+
181
+ if not token:
182
+ if "text/html" in request.headers.get("accept", ""):
183
+ return RedirectResponse(f"{AUTH_LOGIN_URL}?redirect_url={request.url}")
184
+ return JSONResponse({"detail": "Unauthorized"}, status_code=401)
185
+
186
+ try:
187
+ async with httpx.AsyncClient(timeout=5) as client:
188
+ res = await client.post(
189
+ AUTH_INTROSPECT_URL,
190
+ headers={
191
+ "Authorization": f"Bearer {token}",
192
+ "X-Client-Id": CLIENT_ID or "",
193
+ "X-Client-Secret": CLIENT_SECRET or "",
194
+ },
195
+ )
196
+ data = res.json()
197
+ except Exception as e:
198
+ return JSONResponse({"detail": "Auth service unreachable", "error": str(e)}, status_code=503)
199
+
200
+ if not data.get("active"):
201
+ return JSONResponse({"detail": "Session expired"}, status_code=401)
202
+
203
+ request.state.user = data.get("user")
204
+ permissions = (data.get("roles") or [])
205
+
206
+ route_obj = request.scope.get("route")
207
+ route_path = route_obj.path if route_obj is not None else path
208
+ method = request.method
209
+
210
+ required_key = f"{SERVICE_NAME}:{route_path}:{method}".lower()
211
+ normalized_permissions = [p.lower() for p in permissions]
212
+
213
+ if required_key not in normalized_permissions:
214
+ return JSONResponse({"detail": "Contact admin for access"}, status_code=403)
215
+
216
+ return await call_next(request)
@@ -0,0 +1,65 @@
1
+ import os
2
+ from functools import lru_cache
3
+
4
+
5
+ # ----- Internal provider functions -----
6
+ def _from_env(name: str) -> str:
7
+ return os.getenv(name)
8
+
9
+
10
+ def _from_aws(name: str) -> str:
11
+ raise NotImplementedError("AWS secret provider not implemented yet")
12
+
13
+
14
+ def _from_azure(name: str) -> str:
15
+ raise NotImplementedError("Azure secret provider not implemented yet")
16
+
17
+
18
+ def _from_gcp(name: str) -> str:
19
+ raise NotImplementedError("GCP secret provider not implemented yet")
20
+
21
+
22
+ def _from_oracle(name: str) -> str:
23
+ raise NotImplementedError("Oracle secret provider not implemented yet")
24
+
25
+
26
+ # ----- Main entry point -----
27
+ @lru_cache(maxsize=128)
28
+ def get_secret(name: str) -> str:
29
+ """
30
+ Universal secret provider supporting:
31
+ - Local .env
32
+ - AWS Secrets Manager
33
+ - Azure Key Vault
34
+ - Google Cloud Secret Manager
35
+ - Oracle Cloud Vault
36
+ """
37
+
38
+ provider = os.getenv("SECRETS_PROVIDER", "env").lower()
39
+
40
+ try:
41
+ if provider == "env":
42
+ return _from_env(name)
43
+
44
+ if provider == "aws":
45
+ return _from_aws(name)
46
+
47
+ if provider == "azure":
48
+ return _from_azure(name)
49
+
50
+ if provider == "gcp":
51
+ return _from_gcp(name)
52
+
53
+ if provider == "oracle":
54
+ return _from_oracle(name)
55
+
56
+ except Exception as e:
57
+ # fallback to environment variables
58
+ value = os.getenv(name)
59
+ if value:
60
+ return value
61
+ raise Exception(
62
+ f"Secret '{name}' not found via provider '{provider}': {e}"
63
+ )
64
+
65
+ raise Exception(f"Unknown SECRETS_PROVIDER '{provider}'")
@@ -0,0 +1,55 @@
1
+ from fastapi import APIRouter, Request, HTTPException
2
+ from .secret_provider import get_secret
3
+
4
+ router = APIRouter()
5
+
6
+
7
+ @router.get("/__verge__/routes", include_in_schema=False)
8
+ async def verge_internal_routes(request: Request):
9
+
10
+ expected_secret = get_secret("VERGE_SERVICE_SECRET")
11
+ received_secret = request.headers.get("X-Verge-Service-Secret")
12
+
13
+ print("expected_secret********", expected_secret)
14
+ print("received_secret********", received_secret)
15
+ if not expected_secret or expected_secret != received_secret:
16
+ print("secrets did not match************")
17
+
18
+ # Enforce exact secret match
19
+ # for debugging stopping secret check
20
+ # if not expected_secret or expected_secret != received_secret:
21
+ # raise HTTPException(status_code=403, detail="Forbidden")
22
+
23
+ collected = []
24
+
25
+ # FastAPI auto-generated system paths you DON'T want to sync
26
+ INTERNAL_PREFIXES = (
27
+ "/__verge__",
28
+ "/openapi",
29
+ "/docs",
30
+ "/redoc"
31
+ )
32
+
33
+ for route in request.app.routes:
34
+ path = getattr(route, "path", None)
35
+ methods = getattr(route, "methods", [])
36
+
37
+ if not path:
38
+ continue
39
+
40
+ # Skip internal/system routes
41
+ if path.startswith(INTERNAL_PREFIXES):
42
+ continue
43
+
44
+ # Filter HTTP methods
45
+ for method in methods:
46
+ if method in ("GET", "POST", "PUT", "PATCH", "DELETE"):
47
+ collected.append({
48
+ "path": path,
49
+ "method": method
50
+ })
51
+
52
+ # Sort for consistency (helps prevent duplicate entries in DB)
53
+ collected.sort(key=lambda r: (r["path"], r["method"]))
54
+
55
+ return collected
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: verge-auth-sdk
3
+ Version: 0.1.21
4
+ Summary: Secure centralized authentication SDK for FastAPI microservices
5
+ Author-email: Verge Infosoft <contactus@vergeinfosoft.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://www.vergeinfosoft.com
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: fastapi
12
+ Requires-Dist: httpx
13
+ Requires-Dist: python-dotenv
14
+ Dynamic: license-file
15
+
16
+ πŸ” Verge Auth SDK
17
+ Secure Identity & Access Management for FastAPI Microservices
18
+
19
+ 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
+ With a single line of code, your service is fully protected and becomes part of a unified authentication ecosystem:
22
+
23
+ from verge_auth_sdk import add_central_auth
24
+ add_central_auth(app)
25
+
26
+ πŸš€ What Verge Auth Provides
27
+
28
+ βœ“ Centralized Login
29
+
30
+ Your users authenticate through the Verge Auth hosted login experience.
31
+
32
+ βœ“ Role-Based Access Control (RBAC)
33
+
34
+ Create roles inside the Verge Auth Dashboard and assign access to microservices and their granular operations.
35
+
36
+ βœ“ Route-Level Permissions
37
+
38
+ When a service integrates the SDK, its available routes automatically appear in the Verge Auth dashboard for permissions assignment.
39
+
40
+ βœ“ Group & User Management
41
+
42
+ Assign roles to users or user groups for highly flexible access control.
43
+
44
+ βœ“ Secure Communication
45
+
46
+ All microservice-to-auth communication is secured using service credentials provided during onboarding.
47
+
48
+ 🧭 End-to-End User Flow
49
+
50
+ 1. Account Creation
51
+
52
+ Users sign up with their organization details, company domain, and email.
53
+
54
+ 2. Email Verification
55
+
56
+ A verification link is sent from no-reply@vergeinfosoft.com
57
+ .
58
+ Once verified, the user is redirected to the Verge Auth platform.
59
+
60
+ 3. Login
61
+
62
+ Users can sign in through the β€œVerge IAM” login page using their verified email and password.
63
+
64
+ 4. Auth Dashboard
65
+
66
+ Once logged in, the dashboard displays:
67
+
68
+ Total users
69
+
70
+ Active groups
71
+
72
+ Available roles
73
+
74
+ Audit logs
75
+
76
+ Permissions overview
77
+
78
+ πŸŽ› Role-Based Access Control (RBAC)
79
+
80
+ RBAC inside Verge Auth is designed to be extremely intuitive β€” while supporting enterprise-level control.
81
+
82
+ Creating a Role
83
+
84
+ Inside the Roles section:
85
+
86
+ Click New Role
87
+
88
+ Enter the role name (e.g., HR Manager, Operations Admin)
89
+
90
+ Optional: Add a description
91
+
92
+ Select the Service you want this role to access
93
+
94
+ Example: employees-service, billing-service, appointments-service
95
+
96
+ After selecting a service, the system automatically shows all available routes for that service
97
+
98
+ Example:
99
+
100
+ /employees/
101
+
102
+ /employees/{id}
103
+
104
+ /employees/create
105
+
106
+ /employees/update
107
+
108
+ /employees/delete
109
+
110
+ Each route is presented with clear CRUD permissions:
111
+
112
+ Create
113
+
114
+ Read
115
+
116
+ Update
117
+
118
+ Delete
119
+
120
+ You can either:
121
+
122
+ Grant Full Access to that service
123
+
124
+ OR choose granular permissions route-by-route
125
+
126
+ Save the role
127
+
128
+ It instantly becomes available for assignment
129
+
130
+ Role creation modal with a dropdown for service selection and an auto-generated route list for CRUD assignment.
131
+
132
+ πŸ§‘β€πŸ€β€πŸ§‘ Assigning Roles to Users or Groups
133
+
134
+ After creating a role, you can:
135
+
136
+ Assign to a User
137
+
138
+ Go to Manage Users
139
+
140
+ Edit a user
141
+
142
+ Select one or more roles
143
+
144
+ Save changes
145
+
146
+ Assign to a User Group
147
+
148
+ Create a group (e.g., HR Team, Finance Department)
149
+
150
+ Assign roles to the group
151
+
152
+ Add users into the group
153
+ (they automatically inherit the group’s permissions)
154
+
155
+ This makes onboarding smoother and keeps role management scalable.
156
+
157
+ πŸ”Œ Integrating the SDK Into a Microservice
158
+ Install from PyPI
159
+ pip install verge_auth_sdk
160
+
161
+ Add the Middleware
162
+ from fastapi import FastAPI
163
+ from verge_auth_sdk import add_central_auth
164
+
165
+ app = FastAPI()
166
+ add_central_auth(app)
167
+
168
+ That’s it.
169
+ The service will now:
170
+
171
+ βœ“ Authenticate incoming requests
172
+ βœ“ Communicate securely with Verge Auth
173
+ βœ“ Provide user identity + roles
174
+ βœ“ Automatically register its routes for RBAC assignment
175
+
176
+ βš™ Environment Configuration
177
+
178
+ Each service requires a minimal set of environment variables:
179
+
180
+ ######################################################################
181
+
182
+ AUTH_INTROSPECT_URL=<auth-server-introspection-endpoint>
183
+ AUTH_LOGIN_URL=<auth-server-login-ui>
184
+
185
+ VERGE_CLIENT_ID=<client-id>
186
+ VERGE_CLIENT_SECRET=<client-secret>
187
+
188
+ VERGE_SERVICE_SECRET=<service-integration-secret>
189
+
190
+ # These are provided by Verge Infosoft during onboarding.
191
+
192
+ # Optional secret provider:
193
+
194
+ SECRETS_PROVIDER=env # azure | aws
195
+
196
+ ########################################################################
197
+
198
+ πŸ›‘ Middleware Responsibilities
199
+
200
+ The SDK transparently handles:
201
+
202
+ User authentication
203
+
204
+ Role injection
205
+
206
+ Cookie vs header auth
207
+
208
+ Unauthorized access responses
209
+
210
+ Service-level authentication
211
+
212
+ Route registration
213
+
214
+ You do not need to implement any auth or RBAC logic manually.
215
+
216
+ πŸ” Security Highlights
217
+
218
+ RSA-based JWT verification
219
+
220
+ Centralized session & token lifecycle management
221
+
222
+ Strong encryption for service credentials
223
+
224
+ Multi-layer permission checks (Role β†’ Service β†’ Route β†’ Operation)
225
+
226
+ HTTPS-only communication
227
+
228
+ Support for cloud key vaults
229
+
230
+ πŸ’Ό Ideal For
231
+
232
+ HRMS, ERP, CRM, Billing platforms
233
+
234
+ Multi-tenant SaaS applications
235
+
236
+ Modern microservice architectures
237
+
238
+ Secure admin dashboards
239
+
240
+ Enterprise platforms needing consistent access control
241
+
242
+ πŸ†˜ Support & Onboarding
243
+
244
+ For enterprise onboarding, custom integrations, or troubleshooting:
245
+
246
+ 🌐 Website
247
+ https://www.vergeinfosoft.com
248
+
249
+ πŸ“§ Email
250
+ contactus@vergeinfosoft.com
@@ -0,0 +1,9 @@
1
+ verge_auth_sdk/__init__.py,sha256=n76KSaK3CohCP7dzrUXWlTPNPGzWesLTlYl-C7rfW60,411
2
+ verge_auth_sdk/middleware.py,sha256=tsh6vUoRqNcBbYTf9m7sBojQZXzJEvlvqvXxFx6-nkM,8268
3
+ verge_auth_sdk/secret_provider.py,sha256=j89rj9LZHl4qTI2fdf0qnn-mgDD3oTbdP3imsm0S9n8,1653
4
+ verge_auth_sdk/verge_routes.py,sha256=IPQHNGK-IiFEb8DmcqiI-PUVagaVi8ZUCTbkwvwi-EU,1728
5
+ verge_auth_sdk-0.1.21.dist-info/licenses/LICENSE,sha256=OLuFd1VUvl1QKIJJ_qJ8zR4ypcDhzeRkEywc7PX2ABQ,1090
6
+ verge_auth_sdk-0.1.21.dist-info/METADATA,sha256=85mYzAHXOOM-gLx_d9Y9wavzHb4Et2jVp8avjPU6huM,5702
7
+ verge_auth_sdk-0.1.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ verge_auth_sdk-0.1.21.dist-info/top_level.txt,sha256=0ik2K2CJZrP11DvuR7USTYJkX_kv2DSJS5wyhP1yAfA,15
9
+ verge_auth_sdk-0.1.21.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Verge Infosoft
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 @@
1
+ verge_auth_sdk