Integrate license_server.py backend with omnibioai-auth IAM - #19
Merged
Conversation
Replaces license_server.py's static admin_key comparison (a plaintext
shared secret, default "admin-secret") with real IAM authorization on
/api/license/generate and /api/license/list: both now require a Bearer
JWT and the manage_licenses permission, verified against omnibioai-auth.
Architecture decisions (confirmed before implementation):
1. Does not use the omnibioai-iam-client shared package, and does not
implement local JWT signature verification. omnibioai-control-center's
own core/jwt_verify.py already documents why the shared package is
unsuitable (not imported by any live service today, no jti-blacklist
revocation support) and built local decode instead -- but that choice
was driven by needing sub-millisecond verification at gateway scale.
License generation/listing is a low-frequency admin action with no
such requirement, so the simpler, "do not introduce duplicate JWT
verification" -- compliant choice is a plain remote call to
omnibioai-auth's own POST /auth/validate, exactly mirroring what
src/ui/lib/session.js's getCurrentUser() already does from the
frontend. No JWT secret or JWKS config is duplicated into this
service.
2. Service identity (PR9's client_credentials/GET /service/me) is used
for a startup self-check only -- license_server.py can hold its own
OAuth client identity in omnibioai-auth, confirmed via GET /service/me
at boot -- never for authorizing the manage_licenses-gated actions
themselves, which need a real human actor for accountability (a
faceless service identity can't provide that). Confirmed as the first
real consumer of this mechanism -- omnibioai-auth's own
test_service_to_service_oauth.py documents that nothing in the
ecosystem calls /oauth/token or depends on a live client_credentials-
gated route today. Best-effort and non-blocking: an unconfigured or
unreachable service identity never prevents startup.
3. IAM-protected actions are audit-logged locally (Python logging, one
structured line per allow/deny outcome) rather than written into
omnibioai-auth's persistent audit ledger (PR9) -- that ledger is a
same-process Python function with no REST ingestion API for external
callers today.
4. Existing OAuth login flow (src/ui/lib/session.js, /auth/login,
/auth/{provider}/login, /auth/refresh, /auth/validate) is untouched --
this PR only changes license_server.py's two admin-gated endpoints.
/api/license/validate and /api/license/pull-token (license-key-gated,
not IAM-gated) are also untouched.
docker-compose.yml: license-server gains IAM_URL (matching the exact env
var name convention every other omnibioai-* service already uses) and
optional STUDIO_SERVICE_CLIENT_ID/_SECRET; ADMIN_KEY removed (dead).
Testing: this repo's entire tests/ directory is live-integration-only (no
unit-test layer, no pytest config, requires the full docker-compose stack
to run at all) -- confirmed via a baseline run that hangs without one.
Per explicit confirmation, added tests/unit/test_license_server.py (21
tests, mocked HTTP calls, no live services required) instead of standing
up the full stack. The pre-existing live-integration suite could not be
executed in this environment -- a pre-existing constraint, not something
this change causes.
No billing, usage metering, or UI redesign.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces
license_server.py's staticadmin_keycomparison (a plaintext shared secret, default"admin-secret") with real IAM authorization on/api/license/generateand/api/license/list: both now require a Bearer JWT and themanage_licensespermission, verified againstomnibioai-auth.Inspection findings (before coding)
backend/is a small, standalone license server — not a full API backend. It was the only place a static admin mechanism existed in the repo.src/ui/lib/session.js) already talks directly toomnibioai-auth's/auth/login,/auth/{provider}/login,/auth/refresh,/auth/validate— untouched by this PR.omnibioai-control-center's owncore/jwt_verify.pyexplicitly documents evaluating and rejecting the sharedomnibioai-iam-clientpackage ("not imported by any live service today, no jti-blacklist revocation support"), building local JWT decode instead for gateway-scale performance needs. License admin actions have no such performance requirement.omnibioai-auth's owntest_service_to_service_oauth.pydocuments that nothing in the ecosystem calls/oauth/tokenor depends on a liveclient_credentials-gated route today — this PR is the first real consumer of that mechanism.Architecture decisions (confirmed before implementation)
iam_clientpackage, no local JWT verification. Uses a plain remote call toomnibioai-auth'sPOST /auth/validate— the same endpointsession.js'sgetCurrentUser()already trusts from the frontend. No JWT secret or JWKS config duplicated into this service.license_server.pycan hold its own OAuth client identity, confirmed viaGET /service/meat boot — never for authorizing themanage_licenses-gated actions themselves, which need a real human actor for accountability. Best-effort and non-blocking: an unconfigured or unreachable service identity never prevents startup.logging, one structured line per allow/deny outcome) rather than written intoomnibioai-auth's persistent audit ledger (PR9) — that ledger has no REST ingestion API for external callers today./api/license/validateand/api/license/pull-token(license-key-gated, not IAM-gated) are also untouched.docker-compose.yml:license-servergainsIAM_URL(matching the exact env var name convention every otheromnibioai-*service already uses) and optionalSTUDIO_SERVICE_CLIENT_ID/_SECRET;ADMIN_KEYremoved (dead).Testing
This repo's entire
tests/directory is live-integration-only (no unit-test layer, no pytest config, requires the full docker-compose stack — auth-service, LIMS, TES, RAG, nginx — to run at all); confirmed via a baseline run that hangs without one in this environment. Per explicit confirmation, addedtests/unit/test_license_server.py(21 tests, mocked HTTP calls, no live services required) instead. The pre-existing live-integration suite could not be executed here — a pre-existing environmental constraint, not something this change causes.tests/unit/test_license_server.py: 21/21 passing — token validation (valid/invalid/network-error), permission checks (missing token/non-Bearer/invalid token/missing permission/granted), both endpoints' 401/403/200 paths, confirmation the staticadmin_keyfield/query-param no longer exists or works,/validateand/pull-tokenunaffected, service-identity self-check (configured/unconfigured/failure/network-error), and app startup never crashing when the service identity is unconfigured.Confirmation
No billing, usage metering, or UI redesign.
🤖 Generated with Claude Code