{"record":{"id":"d5389285b35c7e7a","repo":"different-ai/openwork","slug":"mcp-oauth-authorization-expired","errorCode":"MCP_OAUTH_AUTHORIZATION_EXPIRED","errorMessage":"The OAuth authorization transaction has expired; start the connection again.","messagePattern":"The OAuth authorization transaction has expired; start the connection again\\.","errorType":"exception","errorClass":"EnterpriseMcpOAuthContractError","httpStatus":null,"severity":"error","filePath":"packages/enterprise-mcp-client/src/oauth-provider.ts","lineNumber":467,"sourceCode":"      )\n    }\n    const transaction = await this.persistence.authorizations.load({\n      context: this.context(),\n      id: this.flow.authorizationId,\n    })\n    if (!transaction) {\n      throw new EnterpriseMcpOAuthContractError(\n        \"MCP_OAUTH_AUTHORIZATION_MISSING\",\n        \"The OAuth authorization transaction is missing or was already consumed.\",\n      )\n    }\n    if (transaction.handle.expiresAt <= this.clock.now() + this.expirationSkewMs) {\n      await this.persistence.authorizations.invalidate({\n        context: this.context(),\n        id: this.flow.authorizationId,\n        reason: \"expired\",\n      })\n      throw new EnterpriseMcpOAuthContractError(\n        \"MCP_OAUTH_AUTHORIZATION_EXPIRED\",\n        \"The OAuth authorization transaction has expired; start the connection again.\",\n      )\n    }\n    const clientRevision = this.loadedClient?.revision\n    if (\n      transaction.handle.clientRegistrationRevision !== undefined\n      && transaction.handle.clientRegistrationRevision !== clientRevision\n    ) {\n      throw new EnterpriseMcpOAuthContractError(\n        \"MCP_OAUTH_AUTHORIZATION_CLIENT_CHANGED\",\n        \"The OAuth client registration changed after authorization started.\",\n      )\n    }\n    this.authorizationHandle = transaction.handle\n    return transaction.codeVerifier\n  }\n","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-client/src/oauth-provider.ts#L449-L485","documentation":"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.","triggerScenarios":"codeVerifier() after the transaction TTL elapsed — e.g. user left the authorization page open and returned later; transaction.expiresAt <= clock.now() + expirationSkewMs.","commonSituations":"Long user delays at the IdP login/consent screens; very short authorizationTransactionTtlMs configuration; the callback arriving hours later (user bookmarked the redirect URL).","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."],"exampleFix":"// before\nnew EnterpriseMcpOAuthProvider({ authorizationTransactionTtlMs: 60_000 })\n// after — allow slow IdP logins\nnew EnterpriseMcpOAuthProvider({ authorizationTransactionTtlMs: 10 * 60_000 })","handlingStrategy":"try-catch","validationCode":"const tx = await persistence.authorizations.load({ context, id: authorizationId })\nconst expired = tx !== undefined && tx.handle.expiresAt <= Date.now()","typeGuard":null,"tryCatchPattern":"try { const verifier = await provider.codeVerifier() }\ncatch (e) {\n  if (e instanceof EnterpriseMcpOAuthContractError && e.code === \"MCP_OAUTH_AUTHORIZATION_EXPIRED\") {\n    return redirectToAuthorization() // silently restart the connect flow\n  }\n  throw e\n}","preventionTips":["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"],"tags":["oauth","pkce","expired","ttl"],"backgroundTag":"oauth-authorization-transaction-expired","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}