Skip to content

feat: expose AI-Q as an API with Auth Middleware - #173

Merged
cdgamarose-nv merged 19 commits into
NVIDIA-AI-Blueprints:developfrom
cdgamarose-nv:cdgamarose/api_access
Apr 8, 2026
Merged

cdgamarose-nv merged 19 commits into
NVIDIA-AI-Blueprints:developfrom
cdgamarose-nv:cdgamarose/api_access

Conversation

@cdgamarose-nv

Copy link
Copy Markdown
Collaborator

No description provided.

@cdgamarose-nv cdgamarose-nv changed the title Cdgamarose/api access feat: expose AI-Q as an API with Auth Middleware Apr 6, 2026
@cdgamarose-nv
cdgamarose-nv marked this pull request as ready for review April 8, 2026 01:41
@cdgamarose-nv
cdgamarose-nv requested a review from AjayThorve April 8, 2026 01:41
@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds raw ASGI AuthMiddleware with path filtering and JWT token validation to the aiq_api frontend, introduces a JWTValidator backed by OIDC discovery/JWKS, wires the skip_clarifier flag from the middleware ContextVar into ChatResearcherState, and propagates AuthError from agent nodes so auth failures surface as user-readable messages rather than generic errors. The previously-flagged issues — JWTValidator not inheriting TokenValidator, missing can_handle(), raw claims return without required contract fields, and unprotected JWKS cache — have all been resolved in this revision.

Confidence Score: 5/5

Safe to merge — all previously flagged P0/P1 issues have been resolved; only P2 style/performance suggestions remain.

The three issues from the prior review cycle (missing TokenValidator inheritance, raw claims return, JWKS thread safety) are all addressed. The remaining findings are P2: holding a lock during network I/O in JWTValidator (latency concern, not a correctness bug), overly broad exception handling in CLI auth init, and a redundant ImportError in an except clause. None of these block correct operation.

frontends/aiq_api/src/aiq_api/auth/jwt_validator.py — lock-during-I/O pattern worth addressing before high-load deployment; frontends/cli/cli.py — silent auth failure swallowing.

Vulnerabilities

  • _detect_internal_caller in middleware.py trusts any Authorization: Bearer eyJ… token as a jwt-type user without verification. This is intentional (internal traffic bypasses auth), but means any internal service sending eyJ-prefixed bearer tokens receives elevated JWT identity without credential checks.
  • _initialize_auth in cli.py catches all exceptions including real auth failures, silently continuing without auth when interactive_auth: true is configured.
  • No other injection, secret-exposure, or authz boundary issues identified in the changed code.

Important Files Changed

Filename Overview
frontends/aiq_api/src/aiq_api/auth/middleware.py New raw ASGI auth middleware; path-filter, token-validation, and ContextVar propagation all look correct.
frontends/aiq_api/src/aiq_api/auth/jwt_validator.py JWTValidator now correctly extends TokenValidator and validate() returns the full contract dict. However, _get_signing_key holds _jwks_lock for the entire duration of network I/O, serializing concurrent JWT validations under load.
frontends/aiq_api/src/aiq_api/auth/base.py Clean abstract base with well-documented user-dict contract; no issues.
frontends/aiq_api/src/aiq_api/auth/errors.py Simple AuthError exception type; no issues.
src/aiq_agent/agents/chat_researcher/register.py skip_clarifier logic correctly reads from middleware ContextVar; minor redundancy in except clause.
src/aiq_agent/agents/chat_researcher/agent.py AuthError handling added in both shallow and deep research nodes; skip_clarifier flag correctly threaded through the turn-reset dict.
src/aiq_agent/agents/chat_researcher/models/state.py skip_clarifier field added with correct default (False); no issues.
src/aiq_agent/auth/utils.py Authorization header fallback added correctly; Bearer eyJ heuristic is acceptable for JWT detection.
frontends/aiq_api/tests/test_auth.py Comprehensive test suite covering middleware path filtering, auth flow, JWT validator, and ContextVar reset; no issues.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Middleware as AuthMiddleware
    participant Validator as JWTValidator
    participant JWKS as JWKS Endpoint
    participant App as ASGI App

    Client->>Middleware: HTTP request
    Middleware->>Middleware: check _is_external()

    alt Internal host
        Middleware->>App: pass through (internal user)
    else External host
        Middleware->>Middleware: check _path_allowed()
        alt Path blocked
            Middleware-->>Client: 404
        else Exempt path
            Middleware->>App: anonymous user, no token needed
        else Auth disabled
            Middleware->>App: anonymous user
        else Auth enabled
            Middleware->>Middleware: _extract_token()
            alt No token found
                Middleware-->>Client: 401
            else Token present
                Middleware->>Validator: can_handle(token)
                Validator-->>Middleware: true
                Middleware->>Validator: validate(token)
                Validator->>JWKS: fetch signing key (under lock)
                JWKS-->>Validator: public key
                Validator-->>Middleware: user dict or None
                alt Invalid
                    Middleware-->>Client: 401
                else Valid
                    Middleware->>App: user stored in scope + ContextVar
                end
            end
        end
    end
