@redocly/realm 0.139.0-next.1 → 0.139.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/dist/cli/eject/resolveEjectParams.js +1 -1
  3. package/dist/client/app/Sidebar/RequestAccessButton.js +2 -2
  4. package/dist/client/app/hooks/useL10nConfig.d.ts +1 -0
  5. package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
  6. package/dist/client/app/markdoc/custom-components/openapi/replay-openapi.js +1 -1
  7. package/dist/client/app/search/useSearch.js +1 -1
  8. package/dist/client/app/utils/programmatic-scroll.js +1 -1
  9. package/dist/client/app/webmcp/tools/api-tools.js +1 -1
  10. package/dist/client/app/webmcp/tools/site-tools.js +2 -2
  11. package/dist/client/app/webmcp/useWebMcpTools.js +1 -1
  12. package/dist/client/templates/api-docs/template.js +10 -8
  13. package/dist/constants/common.d.ts +1 -0
  14. package/dist/constants/common.js +1 -1
  15. package/dist/constants/l10n/langs/ar.js +1 -1
  16. package/dist/constants/l10n/langs/de.js +1 -1
  17. package/dist/constants/l10n/langs/en.js +1 -1
  18. package/dist/constants/l10n/langs/es.js +1 -1
  19. package/dist/constants/l10n/langs/fr.js +1 -1
  20. package/dist/constants/l10n/langs/hi.js +1 -1
  21. package/dist/constants/l10n/langs/it.js +1 -1
  22. package/dist/constants/l10n/langs/ja.js +1 -1
  23. package/dist/constants/l10n/langs/ko.js +1 -1
  24. package/dist/constants/l10n/langs/pl.js +1 -1
  25. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  26. package/dist/constants/l10n/langs/pt.js +1 -1
  27. package/dist/constants/l10n/langs/ru.js +1 -1
  28. package/dist/constants/l10n/langs/uk.js +1 -1
  29. package/dist/constants/l10n/langs/zh.js +1 -1
  30. package/dist/markdoc/helpers/get-inner-text.d.ts +2 -0
  31. package/dist/markdoc/helpers/get-inner-text.js +2 -2
  32. package/dist/markdoc/helpers/remove-markdoc-tags.js +1 -1
  33. package/dist/server/config/env-schemas/feature-flags.d.ts +1 -1
  34. package/dist/server/constants/entitlements.d.ts +2 -2
  35. package/dist/server/constants/entitlements.js +1 -1
  36. package/dist/server/entitlements/entitlements-provider.d.ts +2 -2
  37. package/dist/server/entitlements/is-valid-plan.d.ts +2 -2
  38. package/dist/server/entitlements/is-valid-plan.js +1 -1
  39. package/dist/server/fs/content-fs.js +1 -1
  40. package/dist/server/fs/utils/external-ref-resolver.js +2 -2
  41. package/dist/server/fs/utils/is-translation-fallback.d.ts +9 -0
  42. package/dist/server/fs/utils/is-translation-fallback.js +1 -0
  43. package/dist/server/node-bundle-entry.js +1 -1
  44. package/dist/server/plugins/api-docs/get-server-props.js +1 -1
  45. package/dist/server/plugins/api-docs/index.js +1 -1
  46. package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +67 -67
  47. package/dist/server/plugins/arazzo-docs/arazzo-doc-loader.js +2 -2
  48. package/dist/server/plugins/arazzo-docs/index.js +1 -1
  49. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
  50. package/dist/server/plugins/catalog-entities/template/index.js +1 -1
  51. package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
  52. package/dist/server/plugins/default-theme/resolve-logo.js +1 -1
  53. package/dist/server/plugins/dev-onboarding/api/routes/index.js +1 -1
  54. package/dist/server/plugins/dev-onboarding/api/routes/products.js +1 -1
  55. package/dist/server/plugins/dev-onboarding/template/App.js +4 -4
  56. package/dist/server/plugins/dev-onboarding/template/AppsList.js +7 -7
  57. package/dist/server/plugins/dev-onboarding/template/CreateAppDialog.js +3 -3
  58. package/dist/server/plugins/dev-onboarding/template/components/ApiKeyItem.js +4 -4
  59. package/dist/server/plugins/dev-onboarding/template/components/ApiLogDetails.js +7 -7
  60. package/dist/server/plugins/dev-onboarding/template/components/ApiLogs.js +5 -5
  61. package/dist/server/plugins/dev-onboarding/template/components/ApiLogsActions.js +6 -6
  62. package/dist/server/plugins/dev-onboarding/template/components/ApisList.js +1 -1
  63. package/dist/server/plugins/dev-onboarding/template/components/CreateApiKey.js +1 -1
  64. package/dist/server/plugins/dev-onboarding/template/components/DeleteAppDialog.js +1 -1
  65. package/dist/server/plugins/dev-onboarding/template/components/EditApis.js +1 -1
  66. package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
  67. package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
  68. package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
  69. package/dist/server/plugins/dev-onboarding/template/components/RevokeAPIKey.js +5 -5
  70. package/dist/server/plugins/dev-onboarding/template/components/RollAPIKey.js +5 -5
  71. package/dist/server/plugins/dev-onboarding/template/components/UpdateApiKey.js +1 -1
  72. package/dist/server/plugins/l10n/index.js +1 -1
  73. package/dist/server/plugins/markdown/attribute-resolvers/resolve-json-example-ref.js +1 -1
  74. package/dist/server/plugins/markdown/attribute-resolvers/resolve-link.js +1 -1
  75. package/dist/server/plugins/markdown/get-server-props.js +1 -1
  76. package/dist/server/plugins/markdown/index.js +1 -1
  77. package/dist/server/plugins/markdown/markdoc/plugins/utils.js +1 -1
  78. package/dist/server/plugins/markdown/markdoc/resolve-raw-partials.js +1 -1
  79. package/dist/server/plugins/markdown/markdown-static-data-loader.js +1 -1
  80. package/dist/server/plugins/markdown/read-file-content.js +1 -1
  81. package/dist/server/plugins/markdown/search/search-resolver.js +1 -1
  82. package/dist/server/plugins/mcp/codemode/prompts.d.ts +0 -1
  83. package/dist/server/plugins/mcp/codemode/prompts.js +3 -3
  84. package/dist/server/plugins/mcp/constants.d.ts +0 -2
  85. package/dist/server/plugins/mcp/constants.js +1 -1
  86. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  87. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.d.ts +0 -2
  88. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.js +1 -1
  89. package/dist/server/plugins/mcp/gateway-mcp/allowed-http-methods.d.ts +0 -5
  90. package/dist/server/plugins/mcp/gateway-mcp/allowed-http-methods.js +1 -1
  91. package/dist/server/plugins/mcp/gateway-mcp/caller-api-calls.d.ts +3 -4
  92. package/dist/server/plugins/mcp/gateway-mcp/caller-api-calls.js +1 -1
  93. package/dist/server/plugins/mcp/gateway-mcp/eligibility.d.ts +1 -12
  94. package/dist/server/plugins/mcp/gateway-mcp/eligibility.js +1 -1
  95. package/dist/server/plugins/mcp/handlers/errors.d.ts +0 -2
  96. package/dist/server/plugins/mcp/handlers/errors.js +1 -1
  97. package/dist/server/plugins/mcp/utils.d.ts +0 -8
  98. package/dist/server/plugins/mcp/utils.js +1 -1
  99. package/dist/server/plugins/nav-utils.js +1 -1
  100. package/dist/server/plugins/pages/index.js +1 -1
  101. package/dist/server/plugins/register-page-props.d.ts +1 -1
  102. package/dist/server/plugins/register-page-props.js +1 -1
  103. package/dist/server/plugins/sidebars/index.js +1 -1
  104. package/dist/server/plugins/sidebars/utils.js +1 -1
  105. package/dist/server/types/plugins/markdown.d.ts +1 -0
  106. package/dist/server/utils/envs/load-env-variables.js +1 -1
  107. package/dist/server/utils/rbac-expand.d.ts +4 -0
  108. package/dist/server/utils/rbac-expand.js +1 -1
  109. package/dist/server/utils/rbac.js +1 -1
  110. package/dist/server/utils/resolve-asset-path.js +1 -1
  111. package/dist/server/web-server/auth.d.ts +2 -0
  112. package/dist/server/web-server/auth.js +3 -3
  113. package/dist/server/web-server/jwt/jwt.js +1 -1
  114. package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
  115. package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
  116. package/dist/types/entitlements.d.ts +2 -1
  117. package/dist/types/global-data.d.ts +1 -0
  118. package/dist/utils/url/api-keys-url.d.ts +11 -0
  119. package/dist/utils/url/api-keys-url.js +1 -0
  120. package/package.json +7 -7
  121. package/dist/server/plugins/mcp/docs-mcp/tools/core/utils.d.ts +0 -3
  122. package/dist/server/plugins/mcp/docs-mcp/tools/core/utils.js +0 -11
