Skip to content

Commit 027b295

Browse files
committed
fix: typescript-axios generator passes allowsAnonymous flag to auth functions (#22751)
- Add allowsAnonymous field to CodegenOperation to track if endpoint allows anonymous access - Update DefaultGenerator to detect empty SecurityRequirement (anonymous allowed) - Pass allowsAnonymous flag through templates to authentication helper functions - Update auth functions to accept allowsAnonymous parameter - When allowsAnonymous is true, auth functions gracefully handle failures Fixes #22751
1 parent 3b3ae6e commit 027b295

4 files changed

Lines changed: 94 additions & 24 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public class CodegenOperation {
3030
hasVersionHeaders = false, hasVersionQueryParams = false,
3131
isResponseBinary = false, isResponseFile = false, isResponseOptional = false, hasReference = false, defaultReturnType = false,
3232
isDeprecated, isCallbackRequest, uniqueItems,
33-
hasErrorResponseObject; // if 4xx, 5xx responses have at least one error object defined
33+
hasErrorResponseObject, // if 4xx, 5xx responses have at least one error object defined
34+
allowsAnonymous = false; // if the operation allows anonymous access (empty security requirement)
3435
public CodegenProperty returnProperty;
3536
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
3637
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
@@ -403,6 +404,7 @@ public String toString() {
403404
final StringBuffer sb = new StringBuffer("CodegenOperation{");
404405
sb.append("responseHeaders=").append(responseHeaders);
405406
sb.append(", hasAuthMethods=").append(hasAuthMethods);
407+
sb.append(", allowsAnonymous=").append(allowsAnonymous);
406408
sb.append(", hasConsumes=").append(hasConsumes);
407409
sb.append(", hasProduces=").append(hasProduces);
408410
sb.append(", hasOptionalParams=").append(hasOptionalParams);
@@ -475,6 +477,7 @@ public boolean equals(Object o) {
475477
if (o == null || getClass() != o.getClass()) return false;
476478
CodegenOperation that = (CodegenOperation) o;
477479
return hasAuthMethods == that.hasAuthMethods &&
480+
allowsAnonymous == that.allowsAnonymous &&
478481
hasConsumes == that.hasConsumes &&
479482
hasProduces == that.hasProduces &&
480483
hasOptionalParams == that.hasOptionalParams &&
@@ -543,7 +546,7 @@ public boolean equals(Object o) {
543546
@Override
544547
public int hashCode() {
545548

546-
return Objects.hash(responseHeaders, hasAuthMethods, hasConsumes, hasProduces, hasOptionalParams,
549+
return Objects.hash(responseHeaders, hasAuthMethods, allowsAnonymous, hasConsumes, hasProduces, hasOptionalParams,
547550
returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMap,
548551
isArray, isMultipart, isVoid, isResponseBinary, isResponseFile, isResponseOptional, hasReference,
549552
isDeprecated, isCallbackRequest, uniqueItems, path, operationId, returnType, httpMethod,

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,19 @@ private void processOperation(String resourcePath, String httpMethod, Operation
15791579
continue;
15801580
}
15811581

1582+
// Check if anonymous access is allowed (empty SecurityRequirement means anonymous is allowed)
1583+
// This must be checked before processing auth methods to properly handle mixed security requirements
1584+
boolean allowsAnonymous = checkAllowsAnonymous(securities);
1585+
if (!allowsAnonymous && securities == null) {
1586+
// If no operation-level securities, check global securities
1587+
allowsAnonymous = checkAllowsAnonymous(globalSecurities);
1588+
}
1589+
// If no securities at all (neither operation nor global), anonymous is allowed by default
1590+
if (!allowsAnonymous && securities == null && (globalSecurities == null || globalSecurities.isEmpty())) {
1591+
allowsAnonymous = true;
1592+
}
1593+
codegenOperation.allowsAnonymous = allowsAnonymous;
1594+
15821595
Map<String, SecurityScheme> authMethods = getAuthMethods(securities, securitySchemes);
15831596

15841597
if (authMethods != null && !authMethods.isEmpty()) {
@@ -1788,6 +1801,25 @@ private ModelsMap processModels(CodegenConfig config, Map<String, Schema> defini
17881801
return objs;
17891802
}
17901803

1804+
/**
1805+
* Check if the security requirements allow anonymous access.
1806+
* An empty SecurityRequirement (empty map) indicates anonymous access is allowed.
1807+
*
1808+
* @param securities List of security requirements to check
1809+
* @return true if any security requirement is empty (allows anonymous), false otherwise
1810+
*/
1811+
private boolean checkAllowsAnonymous(List<SecurityRequirement> securities) {
1812+
if (securities == null || securities.isEmpty()) {
1813+
return false;
1814+
}
1815+
for (SecurityRequirement req : securities) {
1816+
if (req == null || req.isEmpty()) {
1817+
return true;
1818+
}
1819+
}
1820+
return false;
1821+
}
1822+
17911823
private Map<String, SecurityScheme> getAuthMethods(List<SecurityRequirement> securities, Map<String, SecurityScheme> securitySchemes) {
17921824
if (securities == null || (securitySchemes == null || securitySchemes.isEmpty())) {
17931825
return null;

modules/openapi-generator/src/main/resources/typescript-axios/apiInner.mustache

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const {{classname}}AxiosParamCreator = function (configuration?: Configur
6767
const localVarHeaderParameter = {} as any;
6868
const localVarQueryParameter = {} as any;{{#vendorExtensions}}{{#hasFormParams}}
6969
const localVarFormParams = new {{^multipartFormData}}URLSearchParams(){{/multipartFormData}}{{#multipartFormData}}((configuration && configuration.formDataCtor) || FormData)(){{/multipartFormData}};{{/hasFormParams}}{{/vendorExtensions}}
70+
const allowsAnonymous = {{allowsAnonymous}};
7071
7172
{{#authMethods}}
7273
// authentication {{name}} required
@@ -76,23 +77,23 @@ export const {{classname}}AxiosParamCreator = function (configuration?: Configur
7677
await setAWS4SignatureInterceptor(globalAxios, configuration)
7778
{{/withAWSV4Signature}}
7879
{{#isKeyInHeader}}
79-
await setApiKeyToObject(localVarHeaderParameter, "{{keyParamName}}", configuration)
80+
await setApiKeyToObject(localVarHeaderParameter, "{{keyParamName}}", configuration, allowsAnonymous)
8081
{{/isKeyInHeader}}
8182
{{#isKeyInQuery}}
82-
await setApiKeyToObject(localVarQueryParameter, "{{keyParamName}}", configuration)
83+
await setApiKeyToObject(localVarQueryParameter, "{{keyParamName}}", configuration, allowsAnonymous)
8384
{{/isKeyInQuery}}
8485
{{/isApiKey}}
8586
{{#isBasicBasic}}
8687
// http basic authentication required
87-
setBasicAuthToObject(localVarRequestOptions, configuration)
88+
setBasicAuthToObject(localVarRequestOptions, configuration, allowsAnonymous)
8889
{{/isBasicBasic}}
8990
{{#isBasicBearer}}
9091
// http bearer authentication required
91-
await setBearerAuthToObject(localVarHeaderParameter, configuration)
92+
await setBearerAuthToObject(localVarHeaderParameter, configuration, allowsAnonymous)
9293
{{/isBasicBearer}}
9394
{{#isOAuth}}
9495
// oauth required
95-
await setOAuthToObject(localVarHeaderParameter, "{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}], configuration)
96+
await setOAuthToObject(localVarHeaderParameter, "{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}], configuration, allowsAnonymous)
9697
{{/isOAuth}}
9798
9899
{{/authMethods}}

modules/openapi-generator/src/main/resources/typescript-axios/common.mustache

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,70 @@ export const assertParamExists = function (functionName: string, paramName: stri
2525
}
2626
}
2727

28-
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
28+
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration, allowsAnonymous?: boolean) {
2929
if (configuration && configuration.apiKey) {
30-
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
31-
? await configuration.apiKey(keyParamName)
32-
: await configuration.apiKey;
33-
object[keyParamName] = localVarApiKeyValue;
30+
try {
31+
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
32+
? await configuration.apiKey(keyParamName)
33+
: await configuration.apiKey;
34+
if (localVarApiKeyValue) {
35+
object[keyParamName] = localVarApiKeyValue;
36+
}
37+
} catch (error) {
38+
if (!allowsAnonymous) {
39+
throw error;
40+
}
41+
// If anonymous is allowed, silently ignore authentication failures
42+
}
3443
}
3544
}
3645

37-
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
46+
export const setBasicAuthToObject = function (object: any, configuration?: Configuration, allowsAnonymous?: boolean) {
3847
if (configuration && (configuration.username || configuration.password)) {
39-
object["auth"] = { username: configuration.username, password: configuration.password };
48+
try {
49+
object["auth"] = { username: configuration.username, password: configuration.password };
50+
} catch (error) {
51+
if (!allowsAnonymous) {
52+
throw error;
53+
}
54+
// If anonymous is allowed, silently ignore authentication failures
55+
}
4056
}
4157
}
4258

43-
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
59+
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration, allowsAnonymous?: boolean) {
4460
if (configuration && configuration.accessToken) {
45-
const accessToken = typeof configuration.accessToken === 'function'
46-
? await configuration.accessToken()
47-
: await configuration.accessToken;
48-
object["Authorization"] = "Bearer " + accessToken;
61+
try {
62+
const accessToken = typeof configuration.accessToken === 'function'
63+
? await configuration.accessToken()
64+
: await configuration.accessToken;
65+
if (accessToken) {
66+
object["Authorization"] = "Bearer " + accessToken;
67+
}
68+
} catch (error) {
69+
if (!allowsAnonymous) {
70+
throw error;
71+
}
72+
// If anonymous is allowed, silently ignore authentication failures
73+
}
4974
}
5075
}
5176

52-
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
77+
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration, allowsAnonymous?: boolean) {
5378
if (configuration && configuration.accessToken) {
54-
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
55-
? await configuration.accessToken(name, scopes)
56-
: await configuration.accessToken;
57-
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
79+
try {
80+
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
81+
? await configuration.accessToken(name, scopes)
82+
: await configuration.accessToken;
83+
if (localVarAccessTokenValue) {
84+
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
85+
}
86+
} catch (error) {
87+
if (!allowsAnonymous) {
88+
throw error;
89+
}
90+
// If anonymous is allowed, silently ignore authentication failures
91+
}
5892
}
5993
}
6094

0 commit comments

Comments
 (0)