Agent authentication Module
Three ways an agent gets a token
Every agent action is authenticated by a short vaks_pat_ token that carries the agent's identity. There are three ways to obtain one, and they differ only in where the trust comes from:
| Method | Trust anchor | Best for |
|---|---|---|
| Manual token | A human issues a token in the admin and hands it to the agent. | A handful of agents; anything without an identity platform. Covered in Agent directories → Activate. |
| Federation this guide | The agent's execution platform signs a token proving its identity; Vaks PM verifies it and exchanges it. No secret is ever distributed. | Agents running on a platform that issues workload identities: Kubernetes, Microsoft Entra (Copilot Studio, Azure AI Foundry), GitHub Actions. |
| Headless self-service this guide | The agent holds a rotating refresh credential and renews short tokens itself. | Agents with no identity platform: a self-hosted runtime, a script, a bespoke orchestrator. |
agent-federation feature on your licence. The manual token path always works and is never a prerequisite — federation is the convenience that removes secret handling at scale, not a gate on running agents.
Which one to use
The deciding question is simple: does the platform running the agent hand it a signed identity token?
- Yes — Kubernetes projects a ServiceAccount token; Entra issues a workload-identity token for a service principal; GitHub Actions mints an OIDC token per job. Use federation. Nothing is stored on either side.
- No — the agent is just code you run somewhere, with no surrounding identity fabric. Use headless self-service: a human issues one durable credential, the agent rotates it forever after.
Federation — what to configure in Vaks PM
Federation follows the OAuth 2.0 token-exchange standard (RFC 8693). You declare which issuer you trust; the agent then presents a token from that issuer and receives a short Vaks PM token in return.
Open Admin → Integrations → Federated agent identity and press Add issuer. The fields:
| Field | What to put |
|---|---|
| Label | Free text, shown in the list. |
| Issuer | The exact iss claim the platform puts in its tokens, character for character. For Entra: https://login.microsoftonline.com/<tenant-id>/v2.0. |
| Audience | The value the platform will place in the token's aud claim, e.g. api://vaks-pm/<your-slug>. Mandatory — it is the single control that stops a token the agent obtained for another service (say Microsoft Graph) from being accepted here. Whatever you choose, configure the same string on the platform side. |
| Identity source | Must match the agent's own source: entra-agent for agents imported from Entra, api for agents registered inbound, or a value of your own for a self-declared issuer such as k8s. An agent can only be authenticated by an issuer whose identity source matches its provenance — a defence-in-depth check. |
| Signing keys | One of three: an OIDC discovery URL (Vaks PM resolves the key set for you), a JWKS URI, or a pasted JWKS. Paste the key set when the issuer is internal or air-gapped — verification then makes no outbound call at all. |
| Subject claim | The claim holding the agent's identifier, default sub. This must equal the agent's stored external ID. Entra workload tokens sometimes carry it in oid instead — this is the field to adjust if a valid token is rejected as an unknown agent. |
| Token TTL | Lifetime of the issued token, 300–3600 seconds. |
| Claim constraints (optional) | Extra claims required for an exact match, e.g. { "repository": "org/repo" } to accept only one GitHub repository. |
| Enable this issuer | Until ticked, every exchange is refused. |
Press Test to confirm the key set is reachable. Then make sure the agent that will authenticate has its external ID set to the value the platform puts in the subject claim — that is the link between the signed token and the Vaks PM identity.
Once an issuer is declared and the agent is activated, the agent authenticates like this — the platform provides the JWT, the agent only forwards it:
POST /api/v1/auth/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<the platform-issued JWT>
&subject_token_type=urn:ietf:params:oauth:token-type:jwt
→ { "access_token": "vaks_pat_…", "expires_in": 3600, "scope": "…" }
The returned token is an ordinary agent token, bounded by the agent's capabilities. When it expires the agent simply exchanges again — there is no refresh token to manage, because the platform re-issues the source JWT on demand.
Platform recipes
The Vaks PM side is identical everywhere — the issuer declaration above. What changes is how the platform is told to issue a token for your audience. We do not reproduce each platform's own documentation here; the links go straight to the authoritative source. In every case, the value you set as Audience in Vaks PM must be the same value the platform is configured to request.
Kubernetes & generic OIDC issuers
The simplest case, and the cleanest for an on-premises deployment. A pod requests a projected ServiceAccount token with your audience; the kubelet writes it to a file and rotates it. In Vaks PM, set the issuer to your cluster's OIDC issuer and either point the JWKS URI at it or paste the cluster's key set for a fully air-gapped setup.
- Microsoft — not applicable; see the Kubernetes project docs: ServiceAccount token volume projection.
Microsoft Copilot Studio
A Copilot Studio agent can act as itself once you enable Entra Agent ID for the environment: it receives an Entra service principal and authenticates secret-less using federated identity credentials. Configure its call to Vaks PM with your audience, and declare the Entra tenant as the issuer here.
- Microsoft — Automatically create Entra agent identities.
Azure AI Foundry
A Foundry agent is given a dedicated Entra identity through a managed identity and a federated credential; at runtime Foundry acquires a token for that identity scoped to a downstream audience you configure. Set that audience to your Vaks PM value, and declare the Entra tenant as the issuer here. This is the closest match to the token-exchange model — Foundry does most of the work before it even reaches Vaks PM.
- Microsoft — Agent identity concepts in Microsoft Foundry.
GitHub Actions
A workflow with id-token: write can mint a short OIDC token per job for your audience. Use claim constraints in the issuer to pin it to a single repository or environment.
- GitHub — Security hardening with OpenID Connect.
aud equals your Vaks PM audience, and whose subject claim equals the external ID stored on the agent. Both are configurable here precisely so you can line them up, but confirm the first successful exchange against your real tenant before relying on it in production.
Headless self-service — machine credentials
When there is no platform to sign a token — a self-hosted agent, a script, a bespoke runtime — the agent instead holds a durable, rotating refresh credential and renews short working tokens on its own. A human issues it once; after that no human is in the loop.
Open Admin → Users & Identity → Agents, select the agent, and under Machine credentials press Issue credential. The secret (vaks_agr_…) is shown exactly once — hand it to the agent's own configuration store. The agent then loops:
POST /api/v1/agents/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=vaks_agr_…
→ { "access_token": "vaks_pat_…", // ~1 h, use on every call
"refresh_token": "vaks_agr_…", // NEW — store it, the old one is now spent
"expires_in": 3600 }
Two properties make this safe to leave running unattended:
- The refresh rotates. Each exchange returns a fresh refresh token and retires the previous one (after a short grace window that absorbs a dropped response). Presenting a spent token outside that window is treated as theft: the whole credential chain is revoked at once and the event is logged.
- The working token stays short. The refresh secret only ever travels to
/agents/token; the token used on real API calls lives about an hour. A leaked working token dies quickly on its own.
Revoking the credential (same screen) cuts the agent off immediately — it would need a human to issue a new one. Suspending the agent, or its human owner, has the same effect on the next call.
External agents & their exposure limit
An agent hosted on an external platform (Copilot Studio, Azure AI Foundry, a cloud runtime) has to reach Vaks PM over the network. In an on-premises deployment you do not expose the internal API to the internet: a minimal, secret-less relay in a DMZ is the single public door, and it forwards only a curated surface to the internal API. The internal API, database, cache and object store are never directly reachable.
That relay draws a deliberate network boundary around what an externally-hosted agent can reach in REST, on top of the per-request checks. A federated agent is bounded twice over:
| Layer | What it enforces |
|---|---|
| Capabilities (always) | An agent's effective rights are its role's permissions intersected with its token's scopes intersected with its capabilities. Finance, client data, the user directory, timesheet and audit can never be granted to an agent, whatever route it reaches. |
| Network allow-list (hardened DMZ deployment) | The relay forwards only the agent work surface — tasks, projects, deliverables, comments, attachments, skills, workload, allocations, time, agent runs, teams, holidays. The finance, clients, users, audit and reporting controllers are not forwarded at all, so they are not even reachable from the internet by this path. |
The capability ceiling is the authoritative, always-on boundary; the network allow-list is defence in depth for the hardened deployment. In the demo, where the whole app is internet-facing for convenience, only the capability ceiling applies — the network narrowing is a property of the DMZ relay, which ships as a deployment template (infra/dmz/) rather than being on by default.
What is logged
Everything here leaves an audit trail under Admin → Security & Compliance → Audit log, exportable to your SIEM:
| Action | Recorded |
|---|---|
| Issuer created, updated, deleted | Actor and issuer, at critical severity. Declaring who may vouch for an agent is a root-of-trust decision. |
| Machine credential issued, revoked | Actor and target agent, at critical severity. A theft-triggered revocation is recorded with its reason. |
| Successful exchange | At informational severity, attributed to actor type AGENT — a token is minted once per hour per agent, so these are frequent by design. |
| Refused exchange | At warning severity, with the reason (unknown issuer, wrong audience, replay, inactive owner, and so on) — these are what you watch. |
Related: agent directories to create the agents first · product & features for the agent governance model · AI connector (MCP) for user-delegated access · all integrations.