Skip to content

feat(client): Add client auth support#462

Open
matthewgrossman wants to merge 11 commits into
mainfrom
mgrossman/AUTH-aircore-828-add-first-class-auth-support-to-nemoclient-replace
Open

feat(client): Add client auth support#462
matthewgrossman wants to merge 11 commits into
mainfrom
mgrossman/AUTH-aircore-828-add-first-class-auth-support-to-nemoclient-replace

Conversation

@matthewgrossman

@matthewgrossman matthewgrossman commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class auth support to NemoClient / AsyncNemoClient so they can authenticate independently without requiring a NeMoPlatform instance. Also adds a get_nemo_client() global provider analogous to get_platform_sdk().

Linear: AIRCORE-828

What changed

New auth parameter on NemoClient / AsyncNemoClient:

  • auth="nvapi-xxx" — static token (wrapped in StaticToken)
  • auth=OIDCTokenProvider(...) — OIDC with auto-refresh
  • auth=None — no auth (default, backwards compatible)

Auth injection in send()Authorization: Bearer <token> header set on every request. Async send() handles three cases:

  1. get_access_token_async() — explicit async method (e.g. OIDCTokenProvider)
  2. async get_access_token() — pure async provider (coroutine function)
  3. Sync get_access_token() — wrapped in asyncio.to_thread to avoid blocking the event loop during token refresh IO

NemoClient.from_config() / AsyncNemoClient.from_config() — creates a client from ~/.config/nmp/config.yaml, wiring up OIDC token refresh, config persistence, and cross-process locking automatically.

NemoClientProvider protocol + get_nemo_client() — separate provider system (entry-point group nemo.client_provider) analogous to SDKProvider / get_platform_sdk(). Services migrate incrementally as downstream services expose typed endpoints.

Config + auth code moved from nemo_platform_ext into nemo_platform_plugin:

  • client/auth.pyTokenProvider, AsyncTokenProvider, StaticToken, OIDCTokenProvider, TokenSet, OIDC discovery, provider cache
  • client/config/models.pyOAuthUser, NoAuthUser, Cluster, Context, ConfigFile, etc.
  • client/config/config.pyConfig class with load(), resolve(), save(), write()
  • client_provider.pyNemoClientProvider, DefaultNemoClientProvider, get_nemo_client(), get_async_nemo_client()
  • dependencies.pyget_nemo_client() FastAPI dependency stub

Files

File Change
nemo_platform_plugin/client/auth.py New. Auth protocols, StaticToken, OIDCTokenProvider, OIDC helpers, provider factory
nemo_platform_plugin/client/config/models.py New. User-facing config models (clusters, users, contexts)
nemo_platform_plugin/client/config/config.py New. Config manager (load, resolve, save, write)
nemo_platform_plugin/client/client.py Add auth param, auth injection in send(), from_config() classmethods
nemo_platform_plugin/client_provider.py New. NemoClientProvider protocol + default provider
nemo_platform_plugin/dependencies.py Add get_nemo_client() FastAPI dependency stub
tests/test_client_auth.py New. 21 tests covering all new functionality

Merge order

This PR should land before PR #429 (AIRCORE-839, fsspec migration). Once merged, #429 rebases on top so FilesetFileSystem gets a NemoClient with first-class auth from the start — no skipped tests needed.

