Skip to content

ateapi: make the actor JWT issuer configurable and add typ to the header - #1834

Merged
Taahir Ahmed (ahmedtd) merged 2 commits into
agent-substrate:mainfrom
thompsonmax:actor-jwt-issuer-flag
Sep 25, 2026
Merged

Taahir Ahmed (ahmedtd) merged 2 commits into
agent-substrate:mainfrom
thompsonmax:actor-jwt-issuer-flag

Conversation

@thompsonmax

@thompsonmax Max Thompson (thompsonmax) commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #1756.

  • Adds --actor-jwt-issuer. Unset, it defaults to https://idp.<namespace>.svc, the Service ate-idp-server will serve discovery from. Before this, iss was hardcoded to https://api.ate-system.svc, which points at ateapi's gRPC port.
  • Adds oidcdiscovery.ParseIssuer, which requires a canonical https URL with no query, fragment, or user info and strips trailing slashes. ate-idp-server will use it too, so both emit the same bytes.
  • Actor JWT headers now include typ: JWT.
  • Drops the actorIdentityJWTIssuer plumbing into RPCService, unused since Identity: Move actor JWT/cert minting into the main API #1315.

The default iss changes, but MintActorJWT has no production caller yet.

Testing: new unit tests for the parser, the default, the header, and flag resolution. TestMintActorJWT_Success now checks typ, iss, and sub. It needs Docker, so it hasn't run locally.

Comment thread internal/oidcdiscovery/issuer.go Resolved
Comment thread internal/oidcdiscovery/issuer.go Resolved
Actor JWTs carried the hardcoded issuer https://api.ate-system.svc. That
URL points at ateapi's gRPC port, which serves no discovery document, so
nothing outside ateapi could verify the tokens.

Add --actor-jwt-issuer. When it is unset, the issuer is
https://idp.<namespace>.svc, the Service that will serve the discovery
document and key set. Relying parties compare issuers byte for byte, so
the value must be a canonical https URL with no query, fragment, or user
info. Trailing slashes are stripped.

Tokens now carry typ: JWT in the JOSE header, as RFC 8725 section 3.11
recommends.

RPCService's actorIdentityJWTIssuer field had no reader once minting
moved into the control API, so its constructor slot now carries the actor
JWT issuer.
Replace ParseIssuer with ValidateIssuer, which checks the issuer without
rewriting it. ateapi no longer strips trailing slashes or rejects URLs
that url.Parse would re-encode.

Relying parties compare iss byte for byte against the issuer they
registered, so the token has to carry the operator's exact string.
OpenID Connect Discovery already has relying parties drop a trailing
slash before appending /.well-known/openid-configuration.
@ahmedtd
Taahir Ahmed (ahmedtd) added this pull request to the merge queue Sep 25, 2026
Merged via the queue into agent-substrate:main with commit d3a556a Sep 25, 2026
11 checks passed
jiayimeow pushed a commit to jiayimeow/substrate that referenced this pull request Oct 19, 2026
…entity docs (agent-substrate#2597)

Part of agent-substrate#1756.

**Why:** every install still has to set `actorIdentityJWTProvider`, and
`docs/authentication.md` says it decides who may mint actor JWTs. It no
longer decides anything:

- agent-substrate#757 added it when minting lived in a separate `ActorIdentity`
service. `MintJWT` refused any caller whose JWT came from a different
provider's issuer, which in practice limited minting to atelet's
Kubernetes service account tokens.
- agent-substrate#1315 moved minting into `ateapi.Control` and dropped that check,
leaving caller limits to the authz framework.
- agent-substrate#1834 removed the last code that read the provider's issuer.

So ate-api-server requires a field and fails to start if it names a
missing provider, yet nothing uses it, and the docs say it decides who
may mint, which authorization now decides. Section 7 of
`docs/api-guide.md` still describes the old service and its atelet-only
rules.

**What changes:**

- `actorIdentityJWTProvider` is gone from ate-api-server's
authentication config, and ate-setup stops writing it.
- `docs/authentication.md` drops it and keeps the pointer to
`authorization.md` for what a caller may do.
- Section 7 of `docs/api-guide.md` now describes `MintActorJwt` and
`MintActorCertificate` on `ateapi.Control`, who may call them, their
fields, and the JWT's claims, instead of the old service, its caller
checks, and a wrong SPIFFE path. The ADK paragraph, which pointed at the
same service, now describes the egress gateway attaching actor JWTs.

For reviewers: the config is parsed strictly and ate-setup only creates
`ate-api-authentication` when it's missing, so an existing install must
delete the `actorIdentityJWTProvider` line from that ConfigMap before
ate-api-server restarts on this version.

Testing: unit tests for the config loader and ate-setup's rendered
config. I didn't run e2e; CI's e2e installs a fresh kind cluster, so it
doesn't exercise the upgrade case.

- [ ] Tests pass
- [x] Appropriate changes to documentation are included in the PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api-machinery area/identity kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants