Identity / Connect your agents to Speakeasy
Connect your agents to Speakeasy
Trust Okta as an issuer so agents present an Okta identity assertion (ID-JAG) to the platform and reach MCP servers as the person they act for, with no browser sign-in.
Enterprise Managed Auth is in preview. Contact your Speakeasy account team to enable the Enterprise Managed Auth feature flags for your organization.
An agent that already holds a person’s Okta identity can reach an MCP server without a browser sign-in. It asks Okta for an Identity Assertion JWT Authorization Grant (ID-JAG) addressed to the platform, and the platform’s authorization server trades it for an access token. Okta decides which agents may act for which people; the platform trusts Okta as the issuer. This is the inbound half of Enterprise Managed Auth.
Access requirements
Section titled “Access requirements”Trusting Okta requires the org:admin scope, which only the default Admin
role holds. Choosing
a server’s user session issuer requires project:write. The Okta side needs
an administrator who can configure Cross App Access.
How it works
Section titled “How it works”The agent starts from the person’s Okta ID token and makes two exchanges: one at Okta for an ID-JAG, and one at the MCP server’s authorization server for an access token. Neither step opens a browser.
Each MCP server whose user session issuer trusts Okta announces the JWT bearer grant and the ID-JAG profile in its authorization server metadata. Agents that support Enterprise Managed Authorization find it there.
Setup at a glance
Section titled “Setup at a glance”| Step | Where | What |
|---|---|---|
| 1. Connect Okta | Dashboard and Okta | The Okta connection, done once |
| 2. Trust Okta as an issuer | Dashboard | An organization-level sign-in issuer that trusts Okta |
| 3. Use it on your MCP servers | Dashboard | Each server’s User session issuer |
| 4. Register the agent | Agent | A client at the server’s authorization server |
| 5. Allow the agent in Okta | Okta | Speakeasy as a Cross App Access resource for the agent |
1. Connect Okta
Section titled “1. Connect Okta”Connect your Okta organization on IDP and SSO > Identity providers. The steps are the same as Connect Okta on the services page. The connection adds your Okta organization, such as https://example.okta.com, as an authorization server the platform can trust.
2. Trust Okta as an issuer
Section titled “2. Trust Okta as an issuer”A sign-in issuer accepts ID-JAGs when it is organization-level and trusts Okta. Project-level issuers never accept them.
- Open Manage Okta and go to Okta sign-in on the Setup tab.
- In Sign-in issuers, select Trust Okta sign-in on an existing organization-level issuer, or select Set up Okta sign-in to create the
okta-sign-inissuer. - Open Identity > Authorization Servers in the project sidebar, select the Okta authorization server under Organizational Authorization Servers, and under Issuer configuration select Refresh Discoverable Metadata. The platform checks ID-JAG signatures against the key URL this reads from Okta.
3. Use it on your MCP servers
Section titled “3. Use it on your MCP servers”On each MCP server agents should reach, open Settings > Sessions and set User session issuer to the issuer from step 2.
The server’s authorization server issuer is the URL Okta addresses ID-JAGs to. Read it from the issuer field of the server’s metadata:
curl https://<mcp-host>/.well-known/oauth-authorization-server/mcp/<server-slug>By default the issuer is the MCP server URL, https://<mcp-host>/mcp/<server-slug>, and the token endpoint is that URL plus /token. A custom domain or a separate authentication host changes both, so always copy them from the metadata.
4. Register the agent
Section titled “4. Register the agent”The agent needs a client at the server’s authorization server. A client registered once works for every server that uses the same sign-in issuer.
- Dynamic client registration. Register at the
registration_endpointfrom the metadata and includeurn:ietf:params:oauth:grant-type:jwt-beareringrant_types. Redirect URIs are needed only if the agent also requestsauthorization_code. - Client ID metadata document. Use the document’s URL as the
client_id, unless client admission turns those off.
The agent authenticates at the token endpoint with the method it registered: none, client_secret_basic, client_secret_post, or private_key_jwt.
5. Allow the agent in Okta
Section titled “5. Allow the agent in Okta”Okta issues an ID-JAG only for a resource the agent has a connection to.
- In Okta, open the app that represents Speakeasy, go to Machine Assignments > Callers > Cross-app access (XAA) > Edit, set it to Enabled, and enter the server’s authorization server issuer from step 3 as the Issuer URL.
- On the agent’s AI agent registration, add a resource connection to that app. Use the MCP server URL as the Resource and the agent’s client ID from step 4 as the Client ID.
- Assign the people the agent acts for.
What the agent sends
Section titled “What the agent sends”The agent first asks Okta for an ID-JAG with an OAuth token exchange:
curl https://example.okta.com/oauth2/v1/token \ -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \ -d requested_token_type=urn:ietf:params:oauth:token-type:id-jag \ -d subject_token_type=urn:ietf:params:oauth:token-type:id_token \ -d subject_token=<person's Okta ID token> \ -d audience=https://<mcp-host>/mcp/<server-slug> \ -d resource=https://<mcp-host>/mcp/<server-slug> \ <agent's client authentication at Okta>Then it presents the ID-JAG at the server’s token endpoint:
curl https://<mcp-host>/mcp/<server-slug>/token \ -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \ -d assertion=<ID-JAG> \ -d client_id=<agent's client ID> \ <agent's client authentication, if any>The platform accepts the ID-JAG only when all of these hold:
| Check | Must be |
|---|---|
typ header | oauth-id-jag+jwt |
iss | The Okta organization URL the sign-in issuer trusts, signed by its keys |
aud | Exactly the server’s authorization server issuer |
resource | The MCP server URL |
client_id | The client that authenticated at the token endpoint |
email | A member provisioned through directory sync, matching exactly one person |
exp, iat | Present, at most 10 minutes apart, with 1 minute of clock skew |
jti | Present and not used before. Each ID-JAG works once |
The person must also hold mcp:connect on the server. The access token lasts one hour and comes with no refresh token; the agent requests a new ID-JAG when it expires.
Troubleshooting
Section titled “Troubleshooting”Every refused ID-JAG returns invalid_grant with “assertion is invalid”, so check these in order:
| Symptom | Likely cause |
|---|---|
Metadata lists no authorization_grant_profiles_supported | The server’s user session issuer is project-level or doesn’t trust Okta |
| Every ID-JAG is refused right after setup | The Okta authorization server has no key URL yet. Select Refresh Discoverable Metadata |
| Refused for one server only | aud or resource names a different server, or the server uses another sign-in issuer |
| Refused for one person only | Their email isn’t on exactly one provisioned member, or they lack mcp:connect |
invalid_target | A resource parameter that isn’t the MCP server URL |
invalid_client | The agent’s client isn’t registered at this authorization server, or authenticated with another method |
Limitations
Section titled “Limitations”- Okta is the only supported issuer, and one sign-in issuer trusts one issuer.
- People are never created at sign-in. They must be provisioned first.
- ID-JAGs act for a person. For agents acting on their own, see Agent identity.