Test plan

  • NemoClient(auth="token") sets Authorization header on requests
  • NemoClient(auth=CustomProvider()) calls get_access_token() per request
  • AsyncNemoClient with async provider uses get_access_token_async()
  • AsyncNemoClient with sync provider runs in asyncio.to_thread
  • StaticToken satisfies TokenProvider protocol
  • OIDCTokenProvider refreshes expired tokens and persists via callback
  • OIDCTokenProvider raises when no refresh token available
  • Config.load() + resolve() with OAuth and NoAuth users
  • NemoClient.from_config() with OAuth, static token, and no-auth configs
  • AsyncNemoClient.from_config() returns correct client type
  • DefaultNemoClientProvider returns NemoClient / AsyncNemoClient
  • get_nemo_client() / get_async_nemo_client() delegate to provider
  • All 734 existing tests continue to pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added first-class auth to Nemo clients with automatic Authorization: Bearer … header injection and shared sync/async token providers.
    • Introduced config-driven client creation with context resolution, OAuth/OIDC token support, and proactive refresh.
    • Added a client provider layer to generate clients with service/internal/on-behalf-of request headers.
  • Bug Fixes
    • Improved recovery for expired/invalid refresh tokens and handling of legacy token/config entries.
  • Tests
    • Added comprehensive unit tests for token lifecycles, client auth injection, config resolution, and provider wiring.

Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
@matthewgrossman matthewgrossman requested review from a team as code owners June 25, 2026 17:37
…uth-support-to-nemoclient-replace

Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
@github-actions github-actions Bot added the feat label Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds auth, config, client construction, provider resolution, and FastAPI dependency support for Nemo clients, with tests covering auth, config, and provider flows.

Changes

Nemo client auth and config plumbing

Layer / File(s) Summary
Config models and validation
packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/models.py
OAuthUser, NoAuthUser, clusters, contexts, preferences, override params, and resolved context models are added with context validation.
Config loading and runtime resolution
packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py, packages/nemo_platform_plugin/tests/test_client_auth.py
Config now loads, migrates, saves, reloads, resolves, and creates default contexts; tests cover load, resolve, and write round-trips.
Auth primitives and OIDC helpers
packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py, packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py, packages/nemo_platform_plugin/tests/test_client_auth.py
Token-provider protocols, static token wrapping, JWT helpers, OIDC discovery, refresh-token exchange, token expiry checks, and OIDC provider refresh logic are added; tests cover static tokens and OIDC refresh behavior.
Client auth wiring
packages/nemo_platform_plugin/src/nemo_platform_plugin/client/client.py, packages/nemo_platform_plugin/tests/test_client_auth.py
NemoClient and AsyncNemoClient accept auth providers, inject bearer headers, and build from config or bearer headers; tests cover sync, async, and from_config paths.
Provider resolution and dependency stub
packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py, packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py, packages/nemo_platform_plugin/tests/test_client_auth.py
The provider protocol, default provider, discovery, public factory functions, and FastAPI dependency stub are added; tests cover default and overridden provider selection.

Possibly related PRs

Suggested reviewers

  • maxdubrinsky
  • SandyChapman
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding client authentication support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mgrossman/AUTH-aircore-828-add-first-class-auth-support-to-nemoclient-replace

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (1)
packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py (1)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a regular import and concrete return annotation.

