diff --git a/.changeset/stupid-hats-reflect.md b/.changeset/stupid-hats-reflect.md new file mode 100644 index 0000000000..3c370a07bf --- /dev/null +++ b/.changeset/stupid-hats-reflect.md @@ -0,0 +1,5 @@ +--- +'@gitbook/react-openapi': patch +--- + +Handle optional security headers diff --git a/packages/react-openapi/src/OpenAPISecurities.tsx b/packages/react-openapi/src/OpenAPISecurities.tsx index 465afeb088..fcdd003fd6 100644 --- a/packages/react-openapi/src/OpenAPISecurities.tsx +++ b/packages/react-openapi/src/OpenAPISecurities.tsx @@ -1,8 +1,11 @@ -import type { OpenAPIV3_1 } from '@gitbook/openapi-parser'; import { InteractiveSection } from './InteractiveSection'; import { Markdown } from './Markdown'; import { OpenAPISchemaName } from './OpenAPISchemaName'; -import type { OpenAPIClientContext, OpenAPIOperationData } from './types'; +import type { + OpenAPIClientContext, + OpenAPIOperationData, + OpenAPISecurityWithRequired, +} from './types'; import { resolveDescription } from './utils'; /** @@ -50,26 +53,36 @@ export function OpenAPISecurities(props: { ); } -function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) { +function getLabelForType(security: OpenAPISecurityWithRequired) { switch (security.type) { case 'apiKey': return ( ); case 'http': if (security.scheme === 'basic') { - return ; + return ( + + ); } if (security.scheme === 'bearer') { const description = resolveDescription(security); return ( <> - + {/** Show a default description if none is provided */} {!description ? ( ; + return ; case 'oauth2': - return ; + return ; case 'openIdConnect': - return ; + return ; default: // @ts-ignore return security.type; diff --git a/packages/react-openapi/src/resolveOpenAPIOperation.ts b/packages/react-openapi/src/resolveOpenAPIOperation.ts index 295f2b79ce..ba65b8bd66 100644 --- a/packages/react-openapi/src/resolveOpenAPIOperation.ts +++ b/packages/react-openapi/src/resolveOpenAPIOperation.ts @@ -40,16 +40,27 @@ export async function resolveOpenAPIOperation( } const servers = 'servers' in schema ? (schema.servers ?? []) : []; - const security = flattenSecurities(operation.security ?? schema.security ?? []); + const security: OpenAPIV3_1.SecurityRequirementObject[] = + operation.security ?? schema.security ?? []; + + // If security includes an empty object, it means that the security is optional + const isOptionalSecurity = security.some((entry) => Object.keys(entry).length === 0); + const flatSecurities = flattenSecurities(security); // Resolve securities const securities: OpenAPIOperationData['securities'] = []; - for (const entry of security) { + for (const entry of flatSecurities) { const securityKey = Object.keys(entry)[0]; if (securityKey) { const securityScheme = schema.components?.securitySchemes?.[securityKey]; if (securityScheme && !checkIsReference(securityScheme)) { - securities.push([securityKey, securityScheme]); + securities.push([ + securityKey, + { + ...securityScheme, + required: !isOptionalSecurity, + }, + ]); } } } diff --git a/packages/react-openapi/src/types.ts b/packages/react-openapi/src/types.ts index 39b9899e9b..94dad6f5d7 100644 --- a/packages/react-openapi/src/types.ts +++ b/packages/react-openapi/src/types.ts @@ -57,6 +57,8 @@ export interface OpenAPIContext extends OpenAPIClientContext { specUrl: string; } +export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject & { required?: boolean }; + export interface OpenAPIOperationData extends OpenAPICustomSpecProperties { path: string; method: string; @@ -68,5 +70,5 @@ export interface OpenAPIOperationData extends OpenAPICustomSpecProperties { operation: OpenAPIV3.OperationObject; /** Securities that should be used for this operation */ - securities: [string, OpenAPIV3.SecuritySchemeObject][]; + securities: [string, OpenAPISecurityWithRequired][]; }