@@ -1 +1 @@
1
- import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as x,REDOCLY_ROUTE_RBAC as E}from"@redocly/config";import{MCP_ROUTE as U,PUBLIC_MCP_ROUTE as k}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as D,PUBLIC_RBAC_SCOPE_ITEM as y,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as L,GRAPHQL_SPEC_SLUG as M}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as C,PUBLIC_ASSETS_FOLDER as B}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as b}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as W}from"../constants/rbac.js";import{removeTrailingSlash as T}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as $}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as v}from"../../utils/path/parse-path-versions.js";import{reporter as Y}from"../tools/notifiers/reporter.js";import{bold as G}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as H}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as z}from"../../utils/guards/is-truthy.js";import{canExpandConfig as K,expandRbacConfig as V,getTeamFolderDefaults as J,parseTeamFoldersTemplate as Q,parseTeamNameTemplate as X}from"./rbac-expand.js";import{getUserParamsFromCookies as Z}from"../web-server/auth.js";import{getDeeperGlobPattern as q}from"./globs.js";import{EntitlementsProvider as tt}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],et=new Set(["x-parsed-md-description","x-parsed-md-summary"]),rt=new Set(Object.values(w).map(t=>T(t.split(":")[0])));function It(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const A={};function I(t,e){if(!t?.content)return y;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return y;const s=a=>{const d=`slug:${a}`,p=A[d]??P(a);A[d]=p;const _=`fsPath:${a}`,S=A[_]??P($(a));return A[_]=S,!!(n&&p(n))||!!(o&&S(o))};if(st(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[L]||y;const f=Object.keys(r).filter(a=>s(a));if(f.length==0)return y;const u=f.map(a=>P.scan(a,{tokens:!0,parts:!0}));let h=u[0];for(let a=1;a<u.length;a++)h=q(h,u[a]);return r[h.input]}function F(t,e,r){const n=r?[...e,r]:e,o=[];for(const s of n)t[s]?o.push(t[s]):t[l]&&s!==r&&o.push(t[l]);return o.some(s=>s.toLowerCase()!=="none")}function Ft(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?F(o,e.teams):!0}function Nt(t,e){return O(t,W,e.access?.rbac||{},e.access?.requiresLogin||!1)}function O(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&rt.has(T(t.slug))||(t.slug===U||t.slug===k||t.slug===b)&&tt.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=V(r,e.teams||[]),s=t[x]||I(o,t[E]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:F(s,e?.teams||[],e?.email)}function Ut(t,e,r,n){if(!t.startsWith(C)&&!t.startsWith(g))return!0;const o=t.replace(new RegExp(`^${C}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${M}/`),""),c=o==="."?"":o,i={[E]:{slug:t,fsPath:c},slug:t};return O(i,n,e,r)}function kt(t,e,r,n,o){if(!t.startsWith(B))return!0;const s=t.match(/\..{64}\.([A-Fa-f0-9]{8})\.[^.]+$/)?.[1];if(!s)return!0;const c=n[s];if(!c)return!0;const{base:i,ext:f}=R.parse(t),u=i.split(".")[0],h=f.split(".").join(""),d=c==="."?"":c,p={[E]:{slug:t,fsPath:R.posix.join(d,`${u}.${h}`)},slug:t};return O(p,o,e,r)}async function wt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await Z(t,e),{teams:i=[]}=c;let f;return r?f=i.filter(u=>u!==D):f=[D],{isAuthenticated:r,idpAccessToken:n,teams:f,claims:c}}function N(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=N(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!O(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===x||c===E)continue;if(et.has(c)){s[c]=t[c];continue}const i=N(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(i)&&i.length===0&&t[c].length!==0){o=!0;continue}i!==void 0&&(s[c]=i),c==="paths"&&nt(i)&&ot(i)}return o?void 0:s}return t}function Mt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Bt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===L?[[s,c]]:[[s,c]].concat(t.localeFolders.map(i=>[s.startsWith("/")?`/${i.toLocaleLowerCase()}${s}`:R.posix.join(t.localizationFolder,i,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function bt(t,e){if(!e)return{};const r={},n=new Set((await t.scan()).flatMap(({relativePath:o})=>{const{versionFolderPath:s}=v(o)||{},c=R.dirname(o);return s?[s,c]:c}));for(const o of n)r[H(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function nt(t){return m(t)&&Object.keys(t).length>0}function ot(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function st(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const ct=t=>{if(t&&m(t)&&("content"in t&&m(t.content)||"reunite"in t&&m(t.reunite)||"features"in t&&m(t.features)||t.teamFolders&&t.teamNamePatterns)){const e=Object.values(t.content||{});if(e.length===0)return!0;if(e.every(m))return e.every(r=>Object.values(r).every(n=>typeof n=="string"))}return!1},Wt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(ct(t))return it(t);await Y.panicOnContentError(`You are using an incorrect format of ${G("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},it=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=T(n);r[o]=e.content[n]}e.content=r}return e};function $t(t,e){const r=t.fsPath,n=t.slug,o=[],s=K(e)&&(r||n)?Q(e,[r,n].filter(z)):null;if(s){const i=e?.teamNamePatterns?.map(f=>f.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...i)}const c=I(s?{content:{...J(e),...e.content}}:e,t);return o.push(...at(c)),ft(e,o)}function at(t){if(!t)return[];const e=[],r=l in t?{authenticated:t[l],anonymous:t[l]}:{};for(const[n,o]of Object.entries({...r,...t}))o.toLowerCase()!=="none"&&n!==l&&e.push(n);return e}function ft(t,e){return e.map(n=>X(t,n)??{teamName:n}).map(n=>n.projectRole&&n.projectRole!=="READ"?n.teamName?.toLowerCase().replace(n.projectRole?.toLowerCase?.()??"","read")??"":n.teamName?.toLowerCase()??"")}export{j as PROJECT_ROLES_ORDERED_BY_ACCESS_LEVEL,Bt as applyL10nToRbacConfig,kt as canAccessAsset,Ft as canAccessFeature,O as canAccessResource,Ut as canDownloadApiDefinition,ft as expandTeamsForRead,at as extractTeamsFromScopeItems,N as filterDataByAccessDeep,wt as getAuthDetailsFromCookies,It as getHigherRole,$t as getRbacTeamsListForResource,I as getScopeItemsForResource,F as hasScopeAccess,ct as isRbacConfigValid,Nt as isResourcePubliclyAccessible,it as normalizeRbacConfig,Wt as parseRbacConfig,Mt as parseTeamClaimToArray,bt as resolveDirectoryHashes};
1
+ import R from"path";import P from"picomatch";import"../node-crypto-polyfill.js";import{REDOCLY_TEAMS_RBAC as x,REDOCLY_ROUTE_RBAC as E}from"@redocly/config";import{MCP_ROUTE as U,PUBLIC_MCP_ROUTE as k}from"@redocly/theme/core/constants/mcp";import{ServerRoutes as w}from"../../constants/api.js";import{DEFAULT_ANONYMOUS_VISITOR_TEAM as D,PUBLIC_RBAC_SCOPE_ITEM as y,RBAC_ALL_OTHER_TEAMS as l,DEFAULT_RBAC_SCOPE as L,GRAPHQL_SPEC_SLUG as M}from"../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as g,PUBLIC_API_DEFINITIONS_FOLDER as C,PUBLIC_ASSETS_FOLDER as B}from"../constants/common.js";import{MCP_CREDENTIALS_ROUTE as b}from"../constants/mcp.js";import{ANONYMOUS_AUTH_DETAILS as W}from"../constants/rbac.js";import{removeTrailingSlash as T}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as $}from"../../utils/url/remove-leading-slash.js";import{parsePathVersions as v}from"../../utils/path/parse-path-versions.js";import{reporter as Y}from"../tools/notifiers/reporter.js";import{bold as G}from"../tools/notifiers/helpers/colors.js";import{shaDirPathShort as H}from"../utils/crypto/sha-dir-path-short.js";import{isTruthy as z}from"../../utils/guards/is-truthy.js";import{canExpandConfig as K,expandRbacConfig as V,getTeamFolderDefaults as J,parseTeamFoldersTemplate as Q,parseTeamNameTemplate as X}from"./rbac-expand.js";import{getUserParamsFromCookies as Z}from"../web-server/auth.js";import{getDeeperGlobPattern as q}from"./globs.js";import{EntitlementsProvider as tt}from"../entitlements/entitlements-provider.js";const j=["NONE","READ","TRIAGE","WRITE","MAINTAIN","ADMIN"],et=new Set(["x-parsed-md-description","x-parsed-md-summary"]),rt=new Set(Object.values(w).map(t=>T(t.split(":")[0])));function It(t,e){const r=j.indexOf(t.toUpperCase()),n=j.indexOf(e.toUpperCase());return r>n?t:e}const A={};function I(t,e){if(!t?.content)return y;const r=t.content,{slug:n,fsPath:o}=e;if(!n&&!o)return y;const s=a=>{const d=`slug:${a}`,p=A[d]??P(a);A[d]=p;const _=`fsPath:${a}`,S=A[_]??P($(a));return A[_]=S,!!(n&&p(n))||!!(o&&S(o))};if(st(n||o||"")&&Object.keys(r).filter(p=>s(p)).length===0)return r[L]||y;const f=Object.keys(r).filter(a=>s(a));if(f.length==0)return y;const u=f.map(a=>P.scan(a,{tokens:!0,parts:!0}));let h=u[0];for(let a=1;a<u.length;a++)h=q(h,u[a]);return r[h.input]}function F(t,e,r){const n=r?[...e,r]:e,o=[];for(const s of n)t[s]?o.push(t[s]):t[l]&&s!==r&&o.push(t[l]);return o.some(s=>s.toLowerCase()!=="none")}function Ft(t,e,r={},n=!1){if(n&&Object.keys(r).length===0)return e.isAuthenticated;const o=r.features?.[t];return o?F(o,e.teams):!0}function Nt(t,e){return O(t,W,e.access?.rbac||{},e.access?.requiresLogin||!1)}function O(t,e={},r={},n=!1){if(t.slug&&typeof t.slug=="string"&&rt.has(T(t.slug))||(t.slug===U||t.slug===k||t.slug===b)&&tt.instance().canAccessFeature("mcp"))return!0;if(n&&Object.keys(r).length===0)return!!e.isAuthenticated;const o=V(r,e.teams||[]),s=t[x]||I(o,t[E]||{});return Object.keys(s||{}).length===0?!1:Object.keys(s).length===1&&s[l]&&s[l].toLowerCase()!=="none"?!0:F(s,e?.teams||[],e?.email)}function Ut(t,e,r,n){if(!t.startsWith(C)&&!t.startsWith(g))return!0;const o=t.replace(new RegExp(`^${C}/`),"").replace(new RegExp(`^${g}/`),"").replace(new RegExp(`^${M}/`),""),c=o==="."?"":o,i={[E]:{slug:t,fsPath:c},slug:t};return O(i,n,e,r)}function kt(t,e,r,n,o){if(!t.startsWith(B))return!0;const s=t.match(/\..{64}\.([A-Fa-f0-9]{8})\.[^.]+$/)?.[1];if(!s)return!0;const c=n[s];if(!c)return!0;const{base:i,ext:f}=R.parse(t),u=i.split(".")[0],h=f.split(".").join(""),d=c==="."?"":c,p={[E]:{slug:t,fsPath:R.posix.join(d,`${u}.${h}`)},slug:t};return O(p,o,e,r)}async function wt(t,e){const{isAuthenticated:r=!1,idpAccessToken:n,federatedAccessToken:o,federatedIdToken:s,...c}=await Z(t,e),{teams:i=[]}=c;let f;return r?f=i.filter(u=>u!==D):f=[D],{isAuthenticated:r,idpAccessToken:n,teams:f,claims:c}}function N(t,e,r={},n=!1){if(!t)return t;if(Array.isArray(t)){const o=[];for(const s of t){const c=N(s,e,r,n);c!==void 0&&o.push(c)}return o}if(typeof t=="object"){if(!O(t,e,r,n))return;let o=!1;const s={};for(const c in t){if(c===x||c===E)continue;if(et.has(c)){s[c]=t[c];continue}const i=N(t[c],e,r,n);if(!t.link&&(c==="items"||c==="menu"&&t.type==="tab")&&Array.isArray(i)&&i.length===0&&t[c].length!==0){o=!0;continue}i!==void 0&&(s[c]=i),c==="paths"&&nt(i)&&ot(i)}return o?void 0:s}return t}function Mt(t){return typeof t=="string"?t.split(" ").filter(Boolean):Array.isArray(t)?t.map(e=>e.toString()):[]}function Bt(t,e){if(!e)return;const r=e.content;if(!r)return e;const n=Object.entries(r).flatMap(([s,c])=>s===L?[[s,c]]:[[s,c]].concat(t.localeFolders.map(i=>[s.startsWith("/")?`/${i.toLocaleLowerCase()}${s}`:R.posix.join(t.localizationFolder,i,s),c]))),o=Object.fromEntries(n);return{...e,content:o}}async function bt(t,e){if(!e)return{};const r={},n=new Set(t.scan().flatMap(({relativePath:o})=>{const{versionFolderPath:s}=v(o)||{},c=R.dirname(o);return s?[s,c]:c}));for(const o of n)r[H(o)]=o;return r}const m=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function nt(t){return m(t)&&Object.keys(t).length>0}function ot(t){for(const e of Object.keys(t)){const r=t[e];m(r)&&Object.keys(r).length===0&&delete t[e]}}function st(t){return t?t.split("/").filter(Boolean).some(r=>r.startsWith(".")):!1}const ct=t=>{if(t&&m(t)&&("content"in t&&m(t.content)||"reunite"in t&&m(t.reunite)||"features"in t&&m(t.features)||t.teamFolders&&t.teamNamePatterns)){const e=Object.values(t.content||{});if(e.length===0)return!0;if(e.every(m))return e.every(r=>Object.values(r).every(n=>typeof n=="string"))}return!1},Wt=async t=>{if(t){if(Object.keys(t).length===0)return{};if(ct(t))return it(t);await Y.panicOnContentError(`You are using an incorrect format of ${G("rbac:")} configuration. See: https://redocly.com/docs/realm/access`)}},it=t=>{const e={...t};if(e.content){const r={};for(const n in e.content)if(e.content[n]!==void 0){const o=T(n);r[o]=e.content[n]}e.content=r}return e};function $t(t,e){const r=t.fsPath,n=t.slug,o=[],s=K(e)&&(r||n)?Q(e,[r,n].filter(z)):null;if(s){const i=e?.teamNamePatterns?.map(f=>f.replace("{teamPathSegment}",s.teamPathSegment).replace("{projectRole}","read"))??[];o.push(...i)}const c=I(s?{content:{...J(e),...e.content}}:e,t);return o.push(...at(c)),ft(e,o)}function at(t){if(!t)return[];const e=[],r=l in t?{authenticated:t[l],anonymous:t[l]}:{};for(const[n,o]of Object.entries({...r,...t}))o.toLowerCase()!=="none"&&n!==l&&e.push(n);return e}function ft(t,e){return e.map(n=>X(t,n)??{teamName:n}).map(n=>n.projectRole&&n.projectRole!=="READ"?n.teamName?.toLowerCase().replace(n.projectRole?.toLowerCase?.()??"","read")??"":n.teamName?.toLowerCase()??"")}export{j as PROJECT_ROLES_ORDERED_BY_ACCESS_LEVEL,Bt as applyL10nToRbacConfig,kt as canAccessAsset,Ft as canAccessFeature,O as canAccessResource,Ut as canDownloadApiDefinition,ft as expandTeamsForRead,at as extractTeamsFromScopeItems,N as filterDataByAccessDeep,wt as getAuthDetailsFromCookies,It as getHigherRole,$t as getRbacTeamsListForResource,I as getScopeItemsForResource,F as hasScopeAccess,ct as isRbacConfigValid,Nt as isResourcePubliclyAccessible,it as normalizeRbacConfig,Wt as parseRbacConfig,Mt as parseTeamClaimToArray,bt as resolveDirectoryHashes};
@@ -1 +1 @@
1
- import t from"node:path";import{withPathPrefix as f}from"@redocly/theme/core/utils";import{PUBLIC_STATIC_FOLDER as e}from"../constants/common.js";import{isLocalLink as p}from"../../utils/path/is-local-link.js";import{copyStaticFile as u,FileNotFoundError as x}from"./fs.js";async function h(r,o,i){if(!p(r))return r;const n=r.startsWith("/")?t.posix.join(e,r):void 0;if(n&&await o.exists(n))return f(r);const c=r.startsWith("/")?r.slice(1):t.posix.join(t.dirname(i.fromFileRelativePath),r),m=await o.exists(c)?await o.getFileInfo(c):null;if(!m)throw new x(`Cannot resolve asset path: ${r}`,r);return u(i.contentDir,m.realRelativePath,i.outdir)}export{h as resolveAssetPath};
1
+ import n from"node:path";import{withPathPrefix as f}from"@redocly/theme/core/utils";import{PUBLIC_STATIC_FOLDER as e}from"../constants/common.js";import{isLocalLink as p}from"../../utils/path/is-local-link.js";import{copyStaticFile as u,FileNotFoundError as x}from"./fs.js";async function w(r,o,i){if(!p(r))return r;const t=r.startsWith("/")?n.posix.join(e,r):void 0;if(t&&o.exists(t))return f(r);const c=r.startsWith("/")?r.slice(1):n.posix.join(n.dirname(i.fromFileRelativePath),r),m=o.exists(c)?o.getFileInfo(c):null;if(!m)throw new x(`Cannot resolve asset path: ${r}`,r);return u(i.contentDir,m.realRelativePath,i.outdir)}export{w as resolveAssetPath};
@@ -155,7 +155,9 @@ export declare const oidcJwksCache: {
155
155
  jwks: Record<string, (OidcJwk & {
156
156
  idpIds: string[];
157
157
  }) | null>;
158
+ missedAt: Record<string, number>;
158
159
  };
160
+ export declare const UNKNOWN_KID_RETRY_MS = 60000;
159
161
  export declare function getOidcMetadata(idpId: string, providerConfig: OidcProviderConfig): Promise<OidcIssuerMetadata>;
160
162
  export declare function discoverJwks(ssoConfig: SsoConfig): Promise<void>;
161
163
  export declare function getRedoclyTokenPayload(token: string): Promise<Record<string, unknown>>;
@@ -1,12 +1,12 @@
1
- import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as de}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as le}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as me,withPathPrefix as pe,withoutPathPrefix as fe}from"@redocly/theme/core/utils";import{ServerRoutes as V}from"../../constants/api.js";import{AuthCookieNames as I,DEFAULT_AUTHENTICATED_TEAM as he,REQUIRED_OIDC_SCOPES as B}from"../../constants/common.js";import{appendQueryParams as ye}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as $}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as Ae}from"../utils/crypto/random-string.js";import{randomUUID as M}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as d,JwtTokenExpired as we}from"./jwt/types.js";import*as m from"./jwt/jwt.js";import{InternalTokenTypes as T}from"./jwt/token-types.js";import{parseTeamClaimToArray as _e}from"../utils/index.js";import{parseTeamNameTemplate as Se}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Te,decodeBase64 as F,encodeBase64URL as xe,urlSafeBase64 as H}from"./jwt/encode.js";import{formatSamlCertificate as ge}from"./utils/format-saml-certificate.js";const Ie=new Set(Object.values(T));function x(e){return e?.type===f.OIDC}function Ee(e){return e?.type===f.SAML2}async function Rt(e,t){if(x(t))return ke(e,t);if(Ee(t))return Re(e,t)}async function ke(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(B)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Re(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function K(e){return Array.from(new Set((e.scopes||[]).concat(B))).join(" ")}async function z(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function Ot(e,t,n,r,o={}){return await z(e,r,{scope:K(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function Mt(e,t,n){return await z(e,n,{scope:K(n),grant_type:"refresh_token",refresh_token:t})}function Oe(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},u,h,p){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),w=p?.redirectUriOverride??`${e}${pe(V.OIDC_CALLBACK)}`,_={state:M(),idpId:i,redirectUri:w,redirectTo:u,branch:p?.branchOverride??Ce(e),inviteCode:h,source:p?.sourceOverride??"portal",uiLocales:p?.uiLocales},S={};if(c){const A=H(Ae(50)),E=H(re("sha256").update(A).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),S.code_verifier={value:A,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:me()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(w)),a.searchParams.append("state",xe(JSON.stringify(_))),p?.uiLocales&&a.searchParams.append("ui_locales",p.uiLocales);for(const A in s)s[A]!==void 0&&a.searchParams.append(A,s[A]);return{loginUrl:a.toString(),cookies:S}}function Lt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Me(e,t){const n=R(t?.audience??[]);return!n.length||W(e,n)}function W(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Le(e,t){return!t||!x(t)?!0:Me(e,t)&&Ne(e,t)}function Ne(e,t){return!x(t)||!We(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function Nt(e){if(!e)return!1;try{return m.decode(e).header.alg===d.RS256}catch{return!1}}async function Ct(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return m.sign({type:T.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token_enc:le(e.idToken,y),...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},y,d.HS256)}async function Dt(e){await m.verify(e,y,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");if(typeof t.id_token_enc!="string")throw new Error("Authorization code missing session");return{...t,id_token:de(t.id_token_enc,y)}}async function bt(e){const t=Math.floor(Date.now()/1e3);return m.sign({type:T.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp,jti:M()},y,d.HS256)}async function Pt(e){await m.verify(e,y,d.HS256);const{payload:t}=m.decode(e);if(t.type!==T.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function vt(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=$(e);if(!t)return e;const n=De(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,q(e,t);const r=Y(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,q(e,t)):e}function Ce(e){const t=$(e);if(t)return Y(t.hostname)?.previewBranch}function q(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${fe(n)}`}function Y(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function De(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function be(e){return e.type===f.OIDC}function Pe(e){return e.type===f.SAML2}function jt(e,t,n,r,o){return be(e)?Oe(t,e,n,r,{uiLocales:o}):Pe(e)?ve(t,e,n,r,o):{}}function ve(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
1
+ import"../node-crypto-polyfill.js";import{DOMParser as j}from"@xmldom/xmldom";import{SignedXml as Z}from"xml-crypto";import ee from"xpath";import{deflateSync as te,inflateSync as ne}from"fflate";import{createHash as re}from"crypto";import{ulid as oe}from"ulid";import{AuthProviderType as f,DEFAULT_TEAM_CLAIM_NAME as U}from"@redocly/config";import{ALTERNATIVE_AUD_CLAIM_NAME as se,AUTH_URL as ie,JWT_SECRET_KEY as y,MAX_AUTH_TOKEN_COOKIE_BYTES as ae}from"../constants/common.js";import{ensureArray as R}from"../../utils/array/ensure-array.js";import{envConfig as ce}from"../config/env-config.js";import{decryptClaimValue as ue}from"../utils/crypto/decrypt-claim-value.js";import{decryptAesGcm as de}from"../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as le}from"../utils/crypto/encrypt-aes-gcm.js";import{getPathPrefix as me,withPathPrefix as pe,withoutPathPrefix as fe}from"@redocly/theme/core/utils";import{ServerRoutes as K}from"../../constants/api.js";import{AuthCookieNames as I,DEFAULT_AUTHENTICATED_TEAM as he,REQUIRED_OIDC_SCOPES as V}from"../../constants/common.js";import{appendQueryParams as ye}from"../../utils/url/append-query-params.js";import{parsePreviewUrl as B}from"../../utils/url/parse-preview-url.js";import{logger as O}from"../tools/notifiers/logger.js";import{randomString as Ae}from"../utils/crypto/random-string.js";import{randomUUID as M}from"../utils/crypto/random-uuid.js";import{AlgorithmTypes as l,JwtTokenExpired as _e}from"./jwt/types.js";import*as p from"./jwt/jwt.js";import{InternalTokenTypes as x}from"./jwt/token-types.js";import{parseTeamClaimToArray as we}from"../utils/index.js";import{parseTeamNameTemplate as Se}from"../utils/rbac-expand.js";import{arrayBufferToBase64 as Te,decodeBase64 as $,encodeBase64URL as xe,urlSafeBase64 as F}from"./jwt/encode.js";import{formatSamlCertificate as ge}from"./utils/format-saml-certificate.js";const Ie=new Set(Object.values(x));function g(e){return e?.type===f.OIDC}function Ee(e){return e?.type===f.SAML2}async function Mt(e,t){if(g(t))return ke(e,t);if(Ee(t))return Re(e,t)}async function ke(e,t){const n=await G(e,t),r=new Set((t.scopes||[]).concat(V)),o=t.authorizationRequestCustomParams||{};return{type:f.OIDC,idpId:e,name:"OAuth provider",authorizationEndpoint:n.authorization_endpoint,clientId:t.clientId,responseType:"code",scope:Array.from(r).join(" "),extraParams:o,pkce:t.pkce}}function Re(e,t){return{type:f.SAML2,idpId:e,name:"SAML2 provider",ssoUrl:t.ssoUrl,issuerId:t.issuerId,entityId:t.entityId||t.issuerId}}function H(e){return Array.from(new Set((e.scopes||[]).concat(V))).join(" ")}async function W(e,t,n){return await fetch(e,{method:"POST",body:new URLSearchParams({client_id:t.clientId,...t.clientSecret?{client_secret:t.clientSecret}:{},...n}).toString(),headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"}}).then(r=>r.json())}async function Lt(e,t,n,r,o={}){return await W(e,r,{scope:H(r),code:t,redirect_uri:J(n),grant_type:"authorization_code",...o})}async function Nt(e,t,n){return await W(e,n,{scope:H(n),grant_type:"refresh_token",refresh_token:t})}function Oe(e,{authorizationEndpoint:t,clientId:n,responseType:r,scope:o,extraParams:s,idpId:i,pkce:c},d,h,u){if(!t||!n||!r||!o)return{loginUrl:void 0};const a=new URL(t),_=u?.redirectUriOverride??`${e}${pe(K.OIDC_CALLBACK)}`,S={state:M(),idpId:i,redirectUri:_,redirectTo:d,branch:u?.branchOverride??De(e),inviteCode:h,source:u?.sourceOverride??"portal",uiLocales:u?.uiLocales},T={};if(c){const A=F(Ae(50)),E=F(re("sha256").update(A).digest("base64")),k="S256";a.searchParams.append("code_challenge",E),a.searchParams.append("code_challenge_method",k),T.code_verifier={value:A,options:{secure:!0,httpOnly:!0,expires:new Date(Date.now()+1e3*60*10),path:me()||"/"}}}a.searchParams.append("client_id",n),a.searchParams.append("scope",o),a.searchParams.append("response_type",r),a.searchParams.append("redirect_uri",J(_)),a.searchParams.append("state",xe(JSON.stringify(S))),u?.uiLocales&&a.searchParams.append("ui_locales",u.uiLocales);for(const A in s)s[A]!==void 0&&a.searchParams.append(A,s[A]);return{loginUrl:a.toString(),cookies:T}}function Dt(e,t,n,r){const o=new URL(e);return n&&(o.searchParams.append("post_logout_redirect_uri",t),r&&o.searchParams.append("state",r),o.searchParams.append("id_token_hint",n)),o.toString()}function Me(e,t){const n=R(t?.audience??[]);return!n.length||z(e,n)}function z(e,t){const n=[e.aud,e[se]].flat();return t.some(r=>r!==""&&n.includes(r))}function Le(e,t){return!t||!g(t)?!0:Me(e,t)&&Ne(e,t)}function Ne(e,t){return!g(t)||!Ye(t)||!t.clientId?!0:R(e.aud||[]).includes(t.clientId)}function Ct(e){if(!e)return!1;try{return p.decode(e).header.alg===l.RS256}catch{return!1}}async function bt(e){const t=Math.floor(Date.now()/1e3),n=t+(e.ttlSec??600);return p.sign({type:x.MCP_AUTHORIZATION_CODE,client_id:e.clientId,redirect_uri:e.redirectUri,id_token_enc:le(e.idToken,y),...e.idpId?{idp_id:e.idpId}:{},...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},...e.codeChallenge?{code_challenge:e.codeChallenge}:{},...e.codeChallengeMethod?{code_challenge_method:e.codeChallengeMethod}:{},iat:t,exp:n},y,l.HS256)}async function Pt(e){await p.verify(e,y,l.HS256);const{payload:t}=p.decode(e);if(t.type!==x.MCP_AUTHORIZATION_CODE)throw new Error("Invalid authorization code type");if(!t.client_id||!t.redirect_uri)throw new Error("Authorization code missing required claims");if(typeof t.exp=="number"&&Date.now()>=t.exp*1e3)throw new Error("Authorization code expired");if(typeof t.id_token_enc!="string")throw new Error("Authorization code missing session");return{...t,id_token:de(t.id_token_enc,y)}}async function vt(e){const t=Math.floor(Date.now()/1e3);return p.sign({type:x.MCP_REFRESH_TOKEN,client_id:e.clientId,...e.idpId?{idp_id:e.idpId}:{},claims:e.claims,...e.idpAccessTokenEnc?{idp_access_token_enc:e.idpAccessTokenEnc}:{},...e.idpRefreshTokenEnc?{idp_refresh_token_enc:e.idpRefreshTokenEnc}:{},session_exp:e.sessionExp,iat:t,exp:e.sessionExp,jti:M()},y,l.HS256)}async function jt(e){await p.verify(e,y,l.HS256);const{payload:t}=p.decode(e);if(t.type!==x.MCP_REFRESH_TOKEN)throw new Error("Invalid refresh token type");if(!t.client_id||typeof t.claims!="object"||t.claims===null)throw new Error("Refresh token missing required claims");if(typeof t.session_exp!="number")throw new Error("Refresh token missing session expiry");return t}function Ut(e){const t=e||oe(),n=t.startsWith("mcp_")?t:`mcp_${t}`;return{id:n,object:"mcpSession",uri:`urn:redocly:realm:mcp:session:${n}`}}function J(e){const t=B(e);if(!t)return e;const n=Ce(t.hostname);if(n)return t.hostname=`previewauth--${n.previewBranch}${n.after}`,Y(e,t);const r=q(t.hostname);return r?(t.hostname=`${r.projectSlug}.previewauth.${r.after}`,Y(e,t)):e}function De(e){const t=B(e);if(t)return q(t.hostname)?.previewBranch}function Y(e,t){const n=e.replace(/^https?:\/\/[^/?#]+/i,"");return`${t.protocol}//${t.host}${fe(n)}`}function q(e){const t=e.split(".preview.",2);if(t.length<2)return null;const[n,r]=t,o=n.indexOf("--");if(o===-1)return null;const s=n.slice(0,o),i=n.slice(o+2);return!s||!i||i.includes(".")?null:{projectSlug:s,previewBranch:i,after:r}}function Ce(e){const t=e.indexOf("."),n=t===-1?e:e.slice(0,t);if(!n.startsWith("preview-"))return null;const r=n.indexOf("--");if(r===-1)return null;const o=n.slice(r+2);if(!o)return null;const s=t===-1?"":e.slice(t);return{previewBranch:o,after:s}}function be(e){return e.type===f.OIDC}function Pe(e){return e.type===f.SAML2}function Kt(e,t,n,r,o){return be(e)?Oe(t,e,n,r,{uiLocales:o}):Pe(e)?ve(t,e,n,r,o):{}}function ve(e,t,n,r,o){const i=`<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
2
2
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
3
3
  Version="2.0"
4
4
  ID="_${M()}"
5
5
  IssueInstant="${new Date().toISOString()}"
6
- AssertionConsumerServiceURL="${e}${V.SAML_CALLBACK}"
6
+ AssertionConsumerServiceURL="${e}${K.SAML_CALLBACK}"
7
7
  AttributeConsumingServiceIndex="0">
8
8
  <saml:Issuer>${t.entityId}</saml:Issuer>
9
9
  <samlp:NameIDPolicy
10
10
  AllowCreate="true"
11
11
  Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
12
- </samlp:AuthnRequest>`,c=je(i);return{loginUrl:ye(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function je(e){return Te(te(new TextEncoder().encode(e)).buffer)}function Ut(e){const t=F(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Vt(e){try{return JSON.parse(F(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function Bt(e){const t=new j().parseFromString(e,"application/xml"),r=l(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=l(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,u=l(t,"//*[local-name(.)='Audience']/text()")[0],h=u&&u.nodeValue||void 0,a=l(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",w=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],_=w&&w.nodeValue||"",S=l(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],A=S&&S.nodeValue||"",E=l(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Ue(E),C={},D=l(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(D.length)for(const b of D){const P=l(b,"./@Name")[0];if(P.nodeValue){const v=l(b,"./*[local-name(.)='AttributeValue']/text()")[0];v?.nodeValue&&(C[P.nodeValue]=v.nodeValue)}}return{uid:_,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:C,cert:a,nameFormat:A,destination:s}}function Ue(e){const t=typeof e?.nodeValue=="string"&&L(Date.parse(e.nodeValue)),n=L(Date.now()),r=L(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function L(e){return Math.floor(e/1e3)}const N={},g={jwks:{}};async function G(e,t){if(!N[e]){const n=t.configurationUrl?await Q(t.configurationUrl):t.configuration;N[e]=Ve()?$e(n):n}return N[e]}function Ve(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const Be=["authorization_endpoint","end_session_endpoint"];function $e(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if(Be.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const Fe=["kty","n","e","crv","x","y"];function He(e,t){return Fe.every(n=>e[n]===t[n])}async function Ke(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!x(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await Q(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],u=c&&He(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:u},g.jwks[i.kid]=t[i.kid]}}}}function ze(e,t,n){const r=g.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!x(s))return!1;const i=R(s.audience??[]);return W(t,i.length?i:[s.clientId])})??r.at(-1)}async function Q(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function $t(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function We(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>Je(t.hostname,r))}function Je(e,t){return e===t||e.endsWith(`.${t}`)}async function Ft(e,t){const n=new j().parseFromString(e,"application/xml"),r=l(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=ge(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function qe(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Ye(e,t,n){if(!t)return e;const r=qe(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!Se(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Ht(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Ye(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let u=0;u<c;u++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const p=await m.sign(h,y,d.HS256),a=Buffer.byteLength(p)-ae;if(a<=0)return{token:p,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const w=Buffer.byteLength(JSON.stringify(s.slice(0,i))),_=Math.max(1,w/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/_)))}return null}function Kt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?_e(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function zt(e,t){if(!t)return{};const n=t[I.AUTHORIZATION];if(!n)return{};try{const r=m.decode(n);if(typeof r.payload.type=="string"&&Ie.has(r.payload.type))return{};if(r.header.alg===d.RS256){g.jwks[r.header.kid]===void 0&&await Ke(e);const h=g.jwks[r.header.kid];if(!h)return g.jwks[r.header.kid]=null,{};await m.verify(n,h,d.RS256)}else await m.verify(n,y,d.HS256);const s=r.header.alg===d.RS256?ze(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===d.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===d.RS256&&!Le(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=Xe(i),u=Qe(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],u),..."defaultTeams"in i&&i.defaultTeams||[],...X("teamsClaimName"in i&&r.payload[c||""]||[],u),he])),name:Ge(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,y)||t[I.IDP_ACCESS_TOKEN],federatedAccessToken:t[I.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[I.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof we||O.error("Malformed JWT token: %s",r.message)}return{}}function Ge(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Qe(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function Xe(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??U;case f.OIDC:return e.teamsClaimName;default:return U}}function l(e,t){return ee.select(t,e)||[]}export{jt as buildLoginUrl,Oe as buildOidcLoginUrl,Lt as buildOidcLogoutUrl,ve as buildSAML2LoginUrl,Ct as createMcpAuthorizationCode,bt as createMcpRefreshToken,vt as createMcpSessionResource,Ut as decodeSamlResponse,Ke as discoverJwks,je as encodeSAML2,Kt as extractUserClaims,Rt as getAuthProviderLoginParams,ke as getOidcLoginParams,G as getOidcMetadata,$t as getRedoclyTokenPayload,Re as getSaml2LoginParams,zt as getUserParamsFromCookies,Ge as getUsernameFromPayload,Me as idTokenAudienceIsAllowed,Le as idTokenIsForThisProject,Nt as isIdpIssuedToken,x as isOidcProviderConfig,We as isRedoclySso,Ee as isSaml2ProviderConfig,Ot as oidcExchangeCodeForToken,g as oidcJwksCache,N as oidcMetadataCache,Mt as oidcRefreshTokenGrant,Ye as orderTeamsRbacFirst,Vt as parseOidcState,Ce as parsePreviewBranch,Bt as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Ht as signTokenWithinCookieSizeLimit,Dt as verifyMcpAuthorizationCode,Pt as verifyMcpRefreshToken,Ft as verifySAMLResponse};
12
+ </samlp:AuthnRequest>`,c=je(i);return{loginUrl:ye(t.ssoUrl,{SAMLRequest:c,RelayState:JSON.stringify({idpId:t.idpId,redirectTo:n,inviteCode:r,source:"portal",uiLocales:o})})}}function je(e){return Te(te(new TextEncoder().encode(e)).buffer)}function Vt(e){const t=$(e);if(t.startsWith("<samlp:Response")||t.indexOf("<saml2p:Response")>-1)return t;const n=ne(new Uint8Array(atob(e).split("").map(r=>r.charCodeAt(0))));return new TextDecoder().decode(n)}function Bt(e){try{return JSON.parse($(e||""))}catch{throw new Error("Invalid OAuth2 state")}}function $t(e){const t=new j().parseFromString(e,"application/xml"),r=m(t,"//*[local-name(.)='StatusCode']/@Value")[0]?.nodeValue?.endsWith("Success")||!1,s=m(t,"//*[local-name(.)='Response']/@Destination")[0]?.nodeValue||"",i=m(t,"//*[local-name(.)='Assertion']//*[local-name(.)='Issuer']/text()")[0],c=i&&i.nodeValue||void 0,d=m(t,"//*[local-name(.)='Audience']/text()")[0],h=d&&d.nodeValue||void 0,a=m(t,"//*[local-name(.)='Assertion']//*[local-name(.)='X509Certificate']/text()")[0]?.nodeValue||"",_=m(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/text()")[0],S=_&&_.nodeValue||"",T=m(t,"//*[local-name(.)='Subject']//*[local-name(.)='NameID']/@Format")[0],A=T&&T.nodeValue||"",E=m(t,"//*[local-name(.)='Conditions']/@NotOnOrAfter")[0],k=Ue(E),D={},C=m(t,"//*[local-name(.)='AttributeStatement']//*[local-name(.)='Attribute']");if(C.length)for(const b of C){const P=m(b,"./@Name")[0];if(P.nodeValue){const v=m(b,"./*[local-name(.)='AttributeValue']/text()")[0];v?.nodeValue&&(D[P.nodeValue]=v.nodeValue)}}return{uid:S,success:r,expiresAt:k,issuerId:c,entityId:h,attrs:D,cert:a,nameFormat:A,destination:s}}function Ue(e){const t=typeof e?.nodeValue=="string"&&L(Date.parse(e.nodeValue)),n=L(Date.now()),r=L(Date.now()+720*60*1e3);return t?t>n&&t<r?r:t:n}function L(e){return Math.floor(e/1e3)}const N={},w={jwks:{},missedAt:{}},Ke=6e4;function Ve(e){const t=w.jwks[e];return t===void 0?!0:t===null&&Date.now()-(w.missedAt[e]??0)>=Ke}async function G(e,t){if(!N[e]){const n=t.configurationUrl?await Q(t.configurationUrl):t.configuration;N[e]=Be()?Fe(n):n}return N[e]}function Be(){const e=ce.REDOCLY_ENFORCE_RESIDENCY;return!!e&&e.includes("host.docker.internal")}const $e=["authorization_endpoint","end_session_endpoint"];function Fe(e){if(typeof e!="object"||e===null)return e;const t={...e};for(const n of Object.keys(t)){if($e.includes(String(n)))continue;const r=t[n];typeof r=="string"&&r.includes("://localhost")&&(t[n]=r.replace("://localhost","://host.docker.internal"))}return t}const He=["kty","n","e","crv","x","y"];function We(e,t){return He.every(n=>e[n]===t[n])}async function ze(e){const t={};for(const n of Object.keys(e)){const r=e[n];if(!g(r))continue;const o=await G(n,r);if(o.jwks_uri){const s=await Q(o.jwks_uri);for(const i of s.keys){const c=t[i.kid],d=c&&We(c,i)?[...c.idpIds,n]:[n];t[i.kid]={...i,idpIds:d},w.jwks[i.kid]=t[i.kid]}}}}function Je(e,t,n){const r=w.jwks[e]?.idpIds??[];return r.find(o=>{const s=n[o];if(!g(s))return!1;const i=R(s.audience??[]);return z(t,i.length?i:[s.clientId])})??r.at(-1)}async function Q(e){return fetch(e,{headers:{Accept:"application/json"}}).then(t=>t.json())}async function Ft(e){return fetch(`${ie}/oidc/userinfo`,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`}}).then(t=>t.status===200?t.json():void 0).catch(()=>{})}function Ye(e){if(!e.configurationUrl)return!1;const t=new URL(e.configurationUrl);return["localhost","127.0.0.1","blueharvest.cloud","bhstage.cloud","cloud.redocly.com","beta.redocly.com","cloud.eu.redocly.com","beta.eu.redocly.com"].some(r=>qe(t.hostname,r))}function qe(e,t){return e===t||e.endsWith(`.${t}`)}async function Ht(e,t){const n=new j().parseFromString(e,"application/xml"),r=m(n,"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0];if(!r)throw new Error("Cannot find Signature in the SAML response");const o=ge(t),s=new Z({publicCert:o});s.loadSignature(r);try{return s.checkSignature(e)}catch{return!1}}function Ge(e){const t=new Set,n=r=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[o,s]of Object.entries(r))typeof s=="string"?t.add(o):n(s)};return n(e),t}function Qe(e,t,n){if(!t)return e;const r=Ge(t),o=s=>{const i=n?.[s]||s;return r.has(i)||!!Se(t,i)};return[...e.filter(o),...e.filter(s=>!o(s))]}async function Wt(e,t,n,r){const o=n?e[n]:void 0,s=Array.isArray(o)?Qe(o.map(String),r?.rbacConfig,r?.teamsMapping):[];let i=s.length;const c=s.length+1;for(let d=0;d<c;d++){const h={...e,idpId:t};n&&s.length&&(h[n]=s.slice(0,i));const u=await p.sign(h,y,l.HS256),a=Buffer.byteLength(u)-ae;if(a<=0)return{token:u,teamsTotal:s.length,teamsKept:i,droppedTeams:s.slice(i)};if(i===0)return null;const _=Buffer.byteLength(JSON.stringify(s.slice(0,i))),S=Math.max(1,_/i*4/3);i=Math.max(0,i-Math.max(1,Math.ceil(a/S)))}return null}function zt(e,t,n,r){t==="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"&&(e=n["http://schemas.microsoft.com/identity/claims/objectidentifier"]);let o;(t==="urn:oasis:names:tc:SAML:2.0:nameid-format:email"||t==="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")&&(o=e),t==="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"&&e?.match(/^.+@.+/m)&&(o=e);const s=n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"],i=s?.match(/^.+@.+/m);return o=o||n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]||(i?s:void 0),o=o?.toLowerCase(),{sub:e,given_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],family_name:n["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"],name:n["http://schemas.microsoft.com/identity/claims/displayname"]||s,email:o,email_verified:!0,teams:r?we(n[r]):[]}}function X(e,t={}){return e.map(n=>t[n]||n)}async function Jt(e,t){if(!t)return{};const n=t[I.AUTHORIZATION];if(!n)return{};try{const r=p.decode(n);if(typeof r.payload.type=="string"&&Ie.has(r.payload.type))return{};if(r.header.alg===l.RS256){const h=Ve(r.header.kid);h&&await ze(e);const u=w.jwks[r.header.kid];if(!u)return h&&(w.missedAt[r.header.kid]=Date.now()),w.jwks[r.header.kid]=null,{};await p.verify(n,u,l.RS256)}else await p.verify(n,y,l.HS256);const s=r.header.alg===l.RS256?Je(r.header.kid,r.payload,e):r.payload.idpId,i=e[s]||{};if(r.header.alg===l.RS256&&typeof r.payload.exp!="number")return O.warn("Rejected id_token: it has no exp claim"),{};if(r.header.alg===l.RS256&&!Le(r.payload,i))return O.warn("Rejected id_token: it was issued for another project"),{};const c=et(i),d=Ze(i);return{...r.payload,email:r.payload.email?.toLowerCase(),idpId:s,teams:Array.from(new Set([...X(r.payload.teams||[],d),..."defaultTeams"in i&&i.defaultTeams||[],...X("teamsClaimName"in i&&r.payload[c||""]||[],d),he])),name:Xe(r.payload),isAuthenticated:!0,idpAccessToken:ue(r.payload.idp_access_token,y)||t[I.IDP_ACCESS_TOKEN],federatedAccessToken:t[I.FEDERATED_ACCESS_TOKEN],federatedIdToken:t[I.FEDERATED_ID_TOKEN],authCookie:n}}catch(r){r instanceof _e||O.error("Malformed JWT token: %s",r.message)}return{}}function Xe(e){return(e.firstName&&e.lastName?`${e.firstName} ${e.lastName}`:e.name||e.given_name||e.firstName||e.lastName)||e.email}function Ze(e){switch(e.type){case f.SAML2:return e.teamsAttributeMap;case f.OIDC:return e.teamsClaimMap;default:return}}function et(e){switch(e.type){case f.SAML2:return e.teamsAttributeName??U;case f.OIDC:return e.teamsClaimName;default:return U}}function m(e,t){return ee.select(t,e)||[]}export{Ke as UNKNOWN_KID_RETRY_MS,Kt as buildLoginUrl,Oe as buildOidcLoginUrl,Dt as buildOidcLogoutUrl,ve as buildSAML2LoginUrl,bt as createMcpAuthorizationCode,vt as createMcpRefreshToken,Ut as createMcpSessionResource,Vt as decodeSamlResponse,ze as discoverJwks,je as encodeSAML2,zt as extractUserClaims,Mt as getAuthProviderLoginParams,ke as getOidcLoginParams,G as getOidcMetadata,Ft as getRedoclyTokenPayload,Re as getSaml2LoginParams,Jt as getUserParamsFromCookies,Xe as getUsernameFromPayload,Me as idTokenAudienceIsAllowed,Le as idTokenIsForThisProject,Ct as isIdpIssuedToken,g as isOidcProviderConfig,Ye as isRedoclySso,Ee as isSaml2ProviderConfig,Lt as oidcExchangeCodeForToken,w as oidcJwksCache,N as oidcMetadataCache,Nt as oidcRefreshTokenGrant,Qe as orderTeamsRbacFirst,Bt as parseOidcState,De as parsePreviewBranch,$t as parseSamlResponse,J as rewritePreviewAuthRedirectUri,Wt as signTokenWithinCookieSizeLimit,Pt as verifyMcpAuthorizationCode,jt as verifyMcpRefreshToken,Ht as verifySAMLResponse};
@@ -1 +1 @@
1
- import"../../node-crypto-polyfill.js";import{timingSafeEqual as H}from"node:crypto";import{utf8ToUint8Array as u,encodeBase64URL as m,arrayBufferToBase64URL as w,decodeBase64URL as S}from"./encode.js";import{decodeJwtSignature as b}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as E,JwtTokenInvalid as d,JwtTokenNotBefore as K,JwtTokenExpired as T,JwtTokenSignatureMismatched as B,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=e=>{switch(e.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new E(e)}},g=async(e,t,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");t.startsWith("-----BEGIN PRIVATE KEY-----")&&(t=t.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),t=atob(t));const r=n===s.RS256||n===s.ES256,o=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?C(t):u(t),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),o,u(e))};function C(e){const t=new ArrayBuffer(e.length),n=new Uint8Array(t);for(let r=0,o=e.length;r<o;r++)n[r]=e.charCodeAt(r);return t}const V=async(e,t,n=s.HS256,r)=>{const o=await m(JSON.stringify(e)),i=`${await m(JSON.stringify({alg:n,typ:"JWT",kid:r}))}.${o}`,c=await w(await g(i,t,n));return`${i}.${c}`},k=async(e,t,n=s.HS256,r=!1)=>{const o=e.split(".");if(o.length!==3)throw new d;const{payload:a}=J(e),i=Math.floor(Date.now()/1e3);if(typeof a.nbf=="number"&&a.nbf>i)throw new K;if(!r&&typeof a.exp=="number"&&a.exp<=i)throw new T;const c=o.slice(0,2).join("."),y=o[2];let f=!1;if(typeof t!="string")f=await N(c,y,t,n);else{const A=await w(await g(c,t,n));f=R(A,y)}if(!f)throw new B;return!0};function R(e,t){const n=Buffer.from(e),r=Buffer.from(t);return n.length===r.length&&H(n,r)}const J=e=>{try{const[t,n]=e.split("."),r=JSON.parse(S(t)),o=JSON.parse(S(n));return{header:r,payload:o}}catch{throw new d}},N=async(e,t,n,r)=>{const o=await crypto.subtle.importKey("jwk",n,p(r),!1,[l.Verify]);return await crypto.subtle.verify(p(r),o,b(t),u(e))};export{J as decode,V as sign,k as verify};
1
+ import"../../node-crypto-polyfill.js";import{timingSafeEqual as H}from"node:crypto";import{utf8ToUint8Array as u,encodeBase64URL as m,arrayBufferToBase64URL as w,decodeBase64URL as S}from"./encode.js";import{decodeJwtSignature as b}from"./decode-jwt-signature.js";import{AlgorithmTypes as s,JwtAlgorithmNotImplemented as E,JwtTokenInvalid as d,JwtTokenNotBefore as K,JwtTokenExpired as T,JwtTokenSignatureMismatched as B,CryptoKeyFormat as h,CryptoKeyUsage as l}from"./types.js";const p=e=>{switch(e.toUpperCase()){case"HS256":return{name:"HMAC",hash:{name:"SHA-256"}};case"HS384":return{name:"HMAC",hash:{name:"SHA-384"}};case"HS512":return{name:"HMAC",hash:{name:"SHA-512"}};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new E(e)}},g=async(e,t,n=s.HS256)=>{if(!crypto.subtle||!crypto.subtle.importKey)throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");t.startsWith("-----BEGIN PRIVATE KEY-----")&&(t=t.replace("-----BEGIN PRIVATE KEY-----","").replace("-----END PRIVATE KEY-----","").split(/\\n|\n/).join("").trim(),t=atob(t));const r=n===s.RS256||n===s.ES256,o=await crypto.subtle.importKey(r?h.PKCS8:h.RAW,r?C(t):u(t),p(n),!1,[l.Sign]);return await crypto.subtle.sign(p(n),o,u(e))};function C(e){const t=new ArrayBuffer(e.length),n=new Uint8Array(t);for(let r=0,o=e.length;r<o;r++)n[r]=e.charCodeAt(r);return t}const V=async(e,t,n=s.HS256,r)=>{const o=m(JSON.stringify(e)),i=`${m(JSON.stringify({alg:n,typ:"JWT",kid:r}))}.${o}`,c=await w(await g(i,t,n));return`${i}.${c}`},k=async(e,t,n=s.HS256,r=!1)=>{const o=e.split(".");if(o.length!==3)throw new d;const{payload:a}=J(e),i=Math.floor(Date.now()/1e3);if(typeof a.nbf=="number"&&a.nbf>i)throw new K;if(!r&&typeof a.exp=="number"&&a.exp<=i)throw new T;const c=o.slice(0,2).join("."),y=o[2];let f=!1;if(typeof t!="string")f=await N(c,y,t,n);else{const A=await w(await g(c,t,n));f=R(A,y)}if(!f)throw new B;return!0};function R(e,t){const n=Buffer.from(e),r=Buffer.from(t);return n.length===r.length&&H(n,r)}const J=e=>{try{const[t,n]=e.split("."),r=JSON.parse(S(t)),o=JSON.parse(S(n));return{header:r,payload:o}}catch{throw new d}},N=async(e,t,n,r)=>{const o=await crypto.subtle.importKey("jwk",n,p(r),!1,[l.Verify]);return await crypto.subtle.verify(p(r),o,b(t),u(e))};export{J as decode,V as sign,k as verify};
@@ -1 +1 @@
1
- import{KvService as h}from"../../persistence/kv/services/kv-service.js";import{DEFAULT_AUTHENTICATED_TEAM as l}from"../../../constants/common.js";import{envConfig as s}from"../../config/env-config.js";import{fetchWithClient as T}from"../../utils/fetch-with-client.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as y,JWT_SECRET_KEY as p}from"../../constants/common.js";import*as m from"../jwt/jwt.js";import{AlgorithmTypes as I}from"../jwt/types.js";const f=60,E=e=>["POST","PUT","DELETE","PATCH"].includes(e),_=e=>["GET"].includes(e);function S({serverOutDir:e,protectReadMethods:t=!0}){return async(a,r)=>await w(a,r,e,t)}const w=async(e,t,a,r=!0)=>{e.header("Cache-Control",y);const n=e.req.method,i=E(n)||_(n)&&r,o=e.req.header("apiKey");if(o)return await C(e,t,o,a);const u=e.req.header("authorization")?.replace("Bearer ","");return u?await g(e,t,u):i?e.json({message:"API key is required"},401):await t()},C=async(e,t,a,r)=>{if(!s.BH_API_URL||!s.ORGANIZATION_ID)return e.json({message:"API key validation service not configured"},500);try{const n=await h.getInstance({baseDbDir:r}),i=await v(n,a);if(i)return e.set("apiKeyTeams",i.teams),await t();const o=new URL(`/api/orgs/${s.ORGANIZATION_ID}/session`,s.BH_API_URL).toString(),c=await T(o,{method:"GET",headers:{Authorization:`Bearer ${a}`}});if(!c.ok)return e.json({message:"Invalid API key"},401);const A=(await c.json())?.user?.teams?.[s.ORGANIZATION_ID]??[],d=[l,...A];return e.set("apiKeyTeams",d),await n.set(["api-keys","reunite",a],{valid:!0,teams:d},{ttlInSeconds:f}),await t()}catch{return e.json({message:"API key validation failed"},400)}},g=async(e,t,a)=>{try{const r=await m.verify(a,p,I.HS256),n=m.decode(a).payload.isInternalConnection;return!r||!n?e.json({message:"API key is required"},401):await t()}catch{return e.json({message:"API key validation failed"},400)}},v=async(e,t)=>{try{return await e.get(["api-keys","reunite",t])}catch{return null}};export{S as catalogAuthMiddleware};
1
+ import{KvService as A}from"../../persistence/kv/services/kv-service.js";import{DEFAULT_AUTHENTICATED_TEAM as h}from"../../../constants/common.js";import{envConfig as c}from"../../config/env-config.js";import{fetchWithClient as l}from"../../utils/fetch-with-client.js";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as y,JWT_SECRET_KEY as T}from"../../constants/common.js";import*as m from"../jwt/jwt.js";import{AlgorithmTypes as f}from"../jwt/types.js";const p=60,I=e=>["POST","PUT","DELETE","PATCH"].includes(e),E=e=>["GET"].includes(e);function O({serverOutDir:e,protectReadMethods:t=!0}){return async(a,r)=>await _(a,r,e,t)}const _=async(e,t,a,r=!0)=>{e.header("Cache-Control",y);const n=e.req.method,i=I(n)||E(n)&&r,o=e.req.header("apiKey");if(o)return await w(e,t,o,a);const s=e.req.header("authorization")?.replace("Bearer ","");return s?await C(e,t,s):i?e.json({message:"API key is required"},401):await t()},w=async(e,t,a,r)=>{if(!c.BH_API_URL||!c.ORGANIZATION_ID)return e.json({message:"API key validation service not configured"},500);try{const n=await A.getInstance({baseDbDir:r}),i=await g(n,a);if(i)return e.set("apiKeyTeams",i.teams),await t();const o=new URL(`/api/orgs/${c.ORGANIZATION_ID}/session`,c.BH_API_URL).toString(),u=await l(o,{method:"GET",headers:{Authorization:`Bearer ${a}`}});if(!u.ok)return e.json({message:"Invalid API key"},401);const s=await u.json(),d=[h,...s.teams];return e.set("apiKeyTeams",d),await n.set(["api-keys","reunite",a],{valid:!0,teams:d},{ttlInSeconds:p}),await t()}catch{return e.json({message:"API key validation failed"},400)}},C=async(e,t,a)=>{try{const r=await m.verify(a,T,f.HS256),n=m.decode(a).payload.isInternalConnection;return!r||!n?e.json({message:"API key is required"},401):await t()}catch{return e.json({message:"API key validation failed"},400)}},g=async(e,t)=>{try{return await e.get(["api-keys","reunite",t])}catch{return null}};export{O as catalogAuthMiddleware};
@@ -1 +1 @@
1
- import{getCookie as H}from"hono/cookie";import{createHash as F,timingSafeEqual as ne}from"node:crypto";import{ulid as oe}from"ulid";import{isRedoclySso as ie,isOidcProviderConfig as R,createMcpAuthorizationCode as se,verifyMcpAuthorizationCode as ae,createMcpRefreshToken as G,verifyMcpRefreshToken as ce,createMcpSessionResource as M,oidcRefreshTokenGrant as de,getOidcMetadata as pe,getUserParamsFromCookies as ue,idTokenIsForThisProject as le}from"../../auth.js";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolveSiteBranding as _e}from"../../../plugins/mcp/handlers/credentials-page/site-branding.js";import{SUPPORTED_MCP_SCOPES as E}from"../../../plugins/mcp/constants.js";import{AUTH_URL as fe,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as b,MCP_REFRESH_SESSION_LIFETIME_SEC as K}from"../../../constants/common.js";import{ServerRoutes as I}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as w}from"@redocly/theme/core/utils";import{telemetry as y}from"../../../telemetry/index.js";import{envConfig as q}from"../../../config/env-config.js";import*as C from"../../jwt/jwt.js";import{InternalTokenTypes as O,McpAccessTokenMarker as U}from"../../jwt/token-types.js";import{AlgorithmTypes as A,JwtAlgorithmNotImplemented as he,JwtTokenExpired as me,JwtTokenInvalid as ge,JwtTokenNotBefore as Ce,JwtTokenSignatureMismatched as Te}from"../../jwt/types.js";import{getRequestOrigin as L}from"../../utils/get-request-origin.js";import{encryptClaimValue as B}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as ke}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as J}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as Se,isClientMetadataDocumentUrl as j,isRedirectUriRegistered as Ie}from"./mcp-client-metadata.js";import{CONSENT_PAGE_HEADERS as V,MCP_CONSENT_TTL_SEC as we,consentInvalidPage as Me,consentPage as Ee}from"./mcp-consent-page.js";import{MCP_CONSENT_MEMORY_DAYS as ye,hasConsent as Ae,redeemAuthorizationCode as Re,redeemRefreshToken as Pe,saveConsent as ve}from"./mcp-oauth-store.js";const s=(r,e,t=200,n)=>r.json(e,t,{"Content-Type":"application/json",...n??{}}),z=new Set(["S256"]),$=["authorization_code","refresh_token"],Oe="https://claude.ai/oauth/claude-code-client-metadata";function Ue(r,e,t){if(!r||!z.has(e||""))return!1;const n=F("sha256").update(t).digest("base64url"),i=Buffer.from(n),a=Buffer.from(r);return i.length!==a.length?!1:ne(i,a)}async function Le(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONTEXT,...r,iat:e,exp:e+600},m,A.HS256)}async function Ne(r){await C.verify(r,m,A.HS256);const{payload:e}=C.decode(r);if(e.type!==O.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function fr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=L(e);return s(e,{resource:`${t}${w("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${I.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...E],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function hr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=L(e);return s(e,{issuer:t,authorization_endpoint:`${t}${w(I.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${w(I.MCP_TOKEN_PORTAL)}`,scopes_supported:[...E],registration_endpoint:`${t}${w(I.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...$],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function mr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:q.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...$],response_types:["code"],scope:E.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}const De=new Set(["127.0.0.1","localhost","[::1]"]),He=new Set(["http:","https:","ws:","wss:","ftp:","file:"]),be=new Set(["javascript:","data:","blob:","about:","vbscript:","intent:","chrome:","view-source:","ms-msdt:","ms-appx-web:"]),qe=2048,je=/[\s\p{Cc}]/u;function W(r){return r.protocol==="http:"&&De.has(r.hostname)}function Y(r){if(!r||r.length>qe||r.includes("#")||je.test(r))return;const e=URL.parse(r);if(e){if(e.protocol==="https:"||W(e))return r.toLowerCase().startsWith(`${e.protocol}//`)?e:void 0;if(!(He.has(e.protocol)||be.has(e.protocol)))return e}}function Z(r){const e=r||{},t=Object.keys(e),n=t.find(a=>R(e[a])&&ie(e[a])),i=t.find(a=>R(e[a]));return n||i||t[0]}function gr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:n}=t,i=n.get("redirect_uri"),a=n.get("client_id"),c=n.get("code_challenge"),o=n.get("code_challenge_method"),d=oe();if(!i||!Y(i))return s(e,{error:"invalid_request",error_description:"redirect_uri must be an https URL, a loopback http URL, or a private-use scheme"},400);if(!c)return s(e,{error:"invalid_request",error_description:"code_challenge is required"},400);if(!z.has(o||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...z].join(", ")}`},400);let p;if(j(a))try{const u=await Se(a);p=u.client_name;const _=a===Oe;if(!Ie(i,u.redirect_uris,{ignorePort:_}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(u){const _=u instanceof Error?u.message:String(u);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${_}`},400)}y.sendMcpAuthorizationStartedMessage([{...M(d),redirect_uri:i||null}]);const l=L(e),k={isMcpFlow:!0,originalRedirectUri:i,mcpClientId:a,mcpState:n.get("state"),mcpSessionId:d,mcpCodeChallenge:c,mcpCodeChallengeMethod:o,mcpClientName:p,timestamp:Date.now()};try{const u=await Le(k),_=new URL(w(I.IDP_LOGIN),l);_.searchParams.set("redirectTo",`${I.MCP_CALLBACK}/${u}`);const h=Z(r.getConfig().ssoDirect);return h?(_.searchParams.set("idpId",h),e.redirect(_.toString())):s(e,{error:"not_found"},404)}catch(u){const _=u instanceof Error?u.message:String(u),h=u instanceof Error?u.stack:String(u);y.sendMcpAuthorizationFailedMessage([{...M(d),error:_,error_details:h}]);const S=new URL(w(`${fe}/oauth2/auth`));return S.search=n.toString(),e.redirect(S.toString())}}}async function X(r){const e=Math.floor(Date.now()/1e3),t=r.ttlSec?e+r.ttlSec:Math.min(typeof r.claims.exp=="number"?r.claims.exp:1/0,e+b);return{accessToken:await C.sign({...r.claims,[U.CLAIM]:U.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e}:{},exp:t},m,A.HS256,r.kid),expiresIn:Math.max(t-e,0)}}async function ze(r,e,t){const n=T=>s(r,{error:"invalid_grant",error_description:T},400),i=r.get("logger"),a=t.get("refresh_token"),c=t.get("client_id")||void 0;if(!a)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let o;try{o=await ce(a)}catch(T){return i?.warn("MCP refresh token rejected: %s",T instanceof Error?T.message:String(T)),n("Invalid refresh token")}if(c&&c!==o.client_id)return n("Client mismatch");const d=o.idp_id?e.getConfig().ssoDirect?.[o.idp_id]:void 0;let p,l,k=o.idp_access_token_enc,u=o.idp_refresh_token_enc;if(o.idp_refresh_token_enc){let T;try{T=ke(o.idp_refresh_token_enc,m)}catch{return n("Invalid refresh token")}if(!o.idp_id||!R(d))return n("Unknown identity provider");let f;try{const g=await pe(o.idp_id,d);if(!g?.token_endpoint)return n("Identity provider has no token endpoint");f=await de(g.token_endpoint,T,d)}catch(g){return i?.warn("MCP refresh failed to reach the IdP token endpoint: %s",g instanceof Error?g.message:String(g)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(f.error||!(f.id_token||f.access_token))return i?.warn("MCP refresh rejected by IdP: %s",f.error||"empty token response"),n("Refresh rejected by identity provider");if(f.refresh_token&&(u=J(f.refresh_token,m)),f.access_token&&(k=B(f.access_token,m)),f.id_token){try{const g=C.decode(f.id_token);p=g.payload,l=g.header.kid}catch{return n("Invalid token response from identity provider")}if(p.iss!==o.claims.iss||p.sub!==o.claims.sub)return i?.warn("MCP refresh rejected: the id_token names another issuer or subject"),n("Invalid refresh token")}}const _=Math.min(R(d)&&d.tokenExpirationTime||b,b),h=p??o.claims;if(!le(h,d))return i?.warn("MCP refresh rejected: the id_token was issued for another project"),n("Invalid refresh token");if(!await Pe(await e.getKv(),a,o.exp))return i?.warn("MCP refresh rejected: the refresh token was already used"),n("Refresh token already used");const S=await X({claims:h,kid:l,idpId:o.idp_id,idpAccessTokenEnc:k,ttlSec:p?void 0:_}),D=await G({clientId:o.client_id,idpId:o.idp_id,claims:h,idpAccessTokenEnc:k,idpRefreshTokenEnc:u,sessionExp:o.session_exp});return s(r,{access_token:S.accessToken,token_type:"Bearer",expires_in:S.expiresIn,refresh_token:D,scope:E.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Cr(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),n=t.get("grant_type"),i=t.get("code"),a=t.get("redirect_uri")||void 0,c=t.get("code_verifier")||void 0;if(!$.includes(n))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(n==="refresh_token")return await ze(e,r,t);if(!i)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const o=await ae(i);if(a&&a!==o.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(!c)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!Ue(o.code_challenge,o.code_challenge_method,c))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400);if(q.OAUTH_CLIENT_ID&&o.client_id&&!j(o.client_id)&&o.client_id!==q.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const d=o.id_token;if(typeof d!="string"||d.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const p=await Q(d,r.getConfig().ssoDirect);if(!p)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);if(!await Re(await r.getKv(),i,o.exp))return s(e,{error:"invalid_grant",error_description:"Authorization code already used"},400);const l=o.idp_access_token_enc,{accessToken:k,expiresIn:u}=await X({claims:p.payload,kid:p.kid,idpId:o.idp_id,idpAccessTokenEnc:l}),h=Math.floor(Date.now()/1e3)+K,S=await G({clientId:o.client_id,idpId:o.idp_id,claims:p.payload,idpAccessTokenEnc:l,idpRefreshTokenEnc:o.idp_refresh_token_enc,sessionExp:h}),D={access_token:k,token_type:"Bearer",expires_in:u,refresh_token:S,scope:E.join(" ")};return s(e,D,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(o){return e.get("logger")?.warn("MCP token exchange failed: %s",o instanceof Error?o.message:String(o)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const n=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:n},500)}}}async function Q(r,e){const t=await ue(e||{},{[v.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:n,header:i}=C.decode(r);if(n[U.CLAIM]!==U.VALUE)return{payload:n,kid:i.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function N(r){return F("sha256").update(r).digest("base64url")}async function $e(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONSENT,session_hash:N(r.sessionToken),context_hash:N(r.contextToken),iat:e,exp:e+we},m,A.HS256)}async function Fe(r,e){try{await C.verify(r,m,A.HS256)}catch{return!1}const{payload:t}=C.decode(r);return t.type===O.MCP_CONSENT&&t.session_hash===N(e.sessionToken)&&t.context_hash===N(e.contextToken)}function x(r,e){return async t=>{const n=new URL(t.req.url);let i=n.searchParams.get("context");if(!i&&n.pathname.startsWith(w(`${I.MCP_CALLBACK}/`))){const c=n.pathname.split("/");i=c[c.length-1]}if(!i)return t.text("Missing context parameter",400);let a=null;try{const c=await Ne(i);a=c.mcpSessionId||null;const o=c.originalRedirectUri,d=Y(o);if(!c.isMcpFlow||!o||!d)throw new Error("Invalid MCP context");const p=H(t,v.AUTHORIZATION),l=p?await Q(p,r.getConfig().ssoDirect):void 0;return!p||!l?(y.sendMcpAuthorizationFailedMessage([{...M(a),error:"Authentication required",error_details:null}]),t.text("Authentication required",401)):await e(t,{contextToken:i,mcpContext:c,redirectUri:o,redirectUrl:d,sessionToken:p,session:l})}catch(c){const o=Be(c);return y.sendMcpAuthorizationFailedMessage([{...M(a),...o}]),t.text(`Invalid MCP callback: ${o.error}`,400)}}}async function ee(r,e,{mcpContext:t,redirectUri:n,redirectUrl:i,sessionToken:a,session:c}){const o=H(r,v.IDP_ACCESS_TOKEN),d=H(r,v.IDP_REFRESH_TOKEN),p=await se({idToken:a,idpId:c.idpId??Z(e.getConfig().ssoDirect),idpAccessTokenEnc:o?B(o,m):void 0,idpRefreshTokenEnc:d?J(d,m):void 0,clientId:t.mcpClientId||"",redirectUri:n,codeChallenge:t.mcpCodeChallenge||void 0,codeChallengeMethod:t.mcpCodeChallengeMethod||void 0,ttlSec:600}),l=new URL(i);return l.searchParams.set("code",p),t.mcpState&&l.searchParams.set("state",t.mcpState),y.sendMcpAuthorizationCompletedMessage([{...M(t.mcpSessionId||null),redirect_uri:n}]),r.redirect(l.toString())}function Ge(r,{mcpContext:e,redirectUrl:t}){const n=new URL(t);return n.searchParams.set("error","access_denied"),e.mcpState&&n.searchParams.set("state",e.mcpState),y.sendMcpAuthorizationFailedMessage([{...M(e.mcpSessionId||null),error:"access_denied",error_details:null}]),r.redirect(n.toString())}function Ke({payload:r}){return[r.email,r.name,r.sub].find(e=>typeof e=="string"&&e.length>0)}function re({mcpContext:r,redirectUri:e,session:t}){const{sub:n}=t.payload;if(!(typeof n!="string"||n.length===0))return{idpId:t.idpId,subject:n,clientId:r.mcpClientId||"",redirectUri:e}}function te(r,e){const{seo:t,colorMode:n}=e.getConfig(),{logo:i}=e.getGlobalData();return{..._e({seo:t,logo:i,colorMode:n}),host:new URL(L(r)).host}}function Tr(r){return x(r,async(e,t)=>{const n=re(t);if(W(t.redirectUrl)||n&&await Ae(await r.getKv(),n))return ee(e,r,t);const{mcpClientId:i,mcpClientName:a}=t.mcpContext,c=Ee({site:te(e,r),user:Ke(t.session),clientName:a||void 0,clientIdHost:j(i)?new URL(i).host:void 0,redirectUrl:t.redirectUrl,scopes:E,accessLifetimeHours:K/3600,rememberDays:n?ye:void 0,consentToken:await $e(t)});return new Response(c,{status:200,headers:V})})}function kr(r){return x(r,async(e,t)=>{const n=await e.req.formData(),i=n.get("consent");if(typeof i!="string"||!await Fe(i,t))return new Response(Me(te(e,r)),{status:403,headers:V});if(n.get("decision")==="allow"){const a=re(t);return a&&await ve(await r.getKv(),a),ee(e,r,t)}return Ge(e,t)})}function Be(r){return r instanceof Error&&[he,me,ge,Ce,Te].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{$ as SUPPORTED_MCP_GRANT_TYPES,Le as createMcpContextToken,gr as mcpAuthorizationHandler,Tr as mcpCallbackHandler,kr as mcpConsentHandler,mr as mcpDynamicClientRegistrationHandler,hr as mcpOAuthAuthorizationServerHandler,fr as mcpOAuthProtectedResourceHandler,Cr as mcpTokenPortalHandler,Z as resolveMcpIdpId,Ne as verifyAndParseMcpContextToken,Ue as verifyPkce};
1
+ import{getCookie as H}from"hono/cookie";import{createHash as G,timingSafeEqual as oe}from"node:crypto";import{ulid as ie}from"ulid";import{isRedoclySso as se,isOidcProviderConfig as P,createMcpAuthorizationCode as ae,verifyMcpAuthorizationCode as ce,createMcpRefreshToken as K,verifyMcpRefreshToken as de,createMcpSessionResource as M,oidcRefreshTokenGrant as pe,getOidcMetadata as ue,getUserParamsFromCookies as le,idTokenIsForThisProject as _e}from"../../auth.js";import{isMcpContentProtected as R}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolveSiteBranding as fe}from"../../../plugins/mcp/handlers/credentials-page/site-branding.js";import{SUPPORTED_MCP_SCOPES as E}from"../../../plugins/mcp/constants.js";import{AUTH_URL as he,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as b,MCP_REFRESH_SESSION_LIFETIME_SEC as B}from"../../../constants/common.js";import{ServerRoutes as I}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as w}from"@redocly/theme/core/utils";import{telemetry as y}from"../../../telemetry/index.js";import{envConfig as q}from"../../../config/env-config.js";import{hasProjectDatabase as j}from"../../../providers/database/has-project-database.js";import*as C from"../../jwt/jwt.js";import{InternalTokenTypes as O,McpAccessTokenMarker as U}from"../../jwt/token-types.js";import{AlgorithmTypes as A,JwtAlgorithmNotImplemented as me,JwtTokenExpired as ge,JwtTokenInvalid as Ce,JwtTokenNotBefore as Te,JwtTokenSignatureMismatched as ke}from"../../jwt/types.js";import{getRequestOrigin as L}from"../../utils/get-request-origin.js";import{encryptClaimValue as J}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as Se}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as V}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as Ie,isClientMetadataDocumentUrl as z,isRedirectUriRegistered as we}from"./mcp-client-metadata.js";import{CONSENT_PAGE_HEADERS as W,MCP_CONSENT_TTL_SEC as Me,consentInvalidPage as Ee,consentPage as ye}from"./mcp-consent-page.js";import{MCP_CONSENT_MEMORY_DAYS as Ae,hasConsent as Pe,redeemAuthorizationCode as Re,redeemRefreshToken as ve,saveConsent as Oe}from"./mcp-oauth-store.js";const s=(r,e,t=200,n)=>r.json(e,t,{"Content-Type":"application/json",...n??{}}),$=new Set(["S256"]),F=["authorization_code","refresh_token"],Ue="https://claude.ai/oauth/claude-code-client-metadata";function Le(r,e,t){if(!r||!$.has(e||""))return!1;const n=G("sha256").update(t).digest("base64url"),i=Buffer.from(n),a=Buffer.from(r);return i.length!==a.length?!1:oe(i,a)}async function Ne(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONTEXT,...r,iat:e,exp:e+600},m,A.HS256)}async function De(r){await C.verify(r,m,A.HS256);const{payload:e}=C.decode(r);if(e.type!==O.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function mr(r){return async e=>{if(!R(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=L(e);return s(e,{resource:`${t}${w("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${I.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...E],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function gr(r){return async e=>{if(!R(r))return s(e,{error:"not_found"},404);const t=L(e);return s(e,{issuer:t,authorization_endpoint:`${t}${w(I.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${w(I.MCP_TOKEN_PORTAL)}`,scopes_supported:[...E],registration_endpoint:`${t}${w(I.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...F],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function Cr(r){return async e=>{if(!R(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:q.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...F],response_types:["code"],scope:E.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}const He=new Set(["127.0.0.1","localhost","[::1]"]),be=new Set(["http:","https:","ws:","wss:","ftp:","file:"]),qe=new Set(["javascript:","data:","blob:","about:","vbscript:","intent:","chrome:","view-source:","ms-msdt:","ms-appx-web:"]),je=2048,ze=/[\s\p{Cc}]/u;function Y(r){return r.protocol==="http:"&&He.has(r.hostname)}function Z(r){if(!r||r.length>je||r.includes("#")||ze.test(r))return;const e=URL.parse(r);if(e){if(e.protocol==="https:"||Y(e))return r.toLowerCase().startsWith(`${e.protocol}//`)?e:void 0;if(!(be.has(e.protocol)||qe.has(e.protocol)))return e}}function X(r){const e=r||{},t=Object.keys(e),n=t.find(a=>P(e[a])&&se(e[a])),i=t.find(a=>P(e[a]));return n||i||t[0]}function Tr(r){return async e=>{if(!R(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:n}=t,i=n.get("redirect_uri"),a=n.get("client_id"),c=n.get("code_challenge"),o=n.get("code_challenge_method"),d=ie();if(!i||!Z(i))return s(e,{error:"invalid_request",error_description:"redirect_uri must be an https URL, a loopback http URL, or a private-use scheme"},400);if(!c)return s(e,{error:"invalid_request",error_description:"code_challenge is required"},400);if(!$.has(o||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...$].join(", ")}`},400);let p;if(z(a))try{const u=await Ie(a);p=u.client_name;const _=a===Ue;if(!we(i,u.redirect_uris,{ignorePort:_}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(u){const _=u instanceof Error?u.message:String(u);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${_}`},400)}y.sendMcpAuthorizationStartedMessage([{...M(d),redirect_uri:i||null}]);const l=L(e),k={isMcpFlow:!0,originalRedirectUri:i,mcpClientId:a,mcpState:n.get("state"),mcpSessionId:d,mcpCodeChallenge:c,mcpCodeChallengeMethod:o,mcpClientName:p,timestamp:Date.now()};try{const u=await Ne(k),_=new URL(w(I.IDP_LOGIN),l);_.searchParams.set("redirectTo",`${I.MCP_CALLBACK}/${u}`);const h=X(r.getConfig().ssoDirect);return h?(_.searchParams.set("idpId",h),e.redirect(_.toString())):s(e,{error:"not_found"},404)}catch(u){const _=u instanceof Error?u.message:String(u),h=u instanceof Error?u.stack:String(u);y.sendMcpAuthorizationFailedMessage([{...M(d),error:_,error_details:h}]);const S=new URL(w(`${he}/oauth2/auth`));return S.search=n.toString(),e.redirect(S.toString())}}}async function Q(r){const e=Math.floor(Date.now()/1e3),t=r.ttlSec?e+r.ttlSec:Math.min(typeof r.claims.exp=="number"?r.claims.exp:1/0,e+b);return{accessToken:await C.sign({...r.claims,[U.CLAIM]:U.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e}:{},exp:t},m,A.HS256,r.kid),expiresIn:Math.max(t-e,0)}}async function $e(r,e,t){const n=T=>s(r,{error:"invalid_grant",error_description:T},400),i=r.get("logger"),a=t.get("refresh_token"),c=t.get("client_id")||void 0;if(!a)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let o;try{o=await de(a)}catch(T){return i?.warn("MCP refresh token rejected: %s",T instanceof Error?T.message:String(T)),n("Invalid refresh token")}if(c&&c!==o.client_id)return n("Client mismatch");const d=o.idp_id?e.getConfig().ssoDirect?.[o.idp_id]:void 0;let p,l,k=o.idp_access_token_enc,u=o.idp_refresh_token_enc;if(o.idp_refresh_token_enc){let T;try{T=Se(o.idp_refresh_token_enc,m)}catch{return n("Invalid refresh token")}if(!o.idp_id||!P(d))return n("Unknown identity provider");let f;try{const g=await ue(o.idp_id,d);if(!g?.token_endpoint)return n("Identity provider has no token endpoint");f=await pe(g.token_endpoint,T,d)}catch(g){return i?.warn("MCP refresh failed to reach the IdP token endpoint: %s",g instanceof Error?g.message:String(g)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(f.error||!(f.id_token||f.access_token))return i?.warn("MCP refresh rejected by IdP: %s",f.error||"empty token response"),n("Refresh rejected by identity provider");if(f.refresh_token&&(u=V(f.refresh_token,m)),f.access_token&&(k=J(f.access_token,m)),f.id_token){try{const g=C.decode(f.id_token);p=g.payload,l=g.header.kid}catch{return n("Invalid token response from identity provider")}if(p.iss!==o.claims.iss||p.sub!==o.claims.sub)return i?.warn("MCP refresh rejected: the id_token names another issuer or subject"),n("Invalid refresh token")}}const _=Math.min(P(d)&&d.tokenExpirationTime||b,b),h=p??o.claims;if(!_e(h,d))return i?.warn("MCP refresh rejected: the id_token was issued for another project"),n("Invalid refresh token");if(j()&&!await ve(await e.getKv(),a,o.exp))return i?.warn("MCP refresh rejected: the refresh token was already used"),n("Refresh token already used");const S=await Q({claims:h,kid:l,idpId:o.idp_id,idpAccessTokenEnc:k,ttlSec:p?void 0:_}),D=await K({clientId:o.client_id,idpId:o.idp_id,claims:h,idpAccessTokenEnc:k,idpRefreshTokenEnc:u,sessionExp:o.session_exp});return s(r,{access_token:S.accessToken,token_type:"Bearer",expires_in:S.expiresIn,refresh_token:D,scope:E.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function kr(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),n=t.get("grant_type"),i=t.get("code"),a=t.get("redirect_uri")||void 0,c=t.get("code_verifier")||void 0;if(!F.includes(n))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(n==="refresh_token")return await $e(e,r,t);if(!i)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const o=await ce(i);if(a&&a!==o.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(!c)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!Le(o.code_challenge,o.code_challenge_method,c))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400);if(q.OAUTH_CLIENT_ID&&o.client_id&&!z(o.client_id)&&o.client_id!==q.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const d=o.id_token;if(typeof d!="string"||d.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const p=await x(d,r.getConfig().ssoDirect);if(!p)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);if(j()&&!await Re(await r.getKv(),i,o.exp))return s(e,{error:"invalid_grant",error_description:"Authorization code already used"},400);const l=o.idp_access_token_enc,{accessToken:k,expiresIn:u}=await Q({claims:p.payload,kid:p.kid,idpId:o.idp_id,idpAccessTokenEnc:l}),h=Math.floor(Date.now()/1e3)+B,S=await K({clientId:o.client_id,idpId:o.idp_id,claims:p.payload,idpAccessTokenEnc:l,idpRefreshTokenEnc:o.idp_refresh_token_enc,sessionExp:h}),D={access_token:k,token_type:"Bearer",expires_in:u,refresh_token:S,scope:E.join(" ")};return s(e,D,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(o){return e.get("logger")?.warn("MCP token exchange failed: %s",o instanceof Error?o.message:String(o)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const n=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:n},500)}}}async function x(r,e){const t=await le(e||{},{[v.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:n,header:i}=C.decode(r);if(n[U.CLAIM]!==U.VALUE)return{payload:n,kid:i.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function N(r){return G("sha256").update(r).digest("base64url")}async function Fe(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONSENT,session_hash:N(r.sessionToken),context_hash:N(r.contextToken),iat:e,exp:e+Me},m,A.HS256)}async function Ge(r,e){try{await C.verify(r,m,A.HS256)}catch{return!1}const{payload:t}=C.decode(r);return t.type===O.MCP_CONSENT&&t.session_hash===N(e.sessionToken)&&t.context_hash===N(e.contextToken)}function ee(r,e){return async t=>{const n=new URL(t.req.url);let i=n.searchParams.get("context");if(!i&&n.pathname.startsWith(w(`${I.MCP_CALLBACK}/`))){const c=n.pathname.split("/");i=c[c.length-1]}if(!i)return t.text("Missing context parameter",400);let a=null;try{const c=await De(i);a=c.mcpSessionId||null;const o=c.originalRedirectUri,d=Z(o);if(!c.isMcpFlow||!o||!d)throw new Error("Invalid MCP context");const p=H(t,v.AUTHORIZATION),l=p?await x(p,r.getConfig().ssoDirect):void 0;return!p||!l?(y.sendMcpAuthorizationFailedMessage([{...M(a),error:"Authentication required",error_details:null}]),t.text("Authentication required",401)):await e(t,{contextToken:i,mcpContext:c,redirectUri:o,redirectUrl:d,sessionToken:p,session:l})}catch(c){const o=Je(c);return y.sendMcpAuthorizationFailedMessage([{...M(a),...o}]),t.text(`Invalid MCP callback: ${o.error}`,400)}}}async function re(r,e,{mcpContext:t,redirectUri:n,redirectUrl:i,sessionToken:a,session:c}){const o=H(r,v.IDP_ACCESS_TOKEN),d=H(r,v.IDP_REFRESH_TOKEN),p=await ae({idToken:a,idpId:c.idpId??X(e.getConfig().ssoDirect),idpAccessTokenEnc:o?J(o,m):void 0,idpRefreshTokenEnc:d?V(d,m):void 0,clientId:t.mcpClientId||"",redirectUri:n,codeChallenge:t.mcpCodeChallenge||void 0,codeChallengeMethod:t.mcpCodeChallengeMethod||void 0,ttlSec:600}),l=new URL(i);return l.searchParams.set("code",p),t.mcpState&&l.searchParams.set("state",t.mcpState),y.sendMcpAuthorizationCompletedMessage([{...M(t.mcpSessionId||null),redirect_uri:n}]),r.redirect(l.toString())}function Ke(r,{mcpContext:e,redirectUrl:t}){const n=new URL(t);return n.searchParams.set("error","access_denied"),e.mcpState&&n.searchParams.set("state",e.mcpState),y.sendMcpAuthorizationFailedMessage([{...M(e.mcpSessionId||null),error:"access_denied",error_details:null}]),r.redirect(n.toString())}function Be({payload:r}){return[r.email,r.name,r.sub].find(e=>typeof e=="string"&&e.length>0)}function te({mcpContext:r,redirectUri:e,session:t}){const{sub:n}=t.payload;if(!(!j()||typeof n!="string"||n.length===0))return{idpId:t.idpId,subject:n,clientId:r.mcpClientId||"",redirectUri:e}}function ne(r,e){const{seo:t,colorMode:n}=e.getConfig(),{logo:i}=e.getGlobalData();return{...fe({seo:t,logo:i,colorMode:n}),host:new URL(L(r)).host}}function Sr(r){return ee(r,async(e,t)=>{const n=te(t);if(Y(t.redirectUrl)||n&&await Pe(await r.getKv(),n))return re(e,r,t);const{mcpClientId:i,mcpClientName:a}=t.mcpContext,c=ye({site:ne(e,r),user:Be(t.session),clientName:a||void 0,clientIdHost:z(i)?new URL(i).host:void 0,redirectUrl:t.redirectUrl,scopes:E,accessLifetimeHours:B/3600,rememberDays:n?Ae:void 0,consentToken:await Fe(t)});return new Response(c,{status:200,headers:W})})}function Ir(r){return ee(r,async(e,t)=>{const n=await e.req.formData(),i=n.get("consent");if(typeof i!="string"||!await Ge(i,t))return new Response(Ee(ne(e,r)),{status:403,headers:W});if(n.get("decision")==="allow"){const a=te(t);return a&&await Oe(await r.getKv(),a),re(e,r,t)}return Ke(e,t)})}function Je(r){return r instanceof Error&&[me,ge,Ce,Te,ke].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{F as SUPPORTED_MCP_GRANT_TYPES,Ne as createMcpContextToken,Tr as mcpAuthorizationHandler,Sr as mcpCallbackHandler,Ir as mcpConsentHandler,Cr as mcpDynamicClientRegistrationHandler,gr as mcpOAuthAuthorizationServerHandler,mr as mcpOAuthProtectedResourceHandler,kr as mcpTokenPortalHandler,X as resolveMcpIdpId,De as verifyAndParseMcpContextToken,Le as verifyPkce};
@@ -43,9 +43,10 @@ export type PlanGates = {
43
43
  [FEATURE.mcpRequestRatePerMin]: number;
44
44
  [FEATURE.mcpRequestQuotaPerDay]: number;
45
45
  [FEATURE.cookieConsent]: boolean;
46
+ [FEATURE.sdkGeneration]: boolean;
46
47
  };
47
48
  export type Entitlements = {
48
49
  [key in Feature]: boolean | number | string;
49
50
  };
50
- export type ProductPlan = 'pro' | 'enterprise';
51
+ export type PricingTier = 'pro' | 'scale' | 'enterprise';
51
52
  //# sourceMappingURL=entitlements.d.ts.map
@@ -23,6 +23,7 @@ export type GlobalData = UiAccessibleConfig & {
23
23
  locales: {
24
24
  code: string;
25
25
  name: string;
26
+ showMissingTranslationNotice?: boolean;
26
27
  }[];
27
28
  };
28
29
  removeAttribution?: boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Builds a developer onboarding API path under the configured path prefix.
3
+ *
4
+ * @param path - The path relative to the api-keys API base (e.g., "/meta", "/apps/:id").
5
+ * @returns The api-keys API path, prefixed with `REDOCLY_PREFIX_PATHS` when it is set.
6
+ *
7
+ * @example
8
+ * apiKeysUrl('/apps/my-app'); // "/api/api-keys/apps/my-app" (or "/prefix/api/api-keys/apps/my-app")
9
+ */
10
+ export declare function apiKeysUrl(path: string): string;
11
+ //# sourceMappingURL=api-keys-url.d.ts.map
@@ -0,0 +1 @@
1
+ import{withPathPrefix as r}from"@redocly/theme/core/utils";function e(i){return r(`/api/api-keys${i}`)}export{e as apiKeysUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.139.0-next.1",
3
+ "version": "0.139.0-next.10",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,13 +33,13 @@
33
33
  "@opentelemetry/sdk-trace-web": "2.8.0",
34
34
  "@opentelemetry/semantic-conventions": "1.40.0",
35
35
  "@redocly/ajv": "8.18.0",
36
- "@redocly/api-docs": "0.4.0-next.1",
37
- "@redocly/config": "0.65.0",
38
- "@redocly/openapi-core": "2.62.1",
36
+ "@redocly/api-docs": "0.4.0-next.10",
37
+ "@redocly/config": "0.67.0",
38
+ "@redocly/openapi-core": "2.64.0",
39
39
  "@redocly/portal-legacy-ui": "0.19.0",
40
- "@redocly/portal-plugin-mock-server": "0.23.0",
41
- "@redocly/realm-asyncapi-sdk": "0.16.0",
42
- "@redocly/theme": "0.71.0-next.0",
40
+ "@redocly/portal-plugin-mock-server": "0.24.0-next.2",
41
+ "@redocly/realm-asyncapi-sdk": "0.17.0-next.1",
42
+ "@redocly/theme": "0.71.0-next.6",
43
43
  "@shikijs/transformers": "3.21.0",
44
44
  "@tanstack/react-query": "5.62.3",
45
45
  "@tanstack/react-table": "8.21.3",
@@ -1,3 +0,0 @@
1
- import type { SearchItemData } from '@redocly/theme/core/types';
2
- export declare function processDocuments(documents: Record<string, SearchItemData[]>, outdir: string): string;
3
- //# sourceMappingURL=utils.d.ts.map
@@ -1,11 +0,0 @@
1
- import c from"node:fs";import f from"node:path";import{MAX_DOCUMENTS_PER_CATEGORY as $}from"../../../constants.js";function j(m,u){return Object.entries(m).map(([h,s])=>{if(!s||s.length===0)return"";const x=s.slice(0,$).map(d=>{const{document:t,highlight:o}=d,p=o?.title||(Array.isArray(t.title)?t.title[0]:t.title);let n=`Document: ${t.title}`;n+=`### [${p}](${t.url})
2
-
3
- `;let r;if(t.url)try{let e=t.url.startsWith("/")?t.url.slice(1):t.url;const i=e.indexOf("#");i!==-1&&(e=e.substring(0,i));const l=f.extname(e);l&&(e=e.slice(0,-l.length));const y=e+".md",a=f.join(u,y);c.existsSync(a)&&(r=c.readFileSync(a,"utf8"))}catch{}return r||(r=o?.text||(Array.isArray(t.text)?t.text[0]:t.text)),n+=r,t.facets&&(n+=`
4
-
5
- **Categories:**
6
- `,Object.entries(t.facets).forEach(([e,i])=>{n+=`- ${e}: ${i}
7
- `})),n});return`## ${h}
8
-
9
- ${x}`}).join(`
10
-
11
- `).trim()}export{j as processDocuments};