Move AsyncNemoClient out of TYPE_CHECKING and annotate directly. As per coding guidelines, **/*.py: “Always prefer concrete type hints over string-based ones in Python code; do not import types under TYPE_CHECKING, instead import types as regular imports when possible.”

Proposed fix
-    from nemo_platform_plugin.client.client import AsyncNemoClient
+from nemo_platform_plugin.client.client import AsyncNemoClient
-def get_nemo_client() -> "AsyncNemoClient":
+def get_nemo_client() -> AsyncNemoClient:

Also applies to: 55-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py` at
line 16, The dependency provider is still relying on a TYPE_CHECKING-only import
and a string-based return annotation, which should be replaced with a regular
import and a concrete type hint. Update the dependency module that exposes
AsyncNemoClient so the symbol is imported normally at runtime and the provider
function returns AsyncNemoClient directly, keeping the annotation concrete and
removing the need for deferred typing.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py`:
- Around line 141-151: The header builder in _build_headers() currently forwards
id, email, and groups from _read_principal_from_env() but drops the delegated
identity field. Update the principal-handling block in client_provider.py so
that when principal includes on_behalf_of, it is propagated into the headers
using the same X-NMP-Principal-On-Behalf-Of key used for the explicit
on_behalf_of parameter, and keep the existing explicit parameter behavior
intact.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py`:
- Around line 284-290: The TokenSet dataclass is exposing sensitive credentials
through its auto-generated repr, and OIDCTokenProvider may also print them via
its tokens field. Update TokenSet so access_token and refresh_token are excluded
from repr, and ensure any TokenSet stored or returned by OIDCTokenProvider does
not leak token values in debug output by using the repr-safe fields
consistently. Locate the fix in TokenSet and the OIDCTokenProvider token
handling logic.
- Around line 426-437: The token refresh flow in `AuthClient` updates
`self.tokens` and then calls `on_tokens_refreshed()`, but if persistence fails
after a rotated refresh token is received, the failure is only logged and the
process continues with an unsaved invalid token. Update the `refresh` logic
around `TokenSet.from_access_token`, `self.on_tokens_refreshed`, and the
`logger.warning` path so persistence failures are surfaced to the caller or
otherwise stop the refresh from being treated as successful when the refresh
token has rotated.
- Around line 246-263: refresh_token_grant currently sends refresh tokens to
whatever token_endpoint is provided, so add endpoint validation before the
httpx.post call. In refresh_token_grant, reject malformed URLs and non-HTTPS
schemes up front, but allow explicit HTTP loopback/local-dev endpoints such as
localhost or 127.0.0.1. Keep the check close to the existing token_endpoint
handling so the function only proceeds to build data and post when the endpoint
is safe and valid.
- Around line 426-430: The refresh flow in auth.py is rebuilding the TokenSet
with TokenSet.from_access_token(), which ignores expires_in from the refresh
response and can leave opaque access tokens without an expiration. Update the
token refresh path in the client auth logic to read token_data["expires_in"]
(falling back to the current JWT exp behavior when available), compute and store
expires_at accordingly, and preserve the rotated refresh token when constructing
self.tokens so auto-refresh continues to work after the first refresh.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/client.py`:
- Around line 335-341: The from_config flow is ignoring the requested context,
so Config.load and resolve are using the active/default context instead of the
caller’s override. Update the client configuration path in from_config to pass
the provided context through to the config loading/resolution path, and ensure
the same context is used when computing the resolved config state via
Config.load, config.resolve, and any related current_context handling.
- Around line 345-353: The token passed from ctx.user in the OAuthUser branch is
still treated as non-explicit, which can cause resolve_oidc_provider() to cache
or persist a caller-owned access token. Update the auth resolution path in
client.py to distinguish runtime-provided tokens from stored credentials, and
pass an explicit_access_token flag through the call chain so
resolve_oidc_provider() can avoid persisting tokens sourced from
NMP_ACCESS_TOKEN. Use the ctx.user/OAuthUser handling near
resolve_oidc_provider() to locate the change.

In
`@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py`:
- Around line 357-358: Preserve explicit false overrides in the config assembly
logic: the truncate setting is being skipped in the config path because the
`self.truncate` truthiness check drops `False`, so update the `Config`/config
resolution code that builds `params` to distinguish “unset” from an explicit
boolean value and always propagate `truncate=False` from `NMP_TRUNCATE=false` or
`{"truncate": False}`.
- Around line 139-144: The config lookup in the config path resolver should
honor XDG_CONFIG_HOME even when the nmp directory has not been created yet.
Update the logic in the config path selection method (the block in config.py
that checks xdg_config_home and returns the config.yaml path) so it returns the
XDG-based location unconditionally when XDG_CONFIG_HOME is set, instead of
requiring config_dir.exists(). Keep the existing fallback behavior only for when
XDG_CONFIG_HOME is unset.
- Around line 225-240: The write flow in Config.write resolves and creates the
target context before applying params["current_context"], which can persist a
new current_context without ensuring that context exists. Update Config.write to
read and apply params["current_context"] early, then use that effective context
when determining context_name, calling cls.load/cls.create, and passing through
config_file.ensure_context. Make sure current_context and the created/selected
context stay aligned so a write with only current_context still creates a valid
context before persisting.
- Around line 201-210: The config save flow in config.py writes secrets via
self._config_file.model_dump(include_secrets=True) and then opens the target
file with open(path, "w"), which can leave the file briefly created/truncated
with default umask permissions before os.chmod tightens it. Update the save
logic in the config-writing path to ensure the destination is set to 0600 before
any secret-containing content is written, ideally by preparing/chmodding the
file first or using a secure create/write sequence around the existing
yaml.safe_dump call.

---

Nitpick comments:
In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py`:
- Line 16: The dependency provider is still relying on a TYPE_CHECKING-only
import and a string-based return annotation, which should be replaced with a
regular import and a concrete type hint. Update the dependency module that
exposes AsyncNemoClient so the symbol is imported normally at runtime and the
provider function returns AsyncNemoClient directly, keeping the annotation
concrete and removing the need for deferred typing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 16701030-b694-4838-8d52-fe4b7285a39d

📥 Commits

Reviewing files that changed from the base of the PR and between 1a8a0f8 and a045a7d.

📒 Files selected for processing (7)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/client.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/models.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client_provider.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/dependencies.py
  • packages/nemo_platform_plugin/tests/test_client_auth.py

Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 21910/28758 76.2% 61.0%
Integration Tests 12590/27438 45.9% 19.2%

matthewgrossman and others added 3 commits June 25, 2026 11:21
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…implify config_exists, add edge case tests

- Remove unused _on_behalf_of_headers from client_provider.py
- Remove duplicate _TOKEN_REFRESH_MARGIN_SECONDS, use DEFAULT_REFRESH_MARGIN_SECONDS
- Simplify config_exists logic in _client_from_config
- Add tests: invalid_grant recovery, nonexistent config path, Config.write round-trip

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…ules

- auth.py: protocols (TokenProvider, AsyncTokenProvider), StaticToken, AuthError
- oidc.py: OIDCTokenProvider, TokenSet, JWT helpers, OIDC discovery, TokenRefreshError
- oidc_factory.py: provider cache, config persistence callbacks, cross-process
  locking, resolve_oidc_provider

No re-exports — each consumer imports from the actual module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove postponed annotations.

This stringifies annotations across the module. Convert the forward return on TokenSet.from_access_token() to a concrete runtime-safe form, then drop the future import.

As per coding guidelines, "**/*.py: Always prefer concrete type hints over string-based ones in Python code; do not import types under TYPE_CHECKING, instead import types as regular imports when possible."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py` at
line 15, The module is still relying on postponed annotations, which stringifies
type hints across the file. Update the forward return annotation on
TokenSet.from_access_token() to a concrete runtime-safe type so it no longer
depends on future annotations, then remove the from __future__ import
annotations line from the oidc module. Keep the existing TokenSet symbol and any
referenced types as regular imports so the annotations remain concrete and
runtime-safe.