Loading

Reviews (3): Last reviewed commit: "revert links" | Re-trigger Greptile

Comment thread frontends/aiq_api/src/aiq_api/auth/jwt_validator.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/auth/jwt_validator.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/auth/middleware.py
Comment thread frontends/aiq_api/src/aiq_api/auth/jwt_validator.py Outdated
@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Tip:

Greploops — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.

Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

@cdgamarose-nv
cdgamarose-nv marked this pull request as draft April 8, 2026 15:56
@cdgamarose-nv
cdgamarose-nv marked this pull request as ready for review April 8, 2026 18:52
@AjayThorve

Copy link
Copy Markdown
Member

Tested end-to-end with Keycloak as an external OIDC provider to validate the ISV integration path.

Setup:

  • Keycloak 26.x running locally (http://localhost:8080/realms/aiq-test)
  • Created a minimal entry-point package (aiq-keycloak-test) with ~25 lines of code — registers a JWTValidator via the aiq_api.validators entry point. This is the full ISV integration surface.

Unit tests: 29/29 pass (pytest frontends/aiq_api/tests/test_auth.py)

Integration tests against live Keycloak (RS256 JWTs):

Mode Scenario Expected Result
Auth disabled (REQUIRE_AUTH=false) Request with no token 200, no regression Pass
Auth disabled Request with token 200, no regression Pass
Auth enabled (REQUIRE_AUTH=true) Valid Keycloak JWT 200, claims extracted Pass
Auth enabled Valid JWT + X-AIQ-Mode: headless 200, skip_clarifier=True Pass
Auth enabled No token 401 Missing auth token Pass
Auth enabled Garbage token 401 Invalid or expired auth token Pass
Auth enabled Disallowed path 404 Not found Pass
Auth enabled /health (auth-exempt) 200, no token required Pass
Auth enabled ContextVar reset after request Resets to default Pass

Full server test (nat serve with REQUIRE_AUTH=true):

  • curl with Bearer <garbage> -> 401 as expected
  • curl with valid Keycloak token -> request passes auth, reaches agent graph (hangs waiting on LLM — correct behavior, confirms auth passed)

Verified:

  • OIDC discovery -> JWKS fetch -> RS256 signature verification works against real Keycloak
  • Entry-point plugin discovery works (zero code changes to public repo needed)
  • skip_clarifier propagates correctly through ContextVar -> ChatResearcherState
  • Path filtering blocks non-allowlisted external routes
  • ContextVar properly resets between requests (no leaking between concurrent requests)

ISV replicability: Confirmed. An ISV needs only a pyproject.toml entry point + a get_validators() function returning [JWTValidator(issuer_url=...)]. No fork or code changes to the public repo required.

Follow-up Items (not in scope for this PR)

The PR includes application-level auth documentation (README + inline docstrings). The following deployment-level work is needed separately to fully enable auth in production:

  • docs/ auth guide — This PR adds backend API auth docs inline (frontends/aiq_api/README.md). A full docs/ guide covering the end-to-end story (backend middleware + frontend/UI auth + deployment config) should follow once the frontend OIDC wiring for arbitrary providers is in place.

@AjayThorve AjayThorve left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@cdgamarose-nv
cdgamarose-nv merged commit 7fec684 into NVIDIA-AI-Blueprints:develop Apr 8, 2026
4 checks passed
taylorjordanNC pushed a commit to taylorjordanNC/rh-research that referenced this pull request May 27, 2026
…s#173)

* update nat version and compatibility fixes

* middleware for auth for api access

* add unit tests for aiq api auth

* fix issues with missing dep packages

* direct import from module

* add nvidia-nat-core dependency

* make auth error user facing

* remove duplicate status field

* edit pyproject.toml

* remove log which prints token

* fix bugs in validator

* fix ruff check

* fix ruff check

* fix ruff version

* add aiq api as known first party

* lint fixes for new ruff

* fix dead links

* revert links
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.

2 participants