Skip to content
Status

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.

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.

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.

OktaAgent:Holds your Okta ID token
Startfrom your Okta sign-in
OktaAgent, automatic:Trade it for an ID-JAG
AutomaticOkta checks you are assigned and the agent may reach Speakeasy
SpeakeasyAgent, automatic:Present the ID-JAG to Speakeasy
AutomaticSpeakeasy checks Okta's signature and maps your email to a member
Agent:Call MCP tools as you

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.

StepWhereWhat
1. Connect OktaDashboard and OktaThe Okta connection, done once
2. Trust Okta as an issuerDashboardAn organization-level sign-in issuer that trusts Okta
3. Use it on your MCP serversDashboardEach server’s User session issuer
4. Register the agentAgentA client at the server’s authorization server
5. Allow the agent in OktaOktaSpeakeasy as a Cross App Access resource for the agent

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.

A sign-in issuer accepts ID-JAGs when it is organization-level and trusts Okta. Project-level issuers never accept them.

  1. Open Manage Okta and go to Okta sign-in on the Setup tab.
  2. 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-in issuer.
  3. 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.

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:

Terminal window
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.

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_endpoint from the metadata and include urn:ietf:params:oauth:grant-type:jwt-bearer in grant_types. Redirect URIs are needed only if the agent also requests authorization_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.

Okta issues an ID-JAG only for a resource the agent has a connection to.

  1. 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.
  2. 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.
  3. Assign the people the agent acts for.

The agent first asks Okta for an ID-JAG with an OAuth token exchange:

Terminal window
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:

Terminal window
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:

CheckMust be
typ headeroauth-id-jag+jwt
issThe Okta organization URL the sign-in issuer trusts, signed by its keys
audExactly the server’s authorization server issuer
resourceThe MCP server URL
client_idThe client that authenticated at the token endpoint
emailA member provisioned through directory sync, matching exactly one person
exp, iatPresent, at most 10 minutes apart, with 1 minute of clock skew
jtiPresent 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.

Every refused ID-JAG returns invalid_grant with “assertion is invalid”, so check these in order:

SymptomLikely cause
Metadata lists no authorization_grant_profiles_supportedThe server’s user session issuer is project-level or doesn’t trust Okta
Every ID-JAG is refused right after setupThe Okta authorization server has no key URL yet. Select Refresh Discoverable Metadata
Refused for one server onlyaud or resource names a different server, or the server uses another sign-in issuer
Refused for one person onlyTheir email isn’t on exactly one provisioned member, or they lack mcp:connect
invalid_targetA resource parameter that isn’t the MCP server URL
invalid_clientThe agent’s client isn’t registered at this authorization server, or authenticated with another method
  • 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.