@scalar/api-client 3.4.0 → 3.5.0
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.
- package/CHANGELOG.md +17 -0
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.d.ts +69 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.js +75 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.js.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.d.ts +17 -0
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js +43 -44
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/oauth.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/package.json +14 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @scalar/api-client
|
|
2
2
|
|
|
3
|
+
## 3.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: support version switching for regsitry documents
|
|
8
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: ensure teams can have only one workspace and create default workspace of the team on demand
|
|
9
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): chore: migrate api-client from namespaces to teamSlug and convert team workspaces
|
|
10
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: add empty state for the team workspace
|
|
11
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: make breadcrumbs navigable, add workspace selector fallback, and support team logo slots
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): feat: allow masking all temp urls
|
|
16
|
+
- [#9045](https://github.com/scalar/scalar/pull/9045): fix: hide sidebar group label when we have a single group
|
|
17
|
+
- [#9034](https://github.com/scalar/scalar/pull/9034): Surface actual OAuth error messages instead of generic failure messages
|
|
18
|
+
- [#9043](https://github.com/scalar/scalar/pull/9043): chore: move test documents to cdn
|
|
19
|
+
|
|
3
20
|
## 3.4.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/request-block/RequestBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components'\nimport { canMethodHaveBody } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { REGEX } from '@scalar/helpers/regex/regex-helpers'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n filterGlobalCookie,\n getEnvironmentVariables,\n getExample,\n getResolvedUrl,\n type MergedSecuritySchemes,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId, watch } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ClientOptionGroup } from '@/v2/blocks/operation-code-sample'\nimport RequestBody from '@/v2/blocks/request-block/components/RequestBody.vue'\nimport RequestCodeSnippet from '@/v2/blocks/request-block/components/RequestCodeSnippet.vue'\nimport RequestParams from '@/v2/blocks/request-block/components/RequestParams.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { createParameterHandlers } from '@/v2/blocks/request-block/helpers/create-parameter-handlers'\nimport { getParameterSchema } from '@/v2/blocks/request-block/helpers/get-parameter-schema'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { isParamDisabled } from '@/v2/blocks/request-block/helpers/is-param-disabled'\nimport { AuthSelector } from '@/v2/blocks/scalar-auth-selector-block'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\ntype Filter =\n | 'All'\n | 'Auth'\n | 'Variables'\n | 'Cookies'\n | 'Headers'\n | 'Query'\n | 'Body'\n\nexport type RequestBlockProps = {\n authMeta: AuthMeta\n clientOptions: ClientOptionGroup[]\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n exampleKey: string\n workspaceCookies: XScalarCookie[]\n documentCookies: XScalarCookie[]\n layout: ClientLayout\n method: HttpMethod\n operation: OperationObject\n path: string\n plugins: ClientPlugin[]\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n requestBodyCompositionSelection?: Record<string, number>\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n selectedSecurity: SelectedSecurity\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n server: ServerObject | null\n defaultHeaders: Record<string, string>\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}\n\nconst {\n authMeta = { type: 'document' },\n clientOptions,\n environment,\n eventBus,\n exampleKey,\n workspaceCookies,\n documentCookies,\n layout,\n method,\n operation,\n path,\n plugins,\n proxyUrl,\n requestBodyCompositionSelection,\n securityRequirements,\n securitySchemes,\n selectedClient,\n selectedSecurity,\n selectedSecuritySchemes,\n server,\n defaultHeaders: autoGeneratedHeaders,\n} = defineProps<RequestBlockProps>()\n\n/** Operation metadata used across event emissions */\nconst meta = computed(() => ({\n method,\n path,\n exampleKey,\n}))\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters?.map((param) => getResolvedRef(param)) ?? [],\n 'in',\n (param) => {\n const example = getExample(param, exampleKey, undefined)\n\n return {\n name: param.name,\n value: example?.value ?? '',\n description: param.description,\n schema: getParameterSchema(param),\n isRequired: param.required,\n isDisabled: isParamDisabled(param, example),\n originalParameter: param,\n } as TableRow\n },\n ),\n)\n\n// Generate a reverse map for fast lookup of headers by the name\nconst headersMap = computed(() =>\n groupBy(\n sections.value.header?.map((it) => ({\n ...it,\n name: it.name.toLowerCase(),\n })) ?? [],\n 'name',\n ),\n)\n\nconst defaultHeaders = computed(() => {\n const disableParameters =\n operation['x-scalar-disable-parameters']?.['default-headers']?.[\n exampleKey\n ] ?? {}\n\n const isOverridden = (name: string) => {\n const header = headersMap.value[name.toLowerCase()]?.[0]\n\n if (!header) {\n return false\n }\n\n return !header.isDisabled\n }\n\n return Object.entries(autoGeneratedHeaders).map(([name, value]) => {\n return {\n name,\n value,\n schema: undefined,\n isOverridden: isOverridden(name),\n isReadonly: true,\n isDisabled: disableParameters[name.toLowerCase()] ?? false,\n } satisfies TableRow\n })\n})\n\nconst headers = computed(() => [\n ...defaultHeaders.value,\n ...(sections.value.header ?? []),\n])\n\nconst defaultCookies = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({\n server,\n path,\n })\n const url = replaceEnvVariables(resolvedUrl, environmentVariables)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n const transform = (\n cookie: XScalarCookie,\n location: 'document' | 'workspace',\n ) => {\n return {\n name: cookie.name,\n value: cookie.value,\n globalRoute: { page: location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[cookie.name.toLowerCase()] ?? false,\n } satisfies TableRow\n }\n\n const globalCookies = [\n {\n location: 'workspace',\n cookies: workspaceCookies,\n },\n {\n location: 'document',\n cookies: documentCookies,\n },\n ] as const\n\n return globalCookies.flatMap(({ location, cookies }) => {\n return cookies\n .filter((cookie) =>\n filterGlobalCookie({\n cookie,\n url,\n disabledGlobalCookies: {},\n }),\n )\n .map((cookie) => transform(cookie, location))\n })\n})\n\nconst cookies = computed(() => [\n ...(defaultCookies.value ?? []),\n ...(sections.value.cookie ?? []),\n])\n\n/** Currently selected filter for the request sections */\nconst selectedFilter = ref<Filter>('All')\n\n/** Available operation sections */\nconst OPERATION_SECTIONS: readonly Filter[] = [\n 'Auth',\n 'Variables',\n 'Cookies',\n 'Headers',\n 'Query',\n 'Body',\n] as const\n\n/**\n * Pre-generated stable IDs for all possible filter sections.\n * These are created once at setup time to avoid regenerating IDs on re-render.\n */\nconst sectionIds: Record<Filter, string> = {\n All: useId(),\n Auth: useId(),\n Variables: useId(),\n Cookies: useId(),\n Headers: useId(),\n Query: useId(),\n Body: useId(),\n}\n\n/** Filters available based on operation state */\nconst filters = computed<Filter[]>(() => {\n const availableFilters = new Set<Filter>(['All', ...OPERATION_SECTIONS])\n\n if (!sections.value.path?.length) {\n availableFilters.delete('Variables')\n }\n if (!canMethodHaveBody(method)) {\n availableFilters.delete('Body')\n }\n if (isAuthHidden.value) {\n availableFilters.delete('Auth')\n }\n\n return [...availableFilters]\n})\n\n/**\n * Map available filters to their pre-generated stable IDs.\n * Only includes IDs for filters that are currently available.\n */\nconst filterIds = computed(\n () =>\n Object.fromEntries(\n filters.value.map((section) => [section, sectionIds[section]]),\n ) as Record<Filter, string>,\n)\n\n/**\n * Hide auth selector in readonly mode when no security schemes are defined.\n * This keeps the UI clean when there are no authentication options available.\n */\nconst isAuthHidden = computed(\n () => layout === 'modal' && !Object.keys(securitySchemes ?? {}).length,\n)\n\n/**\n * Keep auth available for unauthenticated operations, but collapse it by default\n * in readonly modal layouts unless a requirement or manual selection exists.\n */\nconst isAuthDefaultOpen = computed(\n () =>\n layout !== 'modal' ||\n Boolean(\n securityRequirements?.length || selectedSecurity.selectedSchemes.length,\n ),\n)\n\n/** Get a sensible placeholder for the request name input */\nconst requestNamePlaceholder = computed(() => {\n if (operation.summary) {\n return operation.summary\n }\n const cleanPath = path.replace(REGEX.PROTOCOL, '')\n return cleanPath || 'Request Name'\n})\n\n/** Check if the section should be shown based on the selected filter */\nconst isSectionVisible = (section: Filter): boolean => {\n return selectedFilter.value === 'All' || selectedFilter.value === section\n}\n\n/**\n * Reset filter to 'All' if Body filter is selected but method changes to one that cannot have a body.\n * This prevents showing an empty Body section when switching methods.\n */\nwatch(\n () => method,\n (newMethod) => {\n if (selectedFilter.value === 'Body' && !canMethodHaveBody(newMethod)) {\n selectedFilter.value = 'All'\n }\n },\n)\n\n/** Handle operation summary updates */\nconst handleSummaryUpdate = (event: Event): void => {\n const summary = (event.target as HTMLInputElement).value\n eventBus.emit('operation:update:meta', {\n meta: meta.value,\n payload: { summary: summary.trim() },\n })\n}\n\n/** Parameter handlers */\nconst parameterHandlers = computed(() => ({\n path: createParameterHandlers('path', eventBus, meta.value, {\n context: sections.value.path ?? [],\n }),\n cookie: createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n header: createParameterHandlers('header', eventBus, meta.value, {\n context: headers.value,\n defaultParameters: defaultHeaders.value.length,\n }),\n query: createParameterHandlers('query', eventBus, meta.value, {\n context: sections.value.query ?? [],\n }),\n}))\n\n/** Handle request body content type update */\nconst handleUpdateContentType = (payload: { value: string }): void =>\n eventBus.emit('operation:update:requestBody:contentType', {\n payload: { contentType: payload.value },\n meta: meta.value,\n })\n\n/** Handle request body value update */\nconst handleUpdateBodyValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey =\n typeof payload === 'string'\n ? `update:requestBody:value-${contentType}`\n : undefined\n\n eventBus.emit(\n 'operation:update:requestBody:value',\n {\n payload,\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\n/** Handle request body value update */\nconst handleUpdateBodyFormValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey = `update:requestBody:${contentType}-form-value`\n\n eventBus.emit(\n 'operation:update:requestBody:formValue',\n {\n payload: payload.map((row) => unpackProxyObject(row, { depth: 1 })),\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\nconst labelRequestNameId = useId()\n\nconst globalCookies = computed(() => [...workspaceCookies, ...documentCookies])\n\n/** Allow updating the operation extensions for the plugins */\nconst updateOperationExtension = (\n payload: ApiReferenceEvents['operation:update:extension']['payload'],\n): void =>\n eventBus.emit('operation:update:extension', { payload, meta: meta.value })\n</script>\n<template>\n <ViewLayoutSection :aria-label=\"`Request: ${operation.summary}`\">\n <template #title>\n <div\n class=\"group pointer-events-none flex flex-1 items-center gap-1 lg:pr-24\">\n <label\n v-if=\"layout !== 'modal'\"\n class=\"pointer-events-auto absolute top-0 left-0 h-full w-full cursor-text opacity-0\"\n :for=\"labelRequestNameId\" />\n <input\n v-if=\"layout !== 'modal'\"\n :id=\"labelRequestNameId\"\n class=\"text-c-1 group-hover-input pointer-events-auto relative z-10 -ml-0.5 h-8 w-full rounded pl-1.25 has-[:focus-visible]:outline md:-ml-1.25\"\n :placeholder=\"requestNamePlaceholder\"\n :value=\"operation.summary\"\n @blur=\"handleSummaryUpdate\" />\n <span\n v-else\n class=\"text-c-1 flex h-8 items-center\">\n {{ operation.summary }}\n </span>\n </div>\n <SectionFilter\n v-model=\"selectedFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\" />\n </template>\n\n <div\n :id=\"filterIds.All\"\n class=\"request-section-content custom-scroll relative flex flex-1 flex-col\"\n :role=\"selectedFilter === 'All' ? 'tabpanel' : 'none'\">\n <!-- Auth Selector -->\n <AuthSelector\n v-show=\"isSectionVisible('Auth') && !isAuthHidden\"\n :id=\"filterIds.Auth\"\n :createAnySecurityScheme=\"layout !== 'modal'\"\n :defaultOpen=\"isAuthDefaultOpen\"\n :environment\n :eventBus\n :meta=\"authMeta\"\n :options\n :proxyUrl\n :securityRequirements\n :securitySchemes\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n title=\"Authentication\" />\n\n <!-- Variables (Path Parameters) -->\n <RequestParams\n v-show=\"isSectionVisible('Variables') && sections.path?.length\"\n :id=\"filterIds.Variables\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.path ?? []\"\n :showAddRowPlaceholder=\"false\"\n title=\"Variables\"\n v-on=\"parameterHandlers.path\" />\n\n <!-- Cookies -->\n <RequestParams\n v-show=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"cookies ?? []\"\n :showAddRowPlaceholder=\"true\"\n title=\"Cookies\"\n v-on=\"parameterHandlers.cookie\" />\n\n <!-- Headers -->\n <RequestParams\n v-show=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"headers ?? []\"\n title=\"Headers\"\n v-on=\"parameterHandlers.header\" />\n\n <!-- Query Parameters -->\n <RequestParams\n v-show=\"isSectionVisible('Query')\"\n :id=\"filterIds.Query\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.query ?? []\"\n title=\"Query Parameters\"\n v-on=\"parameterHandlers.query\" />\n\n <!-- Request Body -->\n <RequestBody\n v-show=\"isSectionVisible('Body') && canMethodHaveBody(method)\"\n :id=\"filterIds.Body\"\n :environment\n :exampleKey\n :requestBody=\"getResolvedRef(operation.requestBody)\"\n :requestBodyCompositionSelection\n title=\"Request Body\"\n @update:contentType=\"handleUpdateContentType\"\n @update:formValue=\"handleUpdateBodyFormValue\"\n @update:value=\"handleUpdateBodyValue\" />\n\n <!-- Inject request section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.request.component\"\n v-if=\"plugin?.components?.request\"\n v-show=\"selectedFilter === 'All'\"\n :operation\n v-bind=\"plugin.components.request.additionalProps\"\n @operation:update:extension=\"updateOperationExtension\" />\n </ScalarErrorBoundary>\n\n <!-- Spacer -->\n <div class=\"flex grow\" />\n <!-- Code Snippet -->\n <RequestCodeSnippet\n v-show=\"selectedFilter === 'All'\"\n :clientOptions\n :eventBus\n :globalCookies\n integration=\"client\"\n :method\n :operation\n :path\n :securitySchemes=\"selectedSecuritySchemes\"\n :selectedClient\n :selectedContentType=\"\n getResolvedRef(operation.requestBody)?.[\n 'x-scalar-selected-content-type'\n ]?.[exampleKey]\n \"\n :selectedServer=\"server ?? undefined\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.request-section-content {\n --scalar-border-width: 0.5px;\n}\n.request-section-content-filter {\n box-shadow: 0 -10px 0 10px var(--scalar-background-1);\n}\n.request-item:focus-within .request-meta-buttons {\n opacity: 1;\n}\n.group-hover-input {\n border-width: var(--scalar-border-width);\n border-color: transparent;\n}\n.group:hover .group-hover-input {\n background: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n border-color: var(--scalar-border-color);\n}\n.group-hover-input:focus {\n background: transparent !important;\n border-color: var(--scalar-border-color) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RequestBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/request-block/RequestBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components'\nimport { canMethodHaveBody } from '@scalar/helpers/http/can-method-have-body'\nimport type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { REGEX } from '@scalar/helpers/regex/regex-helpers'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n ApiReferenceEvents,\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n filterGlobalCookie,\n getEnvironmentVariables,\n getExample,\n getResolvedUrl,\n type MergedSecuritySchemes,\n type SecuritySchemeObjectSecret,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies'\nimport type {\n OpenApiDocument,\n OperationObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId, watch } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ClientOptionGroup } from '@/v2/blocks/operation-code-sample'\nimport RequestBody from '@/v2/blocks/request-block/components/RequestBody.vue'\nimport RequestCodeSnippet from '@/v2/blocks/request-block/components/RequestCodeSnippet.vue'\nimport RequestParams from '@/v2/blocks/request-block/components/RequestParams.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport { createParameterHandlers } from '@/v2/blocks/request-block/helpers/create-parameter-handlers'\nimport { getParameterSchema } from '@/v2/blocks/request-block/helpers/get-parameter-schema'\nimport { groupBy } from '@/v2/blocks/request-block/helpers/group-by'\nimport { isParamDisabled } from '@/v2/blocks/request-block/helpers/is-param-disabled'\nimport { AuthSelector } from '@/v2/blocks/scalar-auth-selector-block'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport type { ClientLayout } from '@/v2/types/layout'\n\ntype Filter =\n | 'All'\n | 'Auth'\n | 'Variables'\n | 'Cookies'\n | 'Headers'\n | 'Query'\n | 'Body'\n\nexport type RequestBlockProps = {\n authMeta: AuthMeta\n clientOptions: ClientOptionGroup[]\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n exampleKey: string\n workspaceCookies: XScalarCookie[]\n documentCookies: XScalarCookie[]\n layout: ClientLayout\n method: HttpMethod\n operation: OperationObject\n path: string\n plugins: ClientPlugin[]\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n requestBodyCompositionSelection?: Record<string, number>\n selectedClient: WorkspaceStore['workspace']['x-scalar-default-client']\n selectedSecurity: SelectedSecurity\n selectedSecuritySchemes: SecuritySchemeObjectSecret[]\n server: ServerObject | null\n defaultHeaders: Record<string, string>\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}\n\nconst {\n authMeta = { type: 'document' },\n clientOptions,\n environment,\n eventBus,\n exampleKey,\n workspaceCookies,\n documentCookies,\n layout,\n method,\n operation,\n path,\n plugins,\n proxyUrl,\n requestBodyCompositionSelection,\n securityRequirements,\n securitySchemes,\n selectedClient,\n selectedSecurity,\n selectedSecuritySchemes,\n server,\n defaultHeaders: autoGeneratedHeaders,\n} = defineProps<RequestBlockProps>()\n\n/** Operation metadata used across event emissions */\nconst meta = computed(() => ({\n method,\n path,\n exampleKey,\n}))\n\n/** Parameters grouped by type (path, query, header, cookie) */\nconst sections = computed(() =>\n groupBy(\n operation.parameters?.map((param) => getResolvedRef(param)) ?? [],\n 'in',\n (param) => {\n const example = getExample(param, exampleKey, undefined)\n\n return {\n name: param.name,\n value: example?.value ?? '',\n description: param.description,\n schema: getParameterSchema(param),\n isRequired: param.required,\n isDisabled: isParamDisabled(param, example),\n originalParameter: param,\n } as TableRow\n },\n ),\n)\n\n// Generate a reverse map for fast lookup of headers by the name\nconst headersMap = computed(() =>\n groupBy(\n sections.value.header?.map((it) => ({\n ...it,\n name: it.name.toLowerCase(),\n })) ?? [],\n 'name',\n ),\n)\n\nconst defaultHeaders = computed(() => {\n const disableParameters =\n operation['x-scalar-disable-parameters']?.['default-headers']?.[\n exampleKey\n ] ?? {}\n\n const isOverridden = (name: string) => {\n const header = headersMap.value[name.toLowerCase()]?.[0]\n\n if (!header) {\n return false\n }\n\n return !header.isDisabled\n }\n\n return Object.entries(autoGeneratedHeaders).map(([name, value]) => {\n return {\n name,\n value,\n schema: undefined,\n isOverridden: isOverridden(name),\n isReadonly: true,\n isDisabled: disableParameters[name.toLowerCase()] ?? false,\n } satisfies TableRow\n })\n})\n\nconst headers = computed(() => [\n ...defaultHeaders.value,\n ...(sections.value.header ?? []),\n])\n\nconst defaultCookies = computed(() => {\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrl = getResolvedUrl({\n server,\n path,\n })\n const url = replaceEnvVariables(resolvedUrl, environmentVariables)\n\n const disabledGlobalCookies =\n operation['x-scalar-disable-parameters']?.['global-cookies']?.[\n exampleKey\n ] ?? {}\n\n const transform = (\n cookie: XScalarCookie,\n location: 'document' | 'workspace',\n ) => {\n return {\n name: cookie.name,\n value: cookie.value,\n globalRoute: { page: location, path: 'cookies' } as const,\n isReadonly: true,\n isDisabled: disabledGlobalCookies[cookie.name.toLowerCase()] ?? false,\n } satisfies TableRow\n }\n\n const globalCookies = [\n {\n location: 'workspace',\n cookies: workspaceCookies,\n },\n {\n location: 'document',\n cookies: documentCookies,\n },\n ] as const\n\n return globalCookies.flatMap(({ location, cookies }) => {\n return cookies\n .filter((cookie) =>\n filterGlobalCookie({\n cookie,\n url,\n disabledGlobalCookies: {},\n }),\n )\n .map((cookie) => transform(cookie, location))\n })\n})\n\nconst cookies = computed(() => [\n ...(defaultCookies.value ?? []),\n ...(sections.value.cookie ?? []),\n])\n\n/** Currently selected filter for the request sections */\nconst selectedFilter = ref<Filter>('All')\n\n/** Available operation sections */\nconst OPERATION_SECTIONS: readonly Filter[] = [\n 'Auth',\n 'Variables',\n 'Cookies',\n 'Headers',\n 'Query',\n 'Body',\n] as const\n\n/**\n * Pre-generated stable IDs for all possible filter sections.\n * These are created once at setup time to avoid regenerating IDs on re-render.\n */\nconst sectionIds: Record<Filter, string> = {\n All: useId(),\n Auth: useId(),\n Variables: useId(),\n Cookies: useId(),\n Headers: useId(),\n Query: useId(),\n Body: useId(),\n}\n\n/** Filters available based on operation state */\nconst filters = computed<Filter[]>(() => {\n const availableFilters = new Set<Filter>(['All', ...OPERATION_SECTIONS])\n\n if (!sections.value.path?.length) {\n availableFilters.delete('Variables')\n }\n if (!canMethodHaveBody(method)) {\n availableFilters.delete('Body')\n }\n if (isAuthHidden.value) {\n availableFilters.delete('Auth')\n }\n\n return [...availableFilters]\n})\n\n/**\n * Map available filters to their pre-generated stable IDs.\n * Only includes IDs for filters that are currently available.\n */\nconst filterIds = computed(\n () =>\n Object.fromEntries(\n filters.value.map((section) => [section, sectionIds[section]]),\n ) as Record<Filter, string>,\n)\n\n/**\n * Hide auth selector in readonly mode when no security schemes are defined.\n * This keeps the UI clean when there are no authentication options available.\n */\nconst isAuthHidden = computed(\n () => layout === 'modal' && !Object.keys(securitySchemes ?? {}).length,\n)\n\n/**\n * Keep auth available for unauthenticated operations, but collapse it by default\n * in readonly modal layouts unless a requirement or manual selection exists.\n */\nconst isAuthDefaultOpen = computed(\n () =>\n layout !== 'modal' ||\n Boolean(\n securityRequirements?.length || selectedSecurity.selectedSchemes.length,\n ),\n)\n\n/** Get a sensible placeholder for the request name input */\nconst requestNamePlaceholder = computed(() => {\n if (operation.summary) {\n return operation.summary\n }\n const cleanPath = path.replace(REGEX.PROTOCOL, '')\n return cleanPath || 'Request Name'\n})\n\n/** Check if the section should be shown based on the selected filter */\nconst isSectionVisible = (section: Filter): boolean => {\n return selectedFilter.value === 'All' || selectedFilter.value === section\n}\n\n/**\n * Reset filter to 'All' if Body filter is selected but method changes to one that cannot have a body.\n * This prevents showing an empty Body section when switching methods.\n */\nwatch(\n () => method,\n (newMethod) => {\n if (selectedFilter.value === 'Body' && !canMethodHaveBody(newMethod)) {\n selectedFilter.value = 'All'\n }\n },\n)\n\n/** Handle operation summary updates */\nconst handleSummaryUpdate = (event: Event): void => {\n const summary = (event.target as HTMLInputElement).value\n eventBus.emit('operation:update:meta', {\n meta: meta.value,\n payload: { summary: summary.trim() },\n })\n}\n\n/** Parameter handlers */\nconst parameterHandlers = computed(() => ({\n path: createParameterHandlers('path', eventBus, meta.value, {\n context: sections.value.path ?? [],\n }),\n cookie: createParameterHandlers('cookie', eventBus, meta.value, {\n context: cookies.value ?? [],\n globalParameters: defaultCookies.value.length,\n }),\n header: createParameterHandlers('header', eventBus, meta.value, {\n context: headers.value,\n defaultParameters: defaultHeaders.value.length,\n }),\n query: createParameterHandlers('query', eventBus, meta.value, {\n context: sections.value.query ?? [],\n }),\n}))\n\n/** Handle request body content type update */\nconst handleUpdateContentType = (payload: { value: string }): void =>\n eventBus.emit('operation:update:requestBody:contentType', {\n payload: { contentType: payload.value },\n meta: meta.value,\n })\n\n/** Handle request body value update */\nconst handleUpdateBodyValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey =\n typeof payload === 'string'\n ? `update:requestBody:value-${contentType}`\n : undefined\n\n eventBus.emit(\n 'operation:update:requestBody:value',\n {\n payload,\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\n/** Handle request body value update */\nconst handleUpdateBodyFormValue = ({\n payload,\n contentType,\n}: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n>): void => {\n const debounceKey = `update:requestBody:${contentType}-form-value`\n\n eventBus.emit(\n 'operation:update:requestBody:formValue',\n {\n payload: payload.map((row) => unpackProxyObject(row, { depth: 1 })),\n contentType,\n meta: meta.value,\n },\n {\n debounceKey,\n },\n )\n}\n\nconst labelRequestNameId = useId()\n\nconst globalCookies = computed(() => [...workspaceCookies, ...documentCookies])\n\n/** Allow updating the operation extensions for the plugins */\nconst updateOperationExtension = (\n payload: ApiReferenceEvents['operation:update:extension']['payload'],\n): void =>\n eventBus.emit('operation:update:extension', { payload, meta: meta.value })\n</script>\n<template>\n <ViewLayoutSection :aria-label=\"`Request: ${operation.summary}`\">\n <template #title>\n <div\n class=\"group pointer-events-none flex flex-1 items-center gap-1 lg:pr-24\">\n <label\n v-if=\"layout !== 'modal'\"\n class=\"pointer-events-auto absolute top-0 left-0 h-full w-full cursor-text opacity-0\"\n :for=\"labelRequestNameId\" />\n <input\n v-if=\"layout !== 'modal'\"\n :id=\"labelRequestNameId\"\n class=\"text-c-1 group-hover-input pointer-events-auto relative z-10 -ml-0.5 h-8 w-full rounded pl-1.25 has-[:focus-visible]:outline md:-ml-1.25\"\n :placeholder=\"requestNamePlaceholder\"\n :value=\"operation.summary\"\n @blur=\"handleSummaryUpdate\" />\n <span\n v-else\n class=\"text-c-1 flex h-8 items-center\">\n {{ operation.summary }}\n </span>\n </div>\n <SectionFilter\n v-model=\"selectedFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\" />\n </template>\n\n <div\n :id=\"filterIds.All\"\n class=\"request-section-content custom-scroll relative flex flex-1 flex-col\"\n :role=\"selectedFilter === 'All' ? 'tabpanel' : 'none'\">\n <!-- Auth Selector -->\n <AuthSelector\n v-show=\"isSectionVisible('Auth') && !isAuthHidden\"\n :id=\"filterIds.Auth\"\n :createAnySecurityScheme=\"layout !== 'modal'\"\n :defaultOpen=\"isAuthDefaultOpen\"\n :environment\n :eventBus\n :meta=\"authMeta\"\n :options\n :proxyUrl\n :securityRequirements\n :securitySchemes\n :selectedSecurity\n :selectedSecuritySchemes\n :server\n title=\"Authentication\" />\n\n <!-- Variables (Path Parameters) -->\n <RequestParams\n v-show=\"isSectionVisible('Variables') && sections.path?.length\"\n :id=\"filterIds.Variables\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.path ?? []\"\n :showAddRowPlaceholder=\"false\"\n title=\"Variables\"\n v-on=\"parameterHandlers.path\" />\n\n <!-- Cookies -->\n <RequestParams\n v-show=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"cookies ?? []\"\n :showAddRowPlaceholder=\"true\"\n title=\"Cookies\"\n v-on=\"parameterHandlers.cookie\" />\n\n <!-- Headers -->\n <RequestParams\n v-show=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"headers ?? []\"\n title=\"Headers\"\n v-on=\"parameterHandlers.header\" />\n\n <!-- Query Parameters -->\n <RequestParams\n v-show=\"isSectionVisible('Query')\"\n :id=\"filterIds.Query\"\n :environment\n :eventBus\n :exampleKey\n :rows=\"sections.query ?? []\"\n title=\"Query Parameters\"\n v-on=\"parameterHandlers.query\" />\n\n <!-- Request Body -->\n <RequestBody\n v-show=\"isSectionVisible('Body') && canMethodHaveBody(method)\"\n :id=\"filterIds.Body\"\n :environment\n :exampleKey\n :requestBody=\"getResolvedRef(operation.requestBody)\"\n :requestBodyCompositionSelection\n title=\"Request Body\"\n @update:contentType=\"handleUpdateContentType\"\n @update:formValue=\"handleUpdateBodyFormValue\"\n @update:value=\"handleUpdateBodyValue\" />\n\n <!-- Inject request section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.request.component\"\n v-if=\"plugin?.components?.request\"\n v-show=\"selectedFilter === 'All'\"\n :operation\n v-bind=\"plugin.components.request.additionalProps\"\n @operation:update:extension=\"updateOperationExtension\" />\n </ScalarErrorBoundary>\n\n <!-- Spacer -->\n <div class=\"flex grow\" />\n <!-- Code Snippet -->\n <RequestCodeSnippet\n v-show=\"selectedFilter === 'All'\"\n :clientOptions\n :eventBus\n :globalCookies\n integration=\"client\"\n :method\n :operation\n :path\n :securitySchemes=\"selectedSecuritySchemes\"\n :selectedClient\n :selectedContentType=\"\n getResolvedRef(operation.requestBody)?.[\n 'x-scalar-selected-content-type'\n ]?.[exampleKey]\n \"\n :selectedServer=\"server ?? undefined\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.request-section-content {\n --scalar-border-width: 0.5px;\n}\n.request-section-content-filter {\n box-shadow: 0 -10px 0 10px var(--scalar-background-1);\n}\n.request-item:focus-within .request-meta-buttons {\n opacity: 1;\n}\n.group-hover-input {\n border-width: var(--scalar-border-width);\n border-color: transparent;\n}\n.group:hover .group-hover-input {\n background: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n border-color: var(--scalar-border-color);\n}\n.group-hover-input:focus {\n background: transparent !important;\n border-color: var(--scalar-border-color) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthSelector.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarComboboxMultiselect,\n ScalarIconButton,\n ScalarListboxCheckbox,\n useModal,\n type Icon,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components'\nimport { ScalarIconCaretDown, ScalarIconTrash } from '@scalar/icons'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n isAuthOptional,\n type MergedSecuritySchemes,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n OpenApiDocument,\n SecurityRequirementObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId } from 'vue'\n\nimport DeleteRequestAuthModal from '@/v2/blocks/scalar-auth-selector-block/components/DeleteRequestAuthModal.vue'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport {\n formatComplexScheme,\n formatScheme,\n getSecuritySchemeOptions,\n type SecuritySchemeOption,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/security-scheme'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n defaultOpen = true,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: boolean\n /** Creates a static disclosure that cannot be collapsed */\n isStatic?: boolean\n meta: AuthMeta\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n selectedSecurity: SelectedSecurity | undefined\n server: ServerObject | null\n title: string\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}>()\n\nconst titleId = useId()\nconst comboboxButtonRef = ref<typeof ScalarButtonType | null>(null)\nconst isDisclosureOpen = ref(false)\n\nconst deleteModal = useModal()\nconst schemeToDelete = ref<{\n label: string\n payload: SecurityRequirementObject\n} | null>(null)\n\n/**\n * Determines if authentication is required or optional\n *\n * Auth is optional when there is an empty security requirement and no complex requirements.\n * Complex requirements have multiple auth schemes combined (e.g., API key + OAuth).\n */\nconst authIndicator = computed<{ icon: Icon; text: string } | null>(() => {\n if (!securityRequirements?.length) {\n return null\n }\n\n const isOptional = isAuthOptional(securityRequirements)\n\n return {\n icon: isOptional ? 'Unlock' : 'Lock',\n text: isOptional ? 'Optional' : 'Required',\n }\n})\n\n/** All available auth scheme options for the dropdown */\nconst availableSchemeOptions = computed(() =>\n getSecuritySchemeOptions(\n securityRequirements ?? [],\n securitySchemes ?? {},\n selectedSecurity?.selectedSchemes ?? [],\n createAnySecurityScheme,\n ),\n)\n\n/** Currently active auth schemes selected for this operation or collection */\nconst activeSchemeOptions = computed<SecuritySchemeOption[]>(() => {\n const schemes = selectedSecurity?.selectedSchemes\n if (!schemes?.length) {\n return []\n }\n\n return schemes.flatMap((requirement) => {\n const schemeNames = Object.keys(requirement)\n\n if (schemeNames.length === 0) {\n return []\n }\n\n // Complex auth requirement with multiple schemes\n if (schemeNames.length > 1) {\n return formatComplexScheme(requirement)\n }\n\n // Simple auth requirement with single scheme\n const schemeName = schemeNames[0]\n if (!schemeName) {\n return []\n }\n\n const scheme = getResolvedRef(securitySchemes?.[schemeName])\n if (!scheme) {\n return []\n }\n\n return formatScheme({\n name: schemeName,\n value: requirement,\n })\n })\n})\n\n/**\n * Opens the combobox dropdown when clicking the auth indicator badge.\n * Prevents the disclosure from toggling if it is already open.\n */\nconst handleAuthIndicatorClick = (event: Event): void => {\n if (isDisclosureOpen.value) {\n event.stopPropagation()\n }\n comboboxButtonRef.value?.$el.click()\n}\n\n/**\n * Updates the selected auth schemes.\n * Separates existing schemes from newly created ones for the event payload.\n */\nconst handleSchemeSelection = (selected: SecuritySchemeOption[]): void => {\n const existingSchemes = selected\n .filter((option) => option.payload === undefined)\n .map((option) => unpackProxyObject(option.value, { depth: 2 }))\n\n const newSchemes = selected\n .filter((option) => option.payload !== undefined)\n .map((option) => ({\n name: option.label,\n scheme: option.payload!,\n }))\n\n eventBus.emit('auth:update:selected-security-schemes', {\n selectedRequirements: existingSchemes,\n newSchemes,\n meta,\n })\n}\n\n/** Shows the delete confirmation modal for the selected scheme */\nconst handleDeleteRequest = (option: {\n label: string\n value: SecurityRequirementObject\n}): void => {\n schemeToDelete.value = { label: option.label, payload: option.value }\n deleteModal.show()\n}\n\n/** Deletes the selected auth scheme after confirmation */\nconst handleDeleteConfirm = (): void => {\n if (!schemeToDelete.value) {\n return\n }\n\n eventBus.emit('auth:delete:security-scheme', {\n names: Object.keys(schemeToDelete.value.payload),\n })\n\n schemeToDelete.value = null\n deleteModal.hide()\n}\n\ndefineExpose({\n authIndicator,\n selectedSchemeOptions: activeSchemeOptions,\n schemeOptions: availableSchemeOptions,\n})\n</script>\n<template>\n <CollapsibleSection\n class=\"group/params relative\"\n :defaultOpen\n :isStatic=\"isStatic\"\n :itemCount=\"activeSchemeOptions.length\"\n @update:modelValue=\"(open) => (isDisclosureOpen = open)\">\n <template #title>\n <div\n :id=\"titleId\"\n class=\"inline-flex items-center gap-0.5 leading-[20px]\">\n <span>{{ title }}</span>\n\n <span\n v-if=\"authIndicator\"\n class=\"text-c-3 hover:bg-b-3 hover:text-c-1 -my-0.5 -mr-1 cursor-pointer rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{ 'text-c-1': authIndicator.text === 'Required' }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- Auth Dropdown (hidden when only one scheme is available) -->\n <template #actions>\n <ScalarComboboxMultiselect\n class=\"w-72 text-xs\"\n :modelValue=\"activeSchemeOptions\"\n multiple\n :options=\"availableSchemeOptions\"\n placement=\"bottom-end\"\n teleport\n @delete=\"handleDeleteRequest\"\n @update:modelValue=\"handleSchemeSelection\">\n <ScalarButton\n ref=\"comboboxButtonRef\"\n :aria-describedby=\"titleId\"\n class=\"group/combobox-button hover:text-c-1 text-c-2 flex h-fit w-full items-center gap-1 px-0.75 py-0.25 text-base font-normal\"\n variant=\"ghost\">\n <!-- Single auth scheme selected -->\n <template v-if=\"activeSchemeOptions.length === 1\">\n <span class=\"sr-only\">Selected Auth Type:</span>\n {{ activeSchemeOptions[0]?.label }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n Multiple\n <span class=\"sr-only\">Auth Types Selected</span>\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n <span class=\"sr-only\">Select</span>\n Auth Type\n </template>\n\n <ScalarIconCaretDown\n class=\"size-3 shrink-0 transition-transform duration-100 group-aria-expanded/combobox-button:rotate-180\"\n weight=\"bold\" />\n </ScalarButton>\n\n <template #option=\"{ option, selected }\">\n <ScalarListboxCheckbox\n multiselect\n :selected=\"selected\" />\n <div class=\"min-w-0 flex-1 truncate\">\n {{ option.label }}\n </div>\n <ScalarIconButton\n v-if=\"option.isDeletable\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"`Delete ${option.label}`\"\n size=\"xs\"\n @click.stop=\"handleDeleteRequest(option)\" />\n </template>\n </ScalarComboboxMultiselect>\n </template>\n\n <!-- Auth Table -->\n <RequestAuthDataTable\n :activeAuthIndex=\"selectedSecurity?.selectedIndex ?? 0\"\n :environment\n :eventBus\n :isStatic\n :meta\n :options\n :proxyUrl\n :securitySchemes\n :selectedSchemeOptions=\"activeSchemeOptions\"\n :server />\n\n <!-- Delete Auth Modal -->\n <DeleteRequestAuthModal\n v-if=\"schemeToDelete\"\n :label=\"schemeToDelete.label\"\n :scheme=\"schemeToDelete\"\n :state=\"deleteModal\"\n @close=\"deleteModal.hide()\"\n @delete=\"handleDeleteConfirm\" />\n </CollapsibleSection>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuthSelector.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarComboboxMultiselect,\n ScalarIconButton,\n ScalarListboxCheckbox,\n useModal,\n type Icon,\n type ScalarButton as ScalarButtonType,\n} from '@scalar/components'\nimport { ScalarIconCaretDown, ScalarIconTrash } from '@scalar/icons'\nimport type { SelectedSecurity } from '@scalar/workspace-store/entities/auth'\nimport type {\n AuthMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n isAuthOptional,\n type MergedSecuritySchemes,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n OpenApiDocument,\n SecurityRequirementObject,\n ServerObject,\n} from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed, ref, useId } from 'vue'\n\nimport DeleteRequestAuthModal from '@/v2/blocks/scalar-auth-selector-block/components/DeleteRequestAuthModal.vue'\nimport type { OAuth2Options } from '@/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue'\nimport {\n formatComplexScheme,\n formatScheme,\n getSecuritySchemeOptions,\n type SecuritySchemeOption,\n} from '@/v2/blocks/scalar-auth-selector-block/helpers/security-scheme'\nimport { CollapsibleSection } from '@/v2/components/layout'\n\nimport RequestAuthDataTable from './RequestAuthDataTable.vue'\n\nconst {\n environment,\n eventBus,\n createAnySecurityScheme = false,\n defaultOpen = true,\n isStatic = false,\n meta,\n proxyUrl,\n securityRequirements,\n securitySchemes,\n selectedSecurity,\n server,\n title,\n options,\n} = defineProps<{\n environment: XScalarEnvironment\n eventBus: WorkspaceEventBus\n /** Allows adding authentication which is not in the document */\n createAnySecurityScheme?: boolean\n /** Whether the authentication disclosure should start expanded */\n defaultOpen?: boolean\n /** Creates a static disclosure that cannot be collapsed */\n isStatic?: boolean\n meta: AuthMeta\n proxyUrl: string\n securityRequirements: OpenApiDocument['security']\n securitySchemes: MergedSecuritySchemes\n selectedSecurity: SelectedSecurity | undefined\n server: ServerObject | null\n title: string\n /** Any config options required for the OAuth2 flow */\n options?: OAuth2Options\n}>()\n\nconst titleId = useId()\nconst comboboxButtonRef = ref<typeof ScalarButtonType | null>(null)\nconst isDisclosureOpen = ref(false)\n\nconst deleteModal = useModal()\nconst schemeToDelete = ref<{\n label: string\n payload: SecurityRequirementObject\n} | null>(null)\n\n/**\n * Determines if authentication is required or optional\n *\n * Auth is optional when there is an empty security requirement and no complex requirements.\n * Complex requirements have multiple auth schemes combined (e.g., API key + OAuth).\n */\nconst authIndicator = computed<{ icon: Icon; text: string } | null>(() => {\n if (!securityRequirements?.length) {\n return null\n }\n\n const isOptional = isAuthOptional(securityRequirements)\n\n return {\n icon: isOptional ? 'Unlock' : 'Lock',\n text: isOptional ? 'Optional' : 'Required',\n }\n})\n\n/** All available auth scheme options for the dropdown */\nconst availableSchemeOptions = computed(() =>\n getSecuritySchemeOptions(\n securityRequirements ?? [],\n securitySchemes ?? {},\n selectedSecurity?.selectedSchemes ?? [],\n createAnySecurityScheme,\n ),\n)\n\n/** Currently active auth schemes selected for this operation or collection */\nconst activeSchemeOptions = computed<SecuritySchemeOption[]>(() => {\n const schemes = selectedSecurity?.selectedSchemes\n if (!schemes?.length) {\n return []\n }\n\n return schemes.flatMap((requirement) => {\n const schemeNames = Object.keys(requirement)\n\n if (schemeNames.length === 0) {\n return []\n }\n\n // Complex auth requirement with multiple schemes\n if (schemeNames.length > 1) {\n return formatComplexScheme(requirement)\n }\n\n // Simple auth requirement with single scheme\n const schemeName = schemeNames[0]\n if (!schemeName) {\n return []\n }\n\n const scheme = getResolvedRef(securitySchemes?.[schemeName])\n if (!scheme) {\n return []\n }\n\n return formatScheme({\n name: schemeName,\n value: requirement,\n })\n })\n})\n\n/**\n * Opens the combobox dropdown when clicking the auth indicator badge.\n * Prevents the disclosure from toggling if it is already open.\n */\nconst handleAuthIndicatorClick = (event: Event): void => {\n if (isDisclosureOpen.value) {\n event.stopPropagation()\n }\n comboboxButtonRef.value?.$el.click()\n}\n\n/**\n * Updates the selected auth schemes.\n * Separates existing schemes from newly created ones for the event payload.\n */\nconst handleSchemeSelection = (selected: SecuritySchemeOption[]): void => {\n const existingSchemes = selected\n .filter((option) => option.payload === undefined)\n .map((option) => unpackProxyObject(option.value, { depth: 2 }))\n\n const newSchemes = selected\n .filter((option) => option.payload !== undefined)\n .map((option) => ({\n name: option.label,\n scheme: option.payload!,\n }))\n\n eventBus.emit('auth:update:selected-security-schemes', {\n selectedRequirements: existingSchemes,\n newSchemes,\n meta,\n })\n}\n\n/** Shows the delete confirmation modal for the selected scheme */\nconst handleDeleteRequest = (option: {\n label: string\n value: SecurityRequirementObject\n}): void => {\n schemeToDelete.value = { label: option.label, payload: option.value }\n deleteModal.show()\n}\n\n/** Deletes the selected auth scheme after confirmation */\nconst handleDeleteConfirm = (): void => {\n if (!schemeToDelete.value) {\n return\n }\n\n eventBus.emit('auth:delete:security-scheme', {\n names: Object.keys(schemeToDelete.value.payload),\n })\n\n schemeToDelete.value = null\n deleteModal.hide()\n}\n\ndefineExpose({\n authIndicator,\n selectedSchemeOptions: activeSchemeOptions,\n schemeOptions: availableSchemeOptions,\n})\n</script>\n<template>\n <CollapsibleSection\n class=\"group/params relative\"\n :defaultOpen\n :isStatic=\"isStatic\"\n :itemCount=\"activeSchemeOptions.length\"\n @update:modelValue=\"(open) => (isDisclosureOpen = open)\">\n <template #title>\n <div\n :id=\"titleId\"\n class=\"inline-flex items-center gap-0.5 leading-[20px]\">\n <span>{{ title }}</span>\n\n <span\n v-if=\"authIndicator\"\n class=\"text-c-3 hover:bg-b-3 hover:text-c-1 -my-0.5 -mr-1 cursor-pointer rounded px-1 py-0.5 leading-[normal] font-normal\"\n :class=\"{ 'text-c-1': authIndicator.text === 'Required' }\"\n data-testid=\"auth-indicator\"\n @click=\"handleAuthIndicatorClick\">\n {{ authIndicator.text }}\n </span>\n </div>\n </template>\n\n <!-- Auth Dropdown (hidden when only one scheme is available) -->\n <template #actions>\n <ScalarComboboxMultiselect\n class=\"w-72 text-xs\"\n :modelValue=\"activeSchemeOptions\"\n multiple\n :options=\"availableSchemeOptions\"\n placement=\"bottom-end\"\n teleport\n @delete=\"handleDeleteRequest\"\n @update:modelValue=\"handleSchemeSelection\">\n <ScalarButton\n ref=\"comboboxButtonRef\"\n :aria-describedby=\"titleId\"\n class=\"group/combobox-button hover:text-c-1 text-c-2 flex h-fit w-full items-center gap-1 px-0.75 py-0.25 text-base font-normal\"\n variant=\"ghost\">\n <!-- Single auth scheme selected -->\n <template v-if=\"activeSchemeOptions.length === 1\">\n <span class=\"sr-only\">Selected Auth Type:</span>\n {{ activeSchemeOptions[0]?.label }}\n </template>\n\n <!-- Multiple auth schemes selected -->\n <template v-else-if=\"activeSchemeOptions.length > 1\">\n Multiple\n <span class=\"sr-only\">Auth Types Selected</span>\n </template>\n\n <!-- No auth schemes selected -->\n <template v-else>\n <span class=\"sr-only\">Select</span>\n Auth Type\n </template>\n\n <ScalarIconCaretDown\n class=\"size-3 shrink-0 transition-transform duration-100 group-aria-expanded/combobox-button:rotate-180\"\n weight=\"bold\" />\n </ScalarButton>\n\n <template #option=\"{ option, selected }\">\n <ScalarListboxCheckbox\n multiselect\n :selected=\"selected\" />\n <div class=\"min-w-0 flex-1 truncate\">\n {{ option.label }}\n </div>\n <ScalarIconButton\n v-if=\"option.isDeletable\"\n class=\"-m-0.5 shrink-0 p-0.5 opacity-0 group-hover/item:opacity-100\"\n :icon=\"ScalarIconTrash\"\n :label=\"`Delete ${option.label}`\"\n size=\"xs\"\n @click.stop=\"handleDeleteRequest(option)\" />\n </template>\n </ScalarComboboxMultiselect>\n </template>\n\n <!-- Auth Table -->\n <RequestAuthDataTable\n :activeAuthIndex=\"selectedSecurity?.selectedIndex ?? 0\"\n :environment\n :eventBus\n :isStatic\n :meta\n :options\n :proxyUrl\n :securitySchemes\n :selectedSchemeOptions=\"activeSchemeOptions\"\n :server />\n\n <!-- Delete Auth Modal -->\n <DeleteRequestAuthModal\n v-if=\"schemeToDelete\"\n :label=\"schemeToDelete.label\"\n :scheme=\"schemeToDelete\"\n :state=\"deleteModal\"\n @close=\"deleteModal.hide()\"\n @delete=\"handleDeleteConfirm\" />\n </CollapsibleSection>\n</template>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a callback parameter paired with the URL component it was read from.
|
|
3
|
+
* State validation depends on this source so query and hash values cannot be
|
|
4
|
+
* mixed across different OAuth callback shapes.
|
|
5
|
+
*/
|
|
6
|
+
type OAuthCallbackParamResult = {
|
|
7
|
+
/** The query or hash parameters that contained the value. */
|
|
8
|
+
params: URLSearchParams | null;
|
|
9
|
+
/** The callback parameter value, or null when the parameter is missing. */
|
|
10
|
+
value: string | null;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Normalized OAuth callback data used after the popup returns to a readable
|
|
14
|
+
* redirect URL. Credential params are retained so callers can validate state
|
|
15
|
+
* from the same URL component as the returned credential.
|
|
16
|
+
*/
|
|
17
|
+
type OAuthCallbackData = {
|
|
18
|
+
/** Access token returned by implicit or token-based flows. */
|
|
19
|
+
accessToken: string | null;
|
|
20
|
+
/** The URL component that contained the access token. */
|
|
21
|
+
accessTokenParams: URLSearchParams | null;
|
|
22
|
+
/** Authorization code returned by authorization-code flows. */
|
|
23
|
+
code: string | null;
|
|
24
|
+
/** The URL component that contained the authorization code. */
|
|
25
|
+
codeParams: URLSearchParams | null;
|
|
26
|
+
/** OAuth error code returned by the provider. */
|
|
27
|
+
error: string | null;
|
|
28
|
+
/** Provider-supplied details for the OAuth error. */
|
|
29
|
+
errorDescription: string | null;
|
|
30
|
+
/** Refresh token returned by providers that include one in the callback. */
|
|
31
|
+
refreshToken: string | null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Splits an OAuth redirect URL into query and hash parameters.
|
|
35
|
+
*
|
|
36
|
+
* OAuth providers are inconsistent about where they return callback data:
|
|
37
|
+
* authorization-code flows usually use the query string, while implicit flows
|
|
38
|
+
* commonly use the URL fragment. Keeping both sets separate lets callers decide
|
|
39
|
+
* which source should win when a provider sends duplicate keys.
|
|
40
|
+
*/
|
|
41
|
+
export declare const getOAuthCallbackParams: (callbackUrl: string) => {
|
|
42
|
+
searchParams: URLSearchParams;
|
|
43
|
+
hashParams: URLSearchParams;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Reads a callback parameter from the query string first, then the hash fragment.
|
|
47
|
+
*
|
|
48
|
+
* Query-string values intentionally take precedence because they are the
|
|
49
|
+
* standard location for authorization-code callbacks. The hash lookup keeps
|
|
50
|
+
* implicit-flow callbacks and non-standard providers working.
|
|
51
|
+
*/
|
|
52
|
+
export declare const getOAuthCallbackParam: (callbackUrl: string, paramName: string) => string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Reads a callback parameter and returns the URL component it came from.
|
|
55
|
+
*
|
|
56
|
+
* OAuth state must be validated from the same component as the credential
|
|
57
|
+
* (`code` or `access_token`) so mixed query/hash callbacks cannot pair a
|
|
58
|
+
* trusted state with an untrusted credential.
|
|
59
|
+
*/
|
|
60
|
+
export declare const getOAuthCallbackParamWithSource: (searchParams: URLSearchParams, hashParams: URLSearchParams, paramName: string) => OAuthCallbackParamResult;
|
|
61
|
+
/**
|
|
62
|
+
* Safely reads the OAuth popup callback data.
|
|
63
|
+
*
|
|
64
|
+
* Accessing the popup URL can throw while it is still on another origin, so
|
|
65
|
+
* callers get null values until the popup returns to a readable callback URL.
|
|
66
|
+
*/
|
|
67
|
+
export declare const getOAuthCallbackData: (getCallbackUrl: () => string, tokenName?: string) => OAuthCallbackData;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=oauth-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-callback.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,KAAK,wBAAwB,GAAG;IAC9B,6DAA6D;IAC7D,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED;;;;GAIG;AACH,KAAK,iBAAiB,GAAG;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,yDAAyD;IACzD,iBAAiB,EAAE,eAAe,GAAG,IAAI,CAAA;IACzC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+DAA+D;IAC/D,UAAU,EAAE,eAAe,GAAG,IAAI,CAAA;IAClC,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,MAAM,KAClB;IAAE,YAAY,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,eAAe,CAAA;CAO9D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,IAGvF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,GAC1C,cAAc,eAAe,EAC7B,YAAY,eAAe,EAC3B,WAAW,MAAM,KAChB,wBAYF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,MAAM,EAAE,kBAA0B,KAAG,iBA2B/F,CAAA"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.ts
|
|
2
|
+
/**
|
|
3
|
+
* Splits an OAuth redirect URL into query and hash parameters.
|
|
4
|
+
*
|
|
5
|
+
* OAuth providers are inconsistent about where they return callback data:
|
|
6
|
+
* authorization-code flows usually use the query string, while implicit flows
|
|
7
|
+
* commonly use the URL fragment. Keeping both sets separate lets callers decide
|
|
8
|
+
* which source should win when a provider sends duplicate keys.
|
|
9
|
+
*/
|
|
10
|
+
var getOAuthCallbackParams = (callbackUrl) => {
|
|
11
|
+
const parsedUrl = new URL(callbackUrl);
|
|
12
|
+
return {
|
|
13
|
+
searchParams: parsedUrl.searchParams,
|
|
14
|
+
hashParams: new URLSearchParams(parsedUrl.hash.slice(1))
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Reads a callback parameter and returns the URL component it came from.
|
|
19
|
+
*
|
|
20
|
+
* OAuth state must be validated from the same component as the credential
|
|
21
|
+
* (`code` or `access_token`) so mixed query/hash callbacks cannot pair a
|
|
22
|
+
* trusted state with an untrusted credential.
|
|
23
|
+
*/
|
|
24
|
+
var getOAuthCallbackParamWithSource = (searchParams, hashParams, paramName) => {
|
|
25
|
+
const searchValue = searchParams.get(paramName);
|
|
26
|
+
if (searchValue !== null) return {
|
|
27
|
+
params: searchParams,
|
|
28
|
+
value: searchValue
|
|
29
|
+
};
|
|
30
|
+
const hashValue = hashParams.get(paramName);
|
|
31
|
+
if (hashValue !== null) return {
|
|
32
|
+
params: hashParams,
|
|
33
|
+
value: hashValue
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
params: null,
|
|
37
|
+
value: null
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Safely reads the OAuth popup callback data.
|
|
42
|
+
*
|
|
43
|
+
* Accessing the popup URL can throw while it is still on another origin, so
|
|
44
|
+
* callers get null values until the popup returns to a readable callback URL.
|
|
45
|
+
*/
|
|
46
|
+
var getOAuthCallbackData = (getCallbackUrl, tokenName = "access_token") => {
|
|
47
|
+
try {
|
|
48
|
+
const { searchParams, hashParams } = getOAuthCallbackParams(getCallbackUrl());
|
|
49
|
+
const accessTokenResult = getOAuthCallbackParamWithSource(searchParams, hashParams, tokenName);
|
|
50
|
+
const codeResult = getOAuthCallbackParamWithSource(searchParams, hashParams, "code");
|
|
51
|
+
return {
|
|
52
|
+
accessToken: accessTokenResult.value,
|
|
53
|
+
accessTokenParams: accessTokenResult.params,
|
|
54
|
+
code: codeResult.value,
|
|
55
|
+
codeParams: codeResult.params,
|
|
56
|
+
error: searchParams.get("error") ?? hashParams.get("error"),
|
|
57
|
+
errorDescription: searchParams.get("error_description") ?? hashParams.get("error_description"),
|
|
58
|
+
refreshToken: searchParams.get("refresh_token") ?? hashParams.get("refresh_token")
|
|
59
|
+
};
|
|
60
|
+
} catch (_e) {
|
|
61
|
+
return {
|
|
62
|
+
accessToken: null,
|
|
63
|
+
accessTokenParams: null,
|
|
64
|
+
code: null,
|
|
65
|
+
codeParams: null,
|
|
66
|
+
error: null,
|
|
67
|
+
errorDescription: null,
|
|
68
|
+
refreshToken: null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { getOAuthCallbackData };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=oauth-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-callback.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth-callback.ts"],"sourcesContent":["/**\n * Keeps a callback parameter paired with the URL component it was read from.\n * State validation depends on this source so query and hash values cannot be\n * mixed across different OAuth callback shapes.\n */\ntype OAuthCallbackParamResult = {\n /** The query or hash parameters that contained the value. */\n params: URLSearchParams | null\n /** The callback parameter value, or null when the parameter is missing. */\n value: string | null\n}\n\n/**\n * Normalized OAuth callback data used after the popup returns to a readable\n * redirect URL. Credential params are retained so callers can validate state\n * from the same URL component as the returned credential.\n */\ntype OAuthCallbackData = {\n /** Access token returned by implicit or token-based flows. */\n accessToken: string | null\n /** The URL component that contained the access token. */\n accessTokenParams: URLSearchParams | null\n /** Authorization code returned by authorization-code flows. */\n code: string | null\n /** The URL component that contained the authorization code. */\n codeParams: URLSearchParams | null\n /** OAuth error code returned by the provider. */\n error: string | null\n /** Provider-supplied details for the OAuth error. */\n errorDescription: string | null\n /** Refresh token returned by providers that include one in the callback. */\n refreshToken: string | null\n}\n\n/**\n * Splits an OAuth redirect URL into query and hash parameters.\n *\n * OAuth providers are inconsistent about where they return callback data:\n * authorization-code flows usually use the query string, while implicit flows\n * commonly use the URL fragment. Keeping both sets separate lets callers decide\n * which source should win when a provider sends duplicate keys.\n */\nexport const getOAuthCallbackParams = (\n callbackUrl: string,\n): { searchParams: URLSearchParams; hashParams: URLSearchParams } => {\n const parsedUrl = new URL(callbackUrl)\n\n return {\n searchParams: parsedUrl.searchParams,\n hashParams: new URLSearchParams(parsedUrl.hash.slice(1)),\n }\n}\n\n/**\n * Reads a callback parameter from the query string first, then the hash fragment.\n *\n * Query-string values intentionally take precedence because they are the\n * standard location for authorization-code callbacks. The hash lookup keeps\n * implicit-flow callbacks and non-standard providers working.\n */\nexport const getOAuthCallbackParam = (callbackUrl: string, paramName: string): string | null => {\n const { searchParams, hashParams } = getOAuthCallbackParams(callbackUrl)\n return searchParams.get(paramName) ?? hashParams.get(paramName)\n}\n\n/**\n * Reads a callback parameter and returns the URL component it came from.\n *\n * OAuth state must be validated from the same component as the credential\n * (`code` or `access_token`) so mixed query/hash callbacks cannot pair a\n * trusted state with an untrusted credential.\n */\nexport const getOAuthCallbackParamWithSource = (\n searchParams: URLSearchParams,\n hashParams: URLSearchParams,\n paramName: string,\n): OAuthCallbackParamResult => {\n const searchValue = searchParams.get(paramName)\n if (searchValue !== null) {\n return { params: searchParams, value: searchValue }\n }\n\n const hashValue = hashParams.get(paramName)\n if (hashValue !== null) {\n return { params: hashParams, value: hashValue }\n }\n\n return { params: null, value: null }\n}\n\n/**\n * Safely reads the OAuth popup callback data.\n *\n * Accessing the popup URL can throw while it is still on another origin, so\n * callers get null values until the popup returns to a readable callback URL.\n */\nexport const getOAuthCallbackData = (getCallbackUrl: () => string, tokenName = 'access_token'): OAuthCallbackData => {\n try {\n const { searchParams, hashParams } = getOAuthCallbackParams(getCallbackUrl())\n const accessTokenResult = getOAuthCallbackParamWithSource(searchParams, hashParams, tokenName)\n const codeResult = getOAuthCallbackParamWithSource(searchParams, hashParams, 'code')\n\n return {\n accessToken: accessTokenResult.value,\n accessTokenParams: accessTokenResult.params,\n code: codeResult.value,\n codeParams: codeResult.params,\n error: searchParams.get('error') ?? hashParams.get('error'),\n errorDescription: searchParams.get('error_description') ?? hashParams.get('error_description'),\n refreshToken: searchParams.get('refresh_token') ?? hashParams.get('refresh_token'),\n }\n } catch (_e) {\n // Ignore CORS errors while the popup is still on the provider origin.\n return {\n accessToken: null,\n accessTokenParams: null,\n code: null,\n codeParams: null,\n error: null,\n errorDescription: null,\n refreshToken: null,\n }\n }\n}\n"],"mappings":";;;;;;;;;AA0CA,IAAa,0BACX,gBACmE;CACnE,MAAM,YAAY,IAAI,IAAI,YAAY;AAEtC,QAAO;EACL,cAAc,UAAU;EACxB,YAAY,IAAI,gBAAgB,UAAU,KAAK,MAAM,EAAE,CAAC;EACzD;;;;;;;;;AAsBH,IAAa,mCACX,cACA,YACA,cAC6B;CAC7B,MAAM,cAAc,aAAa,IAAI,UAAU;AAC/C,KAAI,gBAAgB,KAClB,QAAO;EAAE,QAAQ;EAAc,OAAO;EAAa;CAGrD,MAAM,YAAY,WAAW,IAAI,UAAU;AAC3C,KAAI,cAAc,KAChB,QAAO;EAAE,QAAQ;EAAY,OAAO;EAAW;AAGjD,QAAO;EAAE,QAAQ;EAAM,OAAO;EAAM;;;;;;;;AAStC,IAAa,wBAAwB,gBAA8B,YAAY,mBAAsC;AACnH,KAAI;EACF,MAAM,EAAE,cAAc,eAAe,uBAAuB,gBAAgB,CAAC;EAC7E,MAAM,oBAAoB,gCAAgC,cAAc,YAAY,UAAU;EAC9F,MAAM,aAAa,gCAAgC,cAAc,YAAY,OAAO;AAEpF,SAAO;GACL,aAAa,kBAAkB;GAC/B,mBAAmB,kBAAkB;GACrC,MAAM,WAAW;GACjB,YAAY,WAAW;GACvB,OAAO,aAAa,IAAI,QAAQ,IAAI,WAAW,IAAI,QAAQ;GAC3D,kBAAkB,aAAa,IAAI,oBAAoB,IAAI,WAAW,IAAI,oBAAoB;GAC9F,cAAc,aAAa,IAAI,gBAAgB,IAAI,WAAW,IAAI,gBAAgB;GACnF;UACM,IAAI;AAEX,SAAO;GACL,aAAa;GACb,mBAAmB;GACnB,MAAM;GACN,YAAY;GACZ,OAAO;GACP,kBAAkB;GAClB,cAAc;GACf"}
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import type { ErrorResponse } from '@scalar/helpers/errors/normalize-error';
|
|
2
2
|
import type { OAuthFlowsObjectSecret } from '@scalar/workspace-store/request-example';
|
|
3
3
|
import type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
|
|
4
|
+
type ActiveServerBase = {
|
|
5
|
+
basePath: string;
|
|
6
|
+
} | {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
} | Record<string, never>;
|
|
4
9
|
export type OAuth2Tokens = {
|
|
5
10
|
accessToken: string;
|
|
6
11
|
refreshToken?: string;
|
|
7
12
|
};
|
|
8
13
|
/** Flow types that support token refresh (all except implicit) */
|
|
9
14
|
type RefreshableFlows = Exclude<keyof OAuthFlowsObjectSecret, 'implicit'>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the active server URL using OpenAPI server variables first, then
|
|
17
|
+
* Scalar environment variables.
|
|
18
|
+
*/
|
|
19
|
+
export declare const getServerUrl: (activeServer: ServerObject | null, environmentVariables?: Record<string, string>) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Builds the base option used when OAuth URLs are relative to the active server.
|
|
22
|
+
*
|
|
23
|
+
* Relative server URLs become browser-only base paths because they need the
|
|
24
|
+
* current window location to resolve correctly.
|
|
25
|
+
*/
|
|
26
|
+
export declare const getActiveServerBase: (activeServer: ServerObject | null, environmentVariables?: Record<string, string>) => ActiveServerBase;
|
|
10
27
|
/**
|
|
11
28
|
* Authorize oauth2 flow
|
|
12
29
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAK3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAA;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAK3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAA;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAchG,KAAK,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAE1F,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,kEAAkE;AAClE,KAAK,gBAAgB,GAAG,OAAO,CAAC,MAAM,sBAAsB,EAAE,UAAU,CAAC,CAAA;AAEzE;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,cAAc,YAAY,GAAG,IAAI,EACjC,uBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAChD,MAIA,CAAA;AAEH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC9B,cAAc,YAAY,GAAG,IAAI,EACjC,uBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAChD,gBAWF,CAAA;AAuCD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,sBAAsB,EAC7B,MAAM,MAAM,sBAAsB,EAClC,gBAAgB,MAAM,EAAE;AACxB,wEAAwE;AACxE,cAAc,YAAY,GAAG,IAAI;AACjC,kCAAkC;AAClC,UAAU,MAAM;AAChB,6FAA6F;AAC7F,uBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAChD,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAsKrC,CAAA;AAoID;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,sBAAsB,EAC7B,MAAM,gBAAgB;AACtB,kCAAkC;AAClC,UAAU,MAAM;AAChB,+DAA+D;AAC/D,cAAc,YAAY,GAAG,IAAI;AACjC,2EAA2E;AAC3E,uBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAChD,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAoFrC,CAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getOAuthCallbackData } from "./oauth-callback.js";
|
|
1
2
|
import { makeUrlAbsolute } from "@scalar/helpers/url/make-url-absolute";
|
|
2
3
|
import { getServerVariables } from "@scalar/workspace-store/request-example";
|
|
3
4
|
import { encode, fromUint8Array } from "js-base64";
|
|
@@ -5,9 +6,17 @@ import { replaceEnvVariables, replacePathVariables } from "@scalar/helpers/regex
|
|
|
5
6
|
import { isRelativePath } from "@scalar/helpers/url/is-relative-path";
|
|
6
7
|
import { shouldUseProxy } from "@scalar/helpers/url/redirect-to-proxy";
|
|
7
8
|
//#region src/v2/blocks/scalar-auth-selector-block/helpers/oauth.ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the active server URL using OpenAPI server variables first, then
|
|
11
|
+
* Scalar environment variables.
|
|
12
|
+
*/
|
|
13
|
+
var getServerUrl = (activeServer, environmentVariables = {}) => replaceEnvVariables(replacePathVariables(activeServer?.url ?? "", getServerVariables(activeServer)), environmentVariables);
|
|
14
|
+
/**
|
|
15
|
+
* Builds the base option used when OAuth URLs are relative to the active server.
|
|
16
|
+
*
|
|
17
|
+
* Relative server URLs become browser-only base paths because they need the
|
|
18
|
+
* current window location to resolve correctly.
|
|
19
|
+
*/
|
|
11
20
|
var getActiveServerBase = (activeServer, environmentVariables = {}) => {
|
|
12
21
|
const serverUrl = getServerUrl(activeServer, environmentVariables);
|
|
13
22
|
if (!serverUrl) return {};
|
|
@@ -86,51 +95,40 @@ var authorizeOauth2 = async (flows, type, selectedScopes, activeServer, proxyUrl
|
|
|
86
95
|
const authWindow = window.open(url, "openAuth2Window", "left=100,top=100,width=800,height=600");
|
|
87
96
|
if (authWindow) return new Promise((resolve) => {
|
|
88
97
|
const checkWindowClosed = setInterval(() => {
|
|
89
|
-
|
|
90
|
-
let refreshToken = null;
|
|
91
|
-
let code = null;
|
|
92
|
-
let error = null;
|
|
93
|
-
let errorDescription = null;
|
|
94
|
-
try {
|
|
95
|
-
const urlParams = new URL(authWindow.location.href).searchParams;
|
|
96
|
-
const tokenName = flow["x-tokenName"] || "access_token";
|
|
97
|
-
accessToken = urlParams.get(tokenName);
|
|
98
|
-
refreshToken = urlParams.get("refresh_token");
|
|
99
|
-
code = urlParams.get("code");
|
|
100
|
-
error = urlParams.get("error");
|
|
101
|
-
errorDescription = urlParams.get("error_description");
|
|
102
|
-
const hashParams = new URLSearchParams(authWindow.location.href.split("#")[1]);
|
|
103
|
-
accessToken ||= hashParams.get(tokenName);
|
|
104
|
-
refreshToken ||= hashParams.get("refresh_token");
|
|
105
|
-
code ||= hashParams.get("code");
|
|
106
|
-
error ||= hashParams.get("error");
|
|
107
|
-
errorDescription ||= hashParams.get("error_description");
|
|
108
|
-
} catch (_e) {}
|
|
98
|
+
const { accessToken, accessTokenParams, code, codeParams, error, errorDescription, refreshToken } = getOAuthCallbackData(() => authWindow.location.href, flow["x-tokenName"] || "access_token");
|
|
109
99
|
if (authWindow.closed || accessToken || code || error) {
|
|
110
100
|
clearInterval(checkWindowClosed);
|
|
111
101
|
authWindow.close();
|
|
112
|
-
if (error)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
102
|
+
if (error) {
|
|
103
|
+
resolve([/* @__PURE__ */ new Error(`OAuth error: ${error}${errorDescription ? ` (${errorDescription})` : ""}`), null]);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (accessToken) {
|
|
107
|
+
if ((accessTokenParams?.get("state") ?? null) === state) resolve([null, {
|
|
108
|
+
accessToken,
|
|
109
|
+
...refreshToken ? { refreshToken } : {}
|
|
110
|
+
}]);
|
|
111
|
+
else resolve([/* @__PURE__ */ new Error("State mismatch"), null]);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (code && type === "authorizationCode") {
|
|
115
|
+
if ((codeParams?.get("state") ?? null) === state) authorizeServers(flows, type, scopes, {
|
|
116
|
+
code,
|
|
117
|
+
pkce,
|
|
118
|
+
proxyUrl
|
|
119
|
+
}, activeServer, environmentVariables).then(resolve).catch((error) => {
|
|
120
|
+
resolve([error instanceof Error ? error : new Error("Failed to get an access token", { cause: error }), null]);
|
|
121
|
+
});
|
|
122
|
+
else resolve([/* @__PURE__ */ new Error("State mismatch"), null]);
|
|
123
|
+
return;
|
|
127
124
|
}
|
|
125
|
+
resolve([/* @__PURE__ */ new Error("Window was closed without granting authorization"), null]);
|
|
128
126
|
}
|
|
129
127
|
}, 200);
|
|
130
128
|
});
|
|
131
129
|
return [/* @__PURE__ */ new Error("Failed to open auth window"), null];
|
|
132
|
-
} catch (
|
|
133
|
-
return [
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return [error instanceof Error ? error : new Error("Failed to authorize oauth2 flow", { cause: error }), null];
|
|
134
132
|
}
|
|
135
133
|
};
|
|
136
134
|
/**
|
|
@@ -173,12 +171,13 @@ var authorizeServers = async (flows, type, scopes, { code, pkce, proxyUrl } = {}
|
|
|
173
171
|
})).json();
|
|
174
172
|
const accessToken = responseData[flow["x-tokenName"] || "access_token"];
|
|
175
173
|
const refreshToken = responseData.refresh_token;
|
|
174
|
+
if (!accessToken) return [new Error(responseData.error_description ?? responseData.error ?? "Failed to get an access token"), null];
|
|
176
175
|
return [null, {
|
|
177
176
|
accessToken,
|
|
178
177
|
...typeof refreshToken === "string" ? { refreshToken } : {}
|
|
179
178
|
}];
|
|
180
|
-
} catch {
|
|
181
|
-
return [
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return [error instanceof Error ? error : new Error("Failed to get an access token. Please check your credentials.", { cause: error }), null];
|
|
182
181
|
}
|
|
183
182
|
};
|
|
184
183
|
/**
|
|
@@ -219,8 +218,8 @@ var refreshOauth2Token = async (flows, type, proxyUrl, activeServer, environment
|
|
|
219
218
|
accessToken,
|
|
220
219
|
...typeof newRefreshToken === "string" ? { refreshToken: newRefreshToken } : { refreshToken }
|
|
221
220
|
}];
|
|
222
|
-
} catch {
|
|
223
|
-
return [
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return [error instanceof Error ? error : new Error("Failed to refresh the access token. Please re-authorize.", { cause: error }), null];
|
|
224
223
|
}
|
|
225
224
|
};
|
|
226
225
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth.ts"],"sourcesContent":["import type { ErrorResponse } from '@scalar/helpers/errors/normalize-error'\nimport { replaceEnvVariables, replacePathVariables } from '@scalar/helpers/regex/replace-variables'\nimport { isRelativePath } from '@scalar/helpers/url/is-relative-path'\nimport { makeUrlAbsolute } from '@scalar/helpers/url/make-url-absolute'\nimport { shouldUseProxy } from '@scalar/helpers/url/redirect-to-proxy'\nimport type { OAuthFlowsObjectSecret } from '@scalar/workspace-store/request-example'\nimport { getServerVariables } from '@scalar/workspace-store/request-example'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { encode, fromUint8Array } from 'js-base64'\n\n/** Oauth2 security schemes which are not implicit */\ntype NonImplicitFlows = Omit<OAuthFlowsObjectSecret, 'implicit'>\n\ntype PKCEState = {\n codeVerifier: string\n codeChallenge: string\n codeChallengeMethod: string\n}\n\nexport type OAuth2Tokens = {\n accessToken: string\n refreshToken?: string\n}\n\n/** Flow types that support token refresh (all except implicit) */\ntype RefreshableFlows = Exclude<keyof OAuthFlowsObjectSecret, 'implicit'>\n\nconst getServerUrl = (activeServer: ServerObject | null, environmentVariables: Record<string, string> = {}) => {\n return replaceEnvVariables(\n replacePathVariables(activeServer?.url ?? '', getServerVariables(activeServer)),\n environmentVariables,\n )\n}\n\nconst getActiveServerBase = (activeServer: ServerObject | null, environmentVariables: Record<string, string> = {}) => {\n const serverUrl = getServerUrl(activeServer, environmentVariables)\n\n if (!serverUrl) {\n return {}\n }\n\n if (isRelativePath(serverUrl)) {\n return typeof window === 'undefined' ? {} : { basePath: serverUrl }\n }\n\n return { baseUrl: serverUrl }\n}\n\n/**\n * Generates a random string for PKCE code verifier\n *\n * @see https://www.rfc-editor.org/rfc/rfc7636#page-8\n */\nconst generateCodeVerifier = (): string => {\n // Generate 32 random bytes\n const buffer = new Uint8Array(32)\n crypto.getRandomValues(buffer)\n\n // Base64URL encode the bytes\n return fromUint8Array(buffer, true)\n}\n\n/**\n * Creates a code challenge from the code verifier\n */\nconst generateCodeChallenge = async (verifier: string, encoding: 'SHA-256' | 'plain'): Promise<string> => {\n if (encoding === 'plain') {\n return verifier\n }\n\n // If crypto.subtle.digest is not a function, we cannot use SHA-256\n if (typeof crypto?.subtle?.digest !== 'function') {\n console.warn('SHA-256 is only supported when using https, using a plain text code challenge instead.')\n return verifier\n }\n\n // ASCII encoding is just taking the lower 8 bits of each character\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const digest = await crypto.subtle.digest('SHA-256', data)\n\n // Base64URL encode the bytes\n return fromUint8Array(new Uint8Array(digest), true)\n}\n\n/**\n * Authorize oauth2 flow\n *\n * @returns the resolved oauth2 tokens\n */\nexport const authorizeOauth2 = async (\n flows: OAuthFlowsObjectSecret,\n type: keyof OAuthFlowsObjectSecret,\n selectedScopes: string[],\n /** We use the active server to set a base for relative redirect uris */\n activeServer: ServerObject | null,\n /** If we want to use the proxy */\n proxyUrl: string,\n /** Flattened environment variables used to resolve server URL templates like `{protocol}` */\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n try {\n if (!flow) {\n return [new Error('Flow not found'), null]\n }\n\n const scopes = selectedScopes.join(' ')\n\n // Client Credentials or Password Flow\n if (type === 'clientCredentials' || type === 'password') {\n return authorizeServers(\n flows,\n type,\n scopes,\n {\n proxyUrl,\n },\n activeServer,\n environmentVariables,\n )\n }\n\n // Generate a random state string with the length of 8 characters\n const state = (Math.random() + 1).toString(36).substring(2, 10)\n\n const authorizationUrl = makeUrlAbsolute(\n flows[type]!['x-scalar-secret-auth-url'] ?? flows[type]!.authorizationUrl,\n getActiveServerBase(activeServer, environmentVariables),\n )\n\n const url = new URL(authorizationUrl)\n\n /** Special PKCE state */\n let pkce: PKCEState | null = null\n\n // Params unique to the flows\n if (type === 'implicit') {\n url.searchParams.set('response_type', 'token')\n } else if (type === 'authorizationCode') {\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n\n url.searchParams.set('response_type', 'code')\n\n // PKCE\n if (typedFlow['x-usePkce'] !== 'no') {\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier, typedFlow['x-usePkce'])\n\n // Set state for later verification\n pkce = {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: typedFlow['x-usePkce'] === 'SHA-256' ? 'S256' : 'plain',\n }\n\n // Set the code challenge and method on the url\n url.searchParams.set('code_challenge', codeChallenge)\n url.searchParams.set('code_challenge_method', pkce.codeChallengeMethod)\n }\n }\n\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n\n // Handle relative redirect uris\n if (typedFlow['x-scalar-secret-redirect-uri'].startsWith('/')) {\n const baseUrl =\n getServerUrl(activeServer, environmentVariables) || window.location.origin + window.location.pathname\n const redirectUri = new URL(typedFlow['x-scalar-secret-redirect-uri'], baseUrl).toString()\n\n url.searchParams.set('redirect_uri', redirectUri)\n } else {\n url.searchParams.set('redirect_uri', typedFlow['x-scalar-secret-redirect-uri'])\n }\n\n if (flow['x-scalar-security-query']) {\n Object.keys(flow['x-scalar-security-query']).forEach((key: string): void => {\n const value = flow['x-scalar-security-query']?.[key]\n if (!value) {\n return\n }\n url.searchParams.set(key, value)\n })\n }\n\n // Common to all flows\n url.searchParams.set('client_id', flow['x-scalar-secret-client-id'])\n url.searchParams.set('state', state)\n if (scopes) {\n url.searchParams.set('scope', scopes)\n }\n\n const windowFeatures = 'left=100,top=100,width=800,height=600'\n const authWindow = window.open(url, 'openAuth2Window', windowFeatures)\n\n // Open up a window and poll until closed or we have the data we want\n if (authWindow) {\n // We need to return a promise here due to the setInterval\n return new Promise<ErrorResponse<OAuth2Tokens>>((resolve) => {\n const checkWindowClosed = setInterval(() => {\n let accessToken: string | null = null\n let refreshToken: string | null = null\n let code: string | null = null\n let error: string | null = null\n let errorDescription: string | null = null\n\n try {\n const urlParams = new URL(authWindow.location.href).searchParams\n const tokenName = flow['x-tokenName'] || 'access_token'\n accessToken = urlParams.get(tokenName)\n refreshToken = urlParams.get('refresh_token')\n code = urlParams.get('code')\n\n error = urlParams.get('error')\n errorDescription = urlParams.get('error_description')\n\n // We may get the properties in a hash\n const hashParams = new URLSearchParams(authWindow.location.href.split('#')[1])\n accessToken ||= hashParams.get(tokenName)\n refreshToken ||= hashParams.get('refresh_token')\n code ||= hashParams.get('code')\n error ||= hashParams.get('error')\n errorDescription ||= hashParams.get('error_description')\n } catch (_e) {\n // Ignore CORS error from popup\n }\n\n // The window has closed OR we have what we are looking for so we stop polling\n if (authWindow.closed || accessToken || code || error) {\n clearInterval(checkWindowClosed)\n authWindow.close()\n\n if (error) {\n resolve([new Error(`OAuth error: ${error}${errorDescription ? ` (${errorDescription})` : ''}`), null])\n }\n\n // Implicit Flow\n else if (accessToken) {\n // State is a hash fragment and cannot be found through search params\n const _state = authWindow.location.href.match(/state=([^&]*)/)?.[1]\n\n if (_state === state) {\n resolve([null, { accessToken, ...(refreshToken ? { refreshToken } : {}) }])\n } else {\n resolve([new Error('State mismatch'), null])\n }\n }\n\n // Authorization Code Server Flow\n else if (code && type === 'authorizationCode') {\n const _state = new URL(authWindow.location.href).searchParams.get('state')\n\n if (_state === state) {\n // biome-ignore lint/nursery/noFloatingPromises: output of authorizeServers must be returned\n authorizeServers(\n flows,\n type,\n scopes,\n {\n code,\n pkce,\n proxyUrl,\n },\n activeServer,\n environmentVariables,\n ).then(resolve)\n } else {\n resolve([new Error('State mismatch'), null])\n }\n }\n // User closed window without authorizing\n else {\n clearInterval(checkWindowClosed)\n resolve([new Error('Window was closed without granting authorization'), null])\n }\n }\n }, 200)\n })\n }\n return [new Error('Failed to open auth window'), null]\n } catch (_) {\n return [new Error('Failed to authorize oauth2 flow'), null]\n }\n}\n\n/**\n * Makes the BE authorization call to grab the token server to server\n * Used for clientCredentials and authorizationCode\n */\nconst authorizeServers = async (\n flows: NonImplicitFlows,\n type: keyof NonImplicitFlows,\n scopes: string,\n {\n code,\n pkce,\n proxyUrl,\n }: {\n code?: string\n pkce?: PKCEState | null\n proxyUrl?: string\n } = {},\n activeServer: ServerObject | null,\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n if (!flow) {\n return [new Error('OAuth2 flow was not defined'), null]\n }\n\n const formData = new URLSearchParams()\n\n // Only client credentials and password flows support scopes in the token request\n if (scopes && (type === 'clientCredentials' || type === 'password')) {\n formData.set('scope', scopes)\n }\n\n /** Where to add the credentials */\n const addCredentialsToBody = flow['x-scalar-credentials-location'] === 'body'\n const hasClientSecret = Boolean(flow['x-scalar-secret-client-secret'])\n /**\n * Public authorization-code clients still need client_id in the token body.\n * We only send it implicitly for that case to avoid conflicting with Basic auth.\n */\n const shouldSendClientIdInBody = addCredentialsToBody || (type === 'authorizationCode' && !hasClientSecret)\n\n if (shouldSendClientIdInBody) {\n formData.set('client_id', flow['x-scalar-secret-client-id'])\n }\n if (addCredentialsToBody && hasClientSecret) {\n formData.set('client_secret', flow['x-scalar-secret-client-secret'])\n }\n if ('x-scalar-secret-redirect-uri' in flow && flow['x-scalar-secret-redirect-uri']) {\n formData.set('redirect_uri', flow['x-scalar-secret-redirect-uri'])\n }\n\n // Authorization Code\n if (code) {\n formData.set('code', code)\n formData.set('grant_type', 'authorization_code')\n\n // PKCE\n if (pkce) {\n formData.set('code_verifier', pkce.codeVerifier)\n }\n }\n // Password\n else if (type === 'password') {\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n formData.set('grant_type', 'password')\n formData.set('username', typedFlow['x-scalar-secret-username'])\n formData.set('password', typedFlow['x-scalar-secret-password'])\n }\n // Client Credentials\n else {\n formData.set('grant_type', 'client_credentials')\n }\n\n // Additional request body parameters\n if (flow['x-scalar-security-body']) {\n Object.entries(flow['x-scalar-security-body']).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.set(key, String(value))\n }\n })\n }\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n // Add client id + secret to headers for confidential clients.\n if (!addCredentialsToBody && hasClientSecret) {\n headers.Authorization = `Basic ${encode(`${flow['x-scalar-secret-client-id']}:${flow['x-scalar-secret-client-secret']}`)}`\n }\n\n // Check if we should use the proxy\n const tokenUrl = makeUrlAbsolute(\n flow['x-scalar-secret-token-url'] ?? flow.tokenUrl,\n getActiveServerBase(activeServer, environmentVariables),\n )\n const url = shouldUseProxy(proxyUrl, tokenUrl)\n ? `${proxyUrl}?${new URLSearchParams([['scalar_url', tokenUrl]]).toString()}`\n : tokenUrl\n\n // Make the call\n const resp = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n })\n const responseData = await resp.json()\n\n // Use custom token name if specified, otherwise default to access_token\n const tokenName = flow['x-tokenName'] || 'access_token'\n const accessToken = responseData[tokenName]\n const refreshToken = responseData.refresh_token\n\n return [null, { accessToken, ...(typeof refreshToken === 'string' ? { refreshToken } : {}) }]\n } catch {\n return [new Error('Failed to get an access token. Please check your credentials.'), null]\n }\n}\n\n/**\n * Exchange a refresh token for a new access token using the `grant_type=refresh_token` flow.\n *\n * Uses the stored refresh token, client credentials, and the token URL (or refreshUrl when available)\n * to request fresh tokens from the authorization server per RFC 6749 Section 6.\n */\nexport const refreshOauth2Token = async (\n flows: OAuthFlowsObjectSecret,\n type: RefreshableFlows,\n /** If we want to use the proxy */\n proxyUrl: string,\n /** We use the active server to set a base for relative URLs */\n activeServer: ServerObject | null,\n /** Flattened environment variables used to resolve server URL templates */\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n if (!flow) {\n return [new Error('OAuth2 flow was not defined'), null]\n }\n\n const refreshToken = flow['x-scalar-secret-refresh-token']\n if (!refreshToken) {\n return [new Error('No refresh token available'), null]\n }\n\n const formData = new URLSearchParams()\n formData.set('grant_type', 'refresh_token')\n formData.set('refresh_token', refreshToken)\n\n const addCredentialsToBody = flow['x-scalar-credentials-location'] === 'body'\n const hasClientSecret = Boolean(flow['x-scalar-secret-client-secret'])\n /**\n * Public authorization-code clients still need client_id in the refresh body per RFC 6749 Section 6.\n * We only send it implicitly for that case to avoid conflicting with Basic auth.\n */\n const shouldSendClientIdInBody = addCredentialsToBody || (type === 'authorizationCode' && !hasClientSecret)\n\n if (shouldSendClientIdInBody) {\n formData.set('client_id', flow['x-scalar-secret-client-id'])\n }\n if (addCredentialsToBody && hasClientSecret) {\n formData.set('client_secret', flow['x-scalar-secret-client-secret'])\n }\n\n if (flow['x-scalar-security-body']) {\n Object.entries(flow['x-scalar-security-body']).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.set(key, String(value))\n }\n })\n }\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n if (!addCredentialsToBody && hasClientSecret) {\n headers.Authorization = `Basic ${encode(`${flow['x-scalar-secret-client-id']}:${flow['x-scalar-secret-client-secret']}`)}`\n }\n\n const refreshUrl = flow.refreshUrl || flow['x-scalar-secret-token-url'] || flow.tokenUrl\n const absoluteRefreshUrl = makeUrlAbsolute(refreshUrl, getActiveServerBase(activeServer, environmentVariables))\n const url = shouldUseProxy(proxyUrl, absoluteRefreshUrl)\n ? `${proxyUrl}?${new URLSearchParams([['scalar_url', absoluteRefreshUrl]]).toString()}`\n : absoluteRefreshUrl\n\n const resp = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n })\n const responseData = await resp.json()\n\n const tokenName = flow['x-tokenName'] || 'access_token'\n const accessToken = responseData[tokenName]\n const newRefreshToken = responseData.refresh_token\n\n if (!accessToken) {\n return [new Error(responseData.error_description ?? responseData.error ?? 'Token refresh failed'), null]\n }\n\n return [\n null,\n {\n accessToken,\n ...(typeof newRefreshToken === 'string' ? { refreshToken: newRefreshToken } : { refreshToken }),\n },\n ]\n } catch {\n return [new Error('Failed to refresh the access token. Please re-authorize.'), null]\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-auth-selector-block/helpers/oauth.ts"],"sourcesContent":["import type { ErrorResponse } from '@scalar/helpers/errors/normalize-error'\nimport { replaceEnvVariables, replacePathVariables } from '@scalar/helpers/regex/replace-variables'\nimport { isRelativePath } from '@scalar/helpers/url/is-relative-path'\nimport { makeUrlAbsolute } from '@scalar/helpers/url/make-url-absolute'\nimport { shouldUseProxy } from '@scalar/helpers/url/redirect-to-proxy'\nimport type { OAuthFlowsObjectSecret } from '@scalar/workspace-store/request-example'\nimport { getServerVariables } from '@scalar/workspace-store/request-example'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { encode, fromUint8Array } from 'js-base64'\n\nimport { getOAuthCallbackData } from './oauth-callback'\n\n/** Oauth2 security schemes which are not implicit */\ntype NonImplicitFlows = Omit<OAuthFlowsObjectSecret, 'implicit'>\n\ntype PKCEState = {\n codeVerifier: string\n codeChallenge: string\n codeChallengeMethod: string\n}\n\ntype ActiveServerBase = { basePath: string } | { baseUrl: string } | Record<string, never>\n\nexport type OAuth2Tokens = {\n accessToken: string\n refreshToken?: string\n}\n\n/** Flow types that support token refresh (all except implicit) */\ntype RefreshableFlows = Exclude<keyof OAuthFlowsObjectSecret, 'implicit'>\n\n/**\n * Resolves the active server URL using OpenAPI server variables first, then\n * Scalar environment variables.\n */\nexport const getServerUrl = (\n activeServer: ServerObject | null,\n environmentVariables: Record<string, string> = {},\n): string =>\n replaceEnvVariables(\n replacePathVariables(activeServer?.url ?? '', getServerVariables(activeServer)),\n environmentVariables,\n )\n\n/**\n * Builds the base option used when OAuth URLs are relative to the active server.\n *\n * Relative server URLs become browser-only base paths because they need the\n * current window location to resolve correctly.\n */\nexport const getActiveServerBase = (\n activeServer: ServerObject | null,\n environmentVariables: Record<string, string> = {},\n): ActiveServerBase => {\n const serverUrl = getServerUrl(activeServer, environmentVariables)\n if (!serverUrl) {\n return {}\n }\n\n if (isRelativePath(serverUrl)) {\n return typeof window === 'undefined' ? {} : { basePath: serverUrl }\n }\n\n return { baseUrl: serverUrl }\n}\n\n/**\n * Generates a random string for PKCE code verifier\n *\n * @see https://www.rfc-editor.org/rfc/rfc7636#page-8\n */\nconst generateCodeVerifier = (): string => {\n // Generate 32 random bytes\n const buffer = new Uint8Array(32)\n crypto.getRandomValues(buffer)\n\n // Base64URL encode the bytes\n return fromUint8Array(buffer, true)\n}\n\n/**\n * Creates a code challenge from the code verifier\n */\nconst generateCodeChallenge = async (verifier: string, encoding: 'SHA-256' | 'plain'): Promise<string> => {\n if (encoding === 'plain') {\n return verifier\n }\n\n // If crypto.subtle.digest is not a function, we cannot use SHA-256\n if (typeof crypto?.subtle?.digest !== 'function') {\n console.warn('SHA-256 is only supported when using https, using a plain text code challenge instead.')\n return verifier\n }\n\n // ASCII encoding is just taking the lower 8 bits of each character\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const digest = await crypto.subtle.digest('SHA-256', data)\n\n // Base64URL encode the bytes\n return fromUint8Array(new Uint8Array(digest), true)\n}\n\n/**\n * Authorize oauth2 flow\n *\n * @returns the resolved oauth2 tokens\n */\nexport const authorizeOauth2 = async (\n flows: OAuthFlowsObjectSecret,\n type: keyof OAuthFlowsObjectSecret,\n selectedScopes: string[],\n /** We use the active server to set a base for relative redirect uris */\n activeServer: ServerObject | null,\n /** If we want to use the proxy */\n proxyUrl: string,\n /** Flattened environment variables used to resolve server URL templates like `{protocol}` */\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n try {\n if (!flow) {\n return [new Error('Flow not found'), null]\n }\n\n const scopes = selectedScopes.join(' ')\n\n // Client Credentials or Password Flow\n if (type === 'clientCredentials' || type === 'password') {\n return authorizeServers(\n flows,\n type,\n scopes,\n {\n proxyUrl,\n },\n activeServer,\n environmentVariables,\n )\n }\n\n // Generate a random state string with the length of 8 characters\n const state = (Math.random() + 1).toString(36).substring(2, 10)\n\n const authorizationUrl = makeUrlAbsolute(\n flows[type]!['x-scalar-secret-auth-url'] ?? flows[type]!.authorizationUrl,\n getActiveServerBase(activeServer, environmentVariables),\n )\n\n const url = new URL(authorizationUrl)\n\n /** Special PKCE state */\n let pkce: PKCEState | null = null\n\n // Params unique to the flows\n if (type === 'implicit') {\n url.searchParams.set('response_type', 'token')\n } else if (type === 'authorizationCode') {\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n\n url.searchParams.set('response_type', 'code')\n\n // PKCE\n if (typedFlow['x-usePkce'] !== 'no') {\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier, typedFlow['x-usePkce'])\n\n // Set state for later verification\n pkce = {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: typedFlow['x-usePkce'] === 'SHA-256' ? 'S256' : 'plain',\n }\n\n // Set the code challenge and method on the url\n url.searchParams.set('code_challenge', codeChallenge)\n url.searchParams.set('code_challenge_method', pkce.codeChallengeMethod)\n }\n }\n\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n\n // Handle relative redirect uris\n if (typedFlow['x-scalar-secret-redirect-uri'].startsWith('/')) {\n const baseUrl =\n getServerUrl(activeServer, environmentVariables) || window.location.origin + window.location.pathname\n const redirectUri = new URL(typedFlow['x-scalar-secret-redirect-uri'], baseUrl).toString()\n\n url.searchParams.set('redirect_uri', redirectUri)\n } else {\n url.searchParams.set('redirect_uri', typedFlow['x-scalar-secret-redirect-uri'])\n }\n\n if (flow['x-scalar-security-query']) {\n Object.keys(flow['x-scalar-security-query']).forEach((key: string): void => {\n const value = flow['x-scalar-security-query']?.[key]\n if (!value) {\n return\n }\n url.searchParams.set(key, value)\n })\n }\n\n // Common to all flows\n url.searchParams.set('client_id', flow['x-scalar-secret-client-id'])\n url.searchParams.set('state', state)\n if (scopes) {\n url.searchParams.set('scope', scopes)\n }\n\n const windowFeatures = 'left=100,top=100,width=800,height=600'\n const authWindow = window.open(url, 'openAuth2Window', windowFeatures)\n\n // Open up a window and poll until closed or we have the data we want\n if (authWindow) {\n // We need to return a promise here due to the setInterval\n return new Promise<ErrorResponse<OAuth2Tokens>>((resolve) => {\n const checkWindowClosed = setInterval(() => {\n const { accessToken, accessTokenParams, code, codeParams, error, errorDescription, refreshToken } =\n getOAuthCallbackData(() => authWindow.location.href, flow['x-tokenName'] || 'access_token')\n\n // The window has closed OR we have what we are looking for so we stop polling\n if (authWindow.closed || accessToken || code || error) {\n clearInterval(checkWindowClosed)\n authWindow.close()\n\n if (error) {\n resolve([new Error(`OAuth error: ${error}${errorDescription ? ` (${errorDescription})` : ''}`), null])\n return\n }\n\n // Implicit Flow\n if (accessToken) {\n const _state = accessTokenParams?.get('state') ?? null\n\n if (_state === state) {\n resolve([null, { accessToken, ...(refreshToken ? { refreshToken } : {}) }])\n } else {\n resolve([new Error('State mismatch'), null])\n }\n return\n }\n\n // Authorization Code Server Flow\n if (code && type === 'authorizationCode') {\n const _state = codeParams?.get('state') ?? null\n\n if (_state === state) {\n void authorizeServers(\n flows,\n type,\n scopes,\n {\n code,\n pkce,\n proxyUrl,\n },\n activeServer,\n environmentVariables,\n )\n .then(resolve)\n .catch((error: unknown) => {\n resolve([\n error instanceof Error ? error : new Error('Failed to get an access token', { cause: error }),\n null,\n ])\n })\n } else {\n resolve([new Error('State mismatch'), null])\n }\n return\n }\n\n // User closed window without authorizing\n resolve([new Error('Window was closed without granting authorization'), null])\n }\n }, 200)\n })\n }\n return [new Error('Failed to open auth window'), null]\n } catch (error) {\n return [error instanceof Error ? error : new Error('Failed to authorize oauth2 flow', { cause: error }), null]\n }\n}\n\n/**\n * Makes the BE authorization call to grab the token server to server\n * Used for clientCredentials and authorizationCode\n */\nconst authorizeServers = async (\n flows: NonImplicitFlows,\n type: keyof NonImplicitFlows,\n scopes: string,\n {\n code,\n pkce,\n proxyUrl,\n }: {\n code?: string\n pkce?: PKCEState | null\n proxyUrl?: string\n } = {},\n activeServer: ServerObject | null,\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n if (!flow) {\n return [new Error('OAuth2 flow was not defined'), null]\n }\n\n const formData = new URLSearchParams()\n\n // Only client credentials and password flows support scopes in the token request\n if (scopes && (type === 'clientCredentials' || type === 'password')) {\n formData.set('scope', scopes)\n }\n\n /** Where to add the credentials */\n const addCredentialsToBody = flow['x-scalar-credentials-location'] === 'body'\n const hasClientSecret = Boolean(flow['x-scalar-secret-client-secret'])\n /**\n * Public authorization-code clients still need client_id in the token body.\n * We only send it implicitly for that case to avoid conflicting with Basic auth.\n */\n const shouldSendClientIdInBody = addCredentialsToBody || (type === 'authorizationCode' && !hasClientSecret)\n\n if (shouldSendClientIdInBody) {\n formData.set('client_id', flow['x-scalar-secret-client-id'])\n }\n if (addCredentialsToBody && hasClientSecret) {\n formData.set('client_secret', flow['x-scalar-secret-client-secret'])\n }\n if ('x-scalar-secret-redirect-uri' in flow && flow['x-scalar-secret-redirect-uri']) {\n formData.set('redirect_uri', flow['x-scalar-secret-redirect-uri'])\n }\n\n // Authorization Code\n if (code) {\n formData.set('code', code)\n formData.set('grant_type', 'authorization_code')\n\n // PKCE\n if (pkce) {\n formData.set('code_verifier', pkce.codeVerifier)\n }\n }\n // Password\n else if (type === 'password') {\n const typedFlow = flows[type]! // Safe to assert due to earlier check\n formData.set('grant_type', 'password')\n formData.set('username', typedFlow['x-scalar-secret-username'])\n formData.set('password', typedFlow['x-scalar-secret-password'])\n }\n // Client Credentials\n else {\n formData.set('grant_type', 'client_credentials')\n }\n\n // Additional request body parameters\n if (flow['x-scalar-security-body']) {\n Object.entries(flow['x-scalar-security-body']).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.set(key, String(value))\n }\n })\n }\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n // Add client id + secret to headers for confidential clients.\n if (!addCredentialsToBody && hasClientSecret) {\n headers.Authorization = `Basic ${encode(`${flow['x-scalar-secret-client-id']}:${flow['x-scalar-secret-client-secret']}`)}`\n }\n\n // Check if we should use the proxy\n const tokenUrl = makeUrlAbsolute(\n flow['x-scalar-secret-token-url'] ?? flow.tokenUrl,\n getActiveServerBase(activeServer, environmentVariables),\n )\n const url = shouldUseProxy(proxyUrl, tokenUrl)\n ? `${proxyUrl}?${new URLSearchParams([['scalar_url', tokenUrl]]).toString()}`\n : tokenUrl\n\n // Make the call\n const resp = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n })\n const responseData = await resp.json()\n\n // Use custom token name if specified, otherwise default to access_token\n const tokenName = flow['x-tokenName'] || 'access_token'\n const accessToken = responseData[tokenName]\n const refreshToken = responseData.refresh_token\n\n if (!accessToken) {\n return [new Error(responseData.error_description ?? responseData.error ?? 'Failed to get an access token'), null]\n }\n\n return [null, { accessToken, ...(typeof refreshToken === 'string' ? { refreshToken } : {}) }]\n } catch (error) {\n return [\n error instanceof Error\n ? error\n : new Error('Failed to get an access token. Please check your credentials.', { cause: error }),\n null,\n ]\n }\n}\n\n/**\n * Exchange a refresh token for a new access token using the `grant_type=refresh_token` flow.\n *\n * Uses the stored refresh token, client credentials, and the token URL (or refreshUrl when available)\n * to request fresh tokens from the authorization server per RFC 6749 Section 6.\n */\nexport const refreshOauth2Token = async (\n flows: OAuthFlowsObjectSecret,\n type: RefreshableFlows,\n /** If we want to use the proxy */\n proxyUrl: string,\n /** We use the active server to set a base for relative URLs */\n activeServer: ServerObject | null,\n /** Flattened environment variables used to resolve server URL templates */\n environmentVariables: Record<string, string> = {},\n): Promise<ErrorResponse<OAuth2Tokens>> => {\n const flow = flows[type]\n\n if (!flow) {\n return [new Error('OAuth2 flow was not defined'), null]\n }\n\n const refreshToken = flow['x-scalar-secret-refresh-token']\n if (!refreshToken) {\n return [new Error('No refresh token available'), null]\n }\n\n const formData = new URLSearchParams()\n formData.set('grant_type', 'refresh_token')\n formData.set('refresh_token', refreshToken)\n\n const addCredentialsToBody = flow['x-scalar-credentials-location'] === 'body'\n const hasClientSecret = Boolean(flow['x-scalar-secret-client-secret'])\n /**\n * Public authorization-code clients still need client_id in the refresh body per RFC 6749 Section 6.\n * We only send it implicitly for that case to avoid conflicting with Basic auth.\n */\n const shouldSendClientIdInBody = addCredentialsToBody || (type === 'authorizationCode' && !hasClientSecret)\n\n if (shouldSendClientIdInBody) {\n formData.set('client_id', flow['x-scalar-secret-client-id'])\n }\n if (addCredentialsToBody && hasClientSecret) {\n formData.set('client_secret', flow['x-scalar-secret-client-secret'])\n }\n\n if (flow['x-scalar-security-body']) {\n Object.entries(flow['x-scalar-security-body']).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.set(key, String(value))\n }\n })\n }\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n }\n\n if (!addCredentialsToBody && hasClientSecret) {\n headers.Authorization = `Basic ${encode(`${flow['x-scalar-secret-client-id']}:${flow['x-scalar-secret-client-secret']}`)}`\n }\n\n const refreshUrl = flow.refreshUrl || flow['x-scalar-secret-token-url'] || flow.tokenUrl\n const absoluteRefreshUrl = makeUrlAbsolute(refreshUrl, getActiveServerBase(activeServer, environmentVariables))\n const url = shouldUseProxy(proxyUrl, absoluteRefreshUrl)\n ? `${proxyUrl}?${new URLSearchParams([['scalar_url', absoluteRefreshUrl]]).toString()}`\n : absoluteRefreshUrl\n\n const resp = await fetch(url, {\n method: 'POST',\n headers,\n body: formData,\n })\n const responseData = await resp.json()\n\n const tokenName = flow['x-tokenName'] || 'access_token'\n const accessToken = responseData[tokenName]\n const newRefreshToken = responseData.refresh_token\n\n if (!accessToken) {\n return [new Error(responseData.error_description ?? responseData.error ?? 'Token refresh failed'), null]\n }\n\n return [\n null,\n {\n accessToken,\n ...(typeof newRefreshToken === 'string' ? { refreshToken: newRefreshToken } : { refreshToken }),\n },\n ]\n } catch (error) {\n return [\n error instanceof Error\n ? error\n : new Error('Failed to refresh the access token. Please re-authorize.', { cause: error }),\n null,\n ]\n }\n}\n"],"mappings":"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"}
|
package/dist/v2/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/v2/constants.ts
|
|
2
2
|
/** The version number taken from the package.json. Consumers can override at build time via define (e.g. OVERRIDE_PACKAGE_VERSION: JSON.stringify('1.2.3')). */
|
|
3
|
-
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "3.
|
|
3
|
+
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "3.5.0";
|
|
4
4
|
//#endregion
|
|
5
5
|
export { APP_VERSION };
|
|
6
6
|
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"rest",
|
|
19
19
|
"testing"
|
|
20
20
|
],
|
|
21
|
-
"version": "3.
|
|
21
|
+
"version": "3.5.0",
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=22"
|
|
24
24
|
},
|
|
@@ -332,22 +332,22 @@
|
|
|
332
332
|
"vue-router": "5.0.4",
|
|
333
333
|
"yaml": "^2.8.3",
|
|
334
334
|
"zod": "^4.3.5",
|
|
335
|
-
"@scalar/components": "0.
|
|
336
|
-
"@scalar/helpers": "0.5.3",
|
|
337
|
-
"@scalar/oas-utils": "0.14.0",
|
|
338
|
-
"@scalar/postman-to-openapi": "0.7.2",
|
|
339
|
-
"@scalar/json-magic": "0.12.9",
|
|
340
|
-
"@scalar/openapi-types": "0.8.0",
|
|
341
|
-
"@scalar/snippetz": "0.9.3",
|
|
335
|
+
"@scalar/components": "0.24.0",
|
|
342
336
|
"@scalar/icons": "0.7.2",
|
|
343
|
-
"@scalar/
|
|
344
|
-
"@scalar/
|
|
345
|
-
"@scalar/
|
|
337
|
+
"@scalar/helpers": "0.5.4",
|
|
338
|
+
"@scalar/postman-to-openapi": "0.7.3",
|
|
339
|
+
"@scalar/oas-utils": "0.15.0",
|
|
340
|
+
"@scalar/sidebar": "0.9.8",
|
|
341
|
+
"@scalar/openapi-types": "0.8.0",
|
|
342
|
+
"@scalar/json-magic": "0.12.10",
|
|
343
|
+
"@scalar/snippetz": "0.9.4",
|
|
344
|
+
"@scalar/types": "0.9.4",
|
|
346
345
|
"@scalar/use-codemirror": "0.14.11",
|
|
347
346
|
"@scalar/use-hooks": "0.4.3",
|
|
348
347
|
"@scalar/use-toasts": "0.10.2",
|
|
349
|
-
"@scalar/
|
|
350
|
-
"@scalar/
|
|
348
|
+
"@scalar/themes": "0.15.3",
|
|
349
|
+
"@scalar/validation": "0.3.2",
|
|
350
|
+
"@scalar/workspace-store": "0.49.0"
|
|
351
351
|
},
|
|
352
352
|
"devDependencies": {
|
|
353
353
|
"@tailwindcss/vite": "^4.2.0",
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"vite": "8.0.0",
|
|
363
363
|
"vite-svg-loader": "5.1.1",
|
|
364
364
|
"vitest": "4.1.0",
|
|
365
|
-
"@scalar/pre-post-request-scripts": "0.4.
|
|
365
|
+
"@scalar/pre-post-request-scripts": "0.4.9"
|
|
366
366
|
},
|
|
367
367
|
"scripts": {
|
|
368
368
|
"build": "vite build && vue-tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|