Skip to content

Integrate license_server.py backend with omnibioai-auth IAM - #19

Merged
man4ish merged 1 commit into
mainfrom
feature/pr11-studio-iam-integration
Aug 4, 2026
Merged

Integrate license_server.py backend with omnibioai-auth IAM#19
man4ish merged 1 commit into
mainfrom
feature/pr11-studio-iam-integration

Conversation

@man4ish

@man4ish man4ish commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

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.

Inspection findings (before coding)

  • Studio's 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.
  • The existing OAuth login flow (src/ui/lib/session.js) already talks directly to omnibioai-auth's /auth/login, /auth/{provider}/login, /auth/refresh, /auth/validate — untouched by this PR.
  • omnibioai-control-center's own core/jwt_verify.py explicitly documents evaluating and rejecting the shared omnibioai-iam-client package ("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 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 — this PR is the first real consumer of that mechanism.

Architecture decisions (confirmed before implementation)

  1. No shared iam_client package, no local JWT verification. Uses a plain remote call to omnibioai-auth's POST /auth/validate — the same endpoint session.js's getCurrentUser() already trusts from the frontend. No JWT secret or JWKS config duplicated into this service.
  2. Service identity (PR9) used for a startup self-check only. license_server.py can hold its own OAuth client identity, confirmed via GET /service/me at boot — never for authorizing the manage_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.
  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 has no REST ingestion API for external callers today.
  4. Existing OAuth login flow untouched. /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 — 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, added tests/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 static admin_key field/query-param no longer exists or works, /validate and /pull-token unaffected, 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

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>
@man4ish
man4ish merged commit 269d98c into main Aug 4, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant