Skip to content

Commit 9670175

Browse files
committed
update
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
1 parent d7869e9 commit 9670175

16 files changed

Lines changed: 484 additions & 68 deletions

File tree

docs/set-up/config-reference.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ jobs:
315315
password_env_var: AUTHENTIK_WORKLOAD_IDENTITY_PASSWORD
316316
# OAuth scope for the Docker demo issuer.
317317
scope:
318+
# Docker workload proof-token provider. 'auto' uses an in-process auth-owned JWT issuer when available and otherwise uses an opaque proof token stored by hash in the workload delegation record. | default: 'auto' | values: 'auto' | 'jwt' | 'opaque'
319+
proof_token_provider: auto
318320
# Fallback subject-token lifetime when the Docker demo issuer response omits expires_in.
319321
subject_token_ttl_seconds: 600
320322
# Seconds before subject-token expiry when the Docker refresher issues a replacement token. | default: 60

openapi/ga/individual/platform.openapi.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openapi/ga/openapi.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openapi/openapi.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/nemo_platform_ext/src/nemo_platform_ext/auth/workload_exchange.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
2727
JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"
2828
ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
29+
DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE = "urn:nvidia:nemo:params:oauth:token-type:docker-opaque-workload-proof"
30+
DOCKER_OPAQUE_WORKLOAD_PROOF_PREFIX = "nmp_obo_v1."
2931

3032

3133
class WorkloadTokenExchangeError(RuntimeError):
@@ -72,6 +74,12 @@ def _validate_token_endpoint(token_endpoint: str) -> None:
7274
)
7375

7476

77+
def subject_token_type_for_exchange(subject_token: str) -> str:
78+
if subject_token.startswith(DOCKER_OPAQUE_WORKLOAD_PROOF_PREFIX):
79+
return DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE
80+
return JWT_TOKEN_TYPE
81+
82+
7583
def token_exchange_grant(
7684
*,
7785
token_endpoint: str,
@@ -87,7 +95,7 @@ def token_exchange_grant(
8795
"grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
8896
"client_id": client_id,
8997
"subject_token": subject_token,
90-
"subject_token_type": JWT_TOKEN_TYPE,
98+
"subject_token_type": subject_token_type_for_exchange(subject_token),
9199
"requested_token_type": ACCESS_TOKEN_TYPE,
92100
}
93101
if audience:

packages/nemo_platform_ext/tests/auth/test_workload_exchange.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
from base64 import urlsafe_b64encode
99
from unittest.mock import MagicMock, patch
1010

11+
import httpx
1112
import pytest
1213
from nemo_platform_ext.auth.workload_exchange import (
1314
ACCESS_TOKEN_TYPE,
15+
DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE,
1416
JWT_TOKEN_TYPE,
1517
TOKEN_EXCHANGE_GRANT_TYPE,
1618
WorkloadTokenExchangeError,
@@ -74,6 +76,19 @@ def test_token_exchange_grant_sends_rfc8693_request(mock_post):
7476
}
7577

7678

79+
@patch("nemo_platform_ext.auth.workload_exchange.httpx.post")
80+
def test_token_exchange_grant_uses_docker_opaque_subject_token_type(mock_post):
81+
mock_post.return_value = httpx.Response(200, json={"access_token": "exchanged-token", "expires_in": 300})
82+
83+
token_exchange_grant(
84+
token_endpoint="https://idp.example.com/token",
85+
client_id="nemo-platform-workload",
86+
subject_token="nmp_obo_v1.delegation.secret",
87+
)
88+
89+
assert mock_post.call_args.kwargs["data"]["subject_token_type"] == DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE
90+
91+
7792
@patch("nemo_platform_ext.auth.workload_exchange.httpx.post")
7893
def test_token_exchange_grant_rejects_http_non_loopback_endpoint_before_sending_subject_token(mock_post):
7994
with pytest.raises(ValueError, match="must use HTTPS"):

packages/nemo_platform_plugin/src/nemo_platform_plugin/client/oidc.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ def generate_unsigned_jwt(
126126
TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
127127
JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"
128128
ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
129+
DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE = "urn:nvidia:nemo:params:oauth:token-type:docker-opaque-workload-proof"
130+
DOCKER_OPAQUE_WORKLOAD_PROOF_PREFIX = "nmp_obo_v1."
129131

130132

131133
@dataclass(frozen=True)
@@ -292,6 +294,12 @@ def read_subject_token_file(path: Path) -> str:
292294
return token
293295

294296

297+
def subject_token_type_for_exchange(subject_token: str) -> str:
298+
if subject_token.startswith(DOCKER_OPAQUE_WORKLOAD_PROOF_PREFIX):
299+
return DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE
300+
return JWT_TOKEN_TYPE
301+
302+
295303
def token_exchange_grant(
296304
*,
297305
token_endpoint: str,
@@ -309,7 +317,7 @@ def token_exchange_grant(
309317
"grant_type": TOKEN_EXCHANGE_GRANT_TYPE,
310318
"client_id": client_id,
311319
"subject_token": subject_token,
312-
"subject_token_type": JWT_TOKEN_TYPE,
320+
"subject_token_type": subject_token_type_for_exchange(subject_token),
313321
"requested_token_type": ACCESS_TOKEN_TYPE,
314322
}
315323
if audience:

packages/nemo_platform_plugin/tests/test_client_auth.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from nemo_platform_plugin.client.constants import WORKLOAD_IDENTITY_TOKEN_FILE_ENVVAR
2727
from nemo_platform_plugin.client.oidc import (
2828
ACCESS_TOKEN_TYPE,
29+
DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE,
2930
JWT_TOKEN_TYPE,
3031
TOKEN_EXCHANGE_GRANT_TYPE,
3132
NMPOIDCConfig,
@@ -335,6 +336,20 @@ def test_token_exchange_grant_sends_rfc8693_request(self, monkeypatch):
335336
verify=True,
336337
)
337338

339+
def test_token_exchange_grant_uses_docker_opaque_subject_token_type(self, monkeypatch):
340+
monkeypatch.delenv(NMP_CLIENT_SSL_CERT_FILE_ENVVAR, raising=False)
341+
342+
with patch("nemo_platform_plugin.client.oidc.httpx.post") as mock_post:
343+
mock_post.return_value = httpx.Response(200, json={"access_token": "exchanged-token", "expires_in": 300})
344+
345+
token_exchange_grant(
346+
token_endpoint="https://idp.example.com/token",
347+
client_id="nemo-platform-workload",
348+
subject_token="nmp_obo_v1.delegation.secret",
349+
)
350+
351+
assert mock_post.call_args.kwargs["data"]["subject_token_type"] == DOCKER_OPAQUE_WORKLOAD_PROOF_TOKEN_TYPE
352+
338353
def test_token_exchange_grant_uses_nemo_scoped_ca_bundle(self, monkeypatch):
339354
monkeypatch.setenv(NMP_CLIENT_SSL_CERT_FILE_ENVVAR, "/tmp/nemo-ca.pem")
340355

sdk/python/nemo-platform/src/nemo_platform/auth/workload_exchange.py

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/auth/test_workload_exchange.py

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)