different-ai/openwork · error · EnterpriseMcpOAuthContractError
MCP_OAUTH_AUTHORIZATION_EXPIRED
MCP_OAUTH_AUTHORIZATION_EXPIRED
Error message
The OAuth authorization transaction has expired; start the connection again.
What it means
Thrown from codeVerifier when the loaded transaction's expiresAt is at or before now plus the expiration skew window. The PKCE transaction has a bounded TTL (authorizationTransactionTtlMs); if the user takes too long to complete authorization at the identity provider, the library invalidates the record and requires starting over. This prevents stale PKCE secrets from being exchanged.
Solutions
- Restart the connect flow to mint a fresh transaction.
- Increase authorizationTransactionTtlMs if your IdP login flow legitimately takes longer.
- Detect this error in the callback handler and automatically re-initiate the authorization redirect.
Example fix
// before
new EnterpriseMcpOAuthProvider({ authorizationTransactionTtlMs: 60_000 })
// after — allow slow IdP logins
new EnterpriseMcpOAuthProvider({ authorizationTransactionTtlMs: 10 * 60_000 }) Defensive patterns
Strategy: try-catch
Validate before calling
const tx = await persistence.authorizations.load({ context, id: authorizationId })
const expired = tx !== undefined && tx.handle.expiresAt <= Date.now() Try / catch
try { const verifier = await provider.codeVerifier() }
catch (e) {
if (e instanceof EnterpriseMcpOAuthContractError && e.code === "MCP_OAUTH_AUTHORIZATION_EXPIRED") {
return redirectToAuthorization() // silently restart the connect flow
}
throw e
} Prevention
- Set authorizationTransactionTtlMs comfortably above your IdP's worst-case login time
- Auto-restart the flow on expiry in the callback handler instead of showing an error
- Warn users before the transaction is about to expire during long consent flows
When it happens
Trigger: codeVerifier() after the transaction TTL elapsed — e.g. user left the authorization page open and returned later; transaction.expiresAt <= clock.now() + expirationSkewMs.
Common situations: Long user delays at the IdP login/consent screens; very short authorizationTransactionTtlMs configuration; the callback arriving hours later (user bookmarked the redirect URL).
Related errors
- MCP_OAUTH_AUTHORIZATION_MISSING
- A secure Linux password store is required for…
- is no longer available as your reconnectable account.
- An enterprise MCP OAuth redirect URI cannot contain…
- An enterprise MCP OAuth redirect URI must use HTTP or HTTPS.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/d5389285b35c7e7a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/enterprise-mcp-client/src/oauth-provider.ts:467
)
}
const transaction = await this.persistence.authorizations.load({
context: this.context(),
id: this.flow.authorizationId,
})
if (!transaction) {
throw new EnterpriseMcpOAuthContractError(
"MCP_OAUTH_AUTHORIZATION_MISSING",
"The OAuth authorization transaction is missing or was already consumed.",
)
}
if (transaction.handle.expiresAt <= this.clock.now() + this.expirationSkewMs) {
await this.persistence.authorizations.invalidate({
context: this.context(),
id: this.flow.authorizationId,
reason: "expired",
})
throw new EnterpriseMcpOAuthContractError(
"MCP_OAUTH_AUTHORIZATION_EXPIRED",
"The OAuth authorization transaction has expired; start the connection again.",
)
}
const clientRevision = this.loadedClient?.revision
if (
transaction.handle.clientRegistrationRevision !== undefined
&& transaction.handle.clientRegistrationRevision !== clientRevision
) {
throw new EnterpriseMcpOAuthContractError(
"MCP_OAUTH_AUTHORIZATION_CLIENT_CHANGED",
"The OAuth client registration changed after authorization started.",
)
}
this.authorizationHandle = transaction.handle
return transaction.codeVerifier
}
View on GitHub (pinned to 2b7df46e8a)