Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,60 @@ export class OpenAPIConverter extends AbstractSpecConverter<OpenAPIConverterCont
}

private convertSecuritySchemes(): void {
if (this.context.authOverrides) {
this.addAuthToIR(
convertApiAuth({
rawApiFileSchema: this.context.authOverrides,
casingsGenerator: this.context.casingsGenerator
})
);
const hasGlobalSecurity = this.context.spec.security != null && this.context.spec.security.length > 0;

// Convert all OpenAPI security schemes (reused across all branches)
const openApiSchemes = this.convertOpenApiSecuritySchemes();

// If we have auth overrides and no global security in OpenAPI, use overrides as primary auth
if (this.context.authOverrides && !hasGlobalSecurity) {
// Convert auth overrides to primary auth (this includes both schemes and global auth)
const overrideAuth = convertApiAuth({
rawApiFileSchema: this.context.authOverrides,
casingsGenerator: this.context.casingsGenerator
});

// Use override auth as primary with additional OpenAPI schemes for references
this.addAuthToIR({
requirement: overrideAuth.requirement,
schemes: [...overrideAuth.schemes, ...openApiSchemes],
docs: overrideAuth.docs
});
return;
}

// If we have auth overrides but there IS global security in OpenAPI, merge them
if (this.context.authOverrides && hasGlobalSecurity) {
// First, add auth overrides from generators.yml
const overrideAuth = convertApiAuth({
rawApiFileSchema: this.context.authOverrides,
casingsGenerator: this.context.casingsGenerator
});

const allAuthSchemes = [...overrideAuth.schemes, ...openApiSchemes];

this.addAuthToIR({
requirement: allAuthSchemes.length === 1 ? "ALL" : "ANY",
schemes: allAuthSchemes,
docs: undefined
});
return;
}

// Original logic: No auth overrides, just convert OpenAPI security schemes
if (openApiSchemes.length > 0) {
// TODO(kenny): we're not using `requirement` here, and should remove.
// this field is oversimplified, since it implies either all,
// or any, but endpoints can have a subset.
this.addAuthToIR({
requirement: openApiSchemes.length === 1 ? "ALL" : "ANY",
schemes: openApiSchemes,
docs: undefined
});
}
}

private convertOpenApiSecuritySchemes(): AuthScheme[] {
const securitySchemes: AuthScheme[] = [];

for (const [id, securityScheme] of Object.entries(this.context.spec.components?.securitySchemes ?? {})) {
Expand All @@ -98,23 +142,12 @@ export class OpenAPIConverter extends AbstractSpecConverter<OpenAPIConverterCont
schemeId: id
});
const convertedScheme = securitySchemeConverter.convert();
if (convertedScheme == null) {
continue;
if (convertedScheme != null) {
securitySchemes.push(convertedScheme);
}

securitySchemes.push(convertedScheme);
}

if (securitySchemes.length > 0) {
// TODO(kenny): we're not using `requirement` here, and should remove.
// this field is oversimplified, since it implies either all,
// or any, but endpoints can have a subset.
this.addAuthToIR({
requirement: securitySchemes.length === 1 ? "ALL" : "ANY",
schemes: securitySchemes,
docs: undefined
});
}
return securitySchemes;
}

private convertServers({ endpointLevelServers }: { endpointLevelServers?: OpenAPIV3_1.ServerObject[] }): {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,12 @@ export class OperationConverter extends AbstractOperationConverter {
),
sdkRequest: undefined,
errors,
auth: this.operation.security != null || this.context.spec.security != null,
security: this.operation.security ?? this.context.spec.security,
auth:
this.operation.security != null ||
this.context.spec.security != null ||
this.shouldApplyDefaultAuthOverrides(),
security:
this.operation.security ?? this.context.spec.security ?? this.getDefaultSecurityFromAuthOverrides(),
availability: this.context.getAvailability({
node: this.operation,
breadcrumbs: this.breadcrumbs
Expand Down Expand Up @@ -456,6 +460,38 @@ export class OperationConverter extends AbstractOperationConverter {
* @param securitySchemeIds - List of security scheme IDs
* @returns Array of HTTP headers derived from the security schemes
*/
private shouldApplyDefaultAuthOverrides(): boolean {
if (!this.context.authOverrides?.auth) {
return false;
}

const hasGlobalSecurity = this.context.spec.security != null && this.context.spec.security.length > 0;
const hasEndpointSecurity = this.operation.security != null;

// If there's already security defined (global or endpoint), don't apply defaults
return !(hasGlobalSecurity || hasEndpointSecurity);
}

private getDefaultSecurityFromAuthOverrides(): OpenAPIV3_1.SecurityRequirementObject[] | undefined {
// If we have auth overrides but no OpenAPI security, create a default security requirement
// that references the global auth scheme from generators.yml
if (!this.context.authOverrides?.auth) {
return undefined;
}

// The auth field from generators.yml contains the scheme name (e.g., "bearerAuth")
// Convert this to OpenAPI security requirement format
const authSchemeName = this.context.authOverrides.auth;
if (typeof authSchemeName !== "string") {
return undefined;
}

// Return OpenAPI security requirement format: [{ "schemeName": [] }]
const securityRequirement: OpenAPIV3_1.SecurityRequirementObject = {};
securityRequirement[authSchemeName] = [];
return [securityRequirement];
}

private authSchemeToHeaders(securitySchemeIds: string[]): FernIr.HttpHeader[] {
const headers: FernIr.HttpHeader[] = [];

Expand Down
Loading
Loading