Source: Coding guidelines

packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove postponed annotations.

from __future__ import annotations makes annotations string-backed module-wide; use concrete runtime annotations/imports instead.

As per coding guidelines, "**/*.py: Always prefer concrete type hints over string-based ones in Python code; do not import types under TYPE_CHECKING, instead import types as regular imports when possible."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py` at
line 13, Remove the postponed-annotations import from auth.py and switch the
module to concrete runtime type hints/imports instead of string-based
annotations. Update any affected annotations in the relevant symbols in this
file so they reference real imported types directly, and avoid relying on
TYPE_CHECKING-only imports where regular imports are possible.

Source: Coding guidelines

packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc_factory.py (1)

11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove postponed annotations.

No forward references here require module-wide postponed annotations; keep type hints concrete at runtime.

As per coding guidelines, "**/*.py: Always prefer concrete type hints over string-based ones in Python code; do not import types under TYPE_CHECKING, instead import types as regular imports when possible."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc_factory.py`
at line 11, Remove the module-wide postponed annotations import from
oidc_factory since there are no forward references that need it. Update the type
hints in the OIDC factory module to use concrete runtime types directly,
following the existing style in symbols like the factory helpers/classes in this
file, and keep any needed imports as regular imports rather than relying on
postponed evaluation.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc_factory.py`:
- Around line 160-165: The auto-refresh setup in oidc_factory should fail fast
when discovery does not provide refresh settings, instead of allowing empty
token_endpoint/client_id to reach the refresh flow. Update the logic in the OIDC
client factory around _discover_oidc_client_settings and the
TokenSet/auto-refresh provider construction to validate
oidc_config.token_endpoint and oidc_config.client_id before proceeding, and
raise a clear error or skip auto-refresh when either value is missing. Use the
existing symbols oidc_config, token_endpoint, client_id, and refresh_scope to
keep the check close to where the provider is built.
- Around line 154-170: `Config.resolve()` is treating `Config.access_token` like
a config-file token, so runtime env/CLI overrides can be cached or persisted
incorrectly. Update the `Config.resolve()` call path to pass
`explicit_access_token=True` whenever the token comes from a runtime override,
and keep `build_oidc_token_provider()` using that flag to set `share_provider`
only for tokens sourced from actual config files. Ensure the `OIDCTokenProvider`
creation path still distinguishes resolved config tokens from override tokens.

---

Nitpick comments:
In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py`:
- Line 13: Remove the postponed-annotations import from auth.py and switch the
module to concrete runtime type hints/imports instead of string-based
annotations. Update any affected annotations in the relevant symbols in this
file so they reference real imported types directly, and avoid relying on
TYPE_CHECKING-only imports where regular imports are possible.

In
`@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc_factory.py`:
- Line 11: Remove the module-wide postponed annotations import from oidc_factory
since there are no forward references that need it. Update the type hints in the
OIDC factory module to use concrete runtime types directly, following the
existing style in symbols like the factory helpers/classes in this file, and
keep any needed imports as regular imports rather than relying on postponed
evaluation.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py`:
- Line 15: The module is still relying on postponed annotations, which
stringifies type hints across the file. Update the forward return annotation on
TokenSet.from_access_token() to a concrete runtime-safe type so it no longer
depends on future annotations, then remove the from __future__ import
annotations line from the oidc module. Keep the existing TokenSet symbol and any
referenced types as regular imports so the annotations remain concrete and
runtime-safe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 19f151bf-8d79-4041-bc94-7c1e1388aa13

📥 Commits

Reviewing files that changed from the base of the PR and between 678c268 and 644fcc5.

📒 Files selected for processing (6)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/auth.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/client.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc_factory.py
  • packages/nemo_platform_plugin/tests/test_client_auth.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/config/config.py
  • packages/nemo_platform_plugin/tests/test_client_auth.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/client.py

matthewgrossman and others added 5 commits June 25, 2026 12:17
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Bug fixes:
- Fix context parameter silently ignored in from_config()
- Propagate on_behalf_of from NMP_PRINCIPAL in client_provider headers

Security:
- Hide token values from TokenSet/OIDCTokenProvider repr
- Validate HTTPS on token endpoint (allow HTTP loopback for local dev)
- Fail fast when OIDC discovery can't provide token_endpoint/client_id

Style:
- Use concrete type hint for AsyncNemoClient in dependencies.py
- Fix refresh_token_grant return type to dict[str, Any]

Tests:
- Add test for from_config context selection
- Add tests for legacy api-key migration
- Add tests for token repr hiding and endpoint validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…uth-support-to-nemoclient-replace

Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
- Honor expires_in from refresh responses for opaque (non-JWT) tokens
- Surface persistence failure when IdP rotates the refresh token
  (only swallow persistence errors when the refresh token wasn't rotated)
- Pass explicit_access_token=True when token comes from NMP_ACCESS_TOKEN
  env var so the provider isn't cached/persisted against the config file
- Honor XDG_CONFIG_HOME even before the directory exists
- Set file permissions (0600) atomically via os.open before writing secrets
  (eliminates race window where file is world-readable)
- Use params["current_context"] to determine write target in Config.write()
- Preserve explicit truncate=False in default config resolution
  (use `is not None` checks instead of truthiness)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…uth-support-to-nemoclient-replace

Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants