Skip to content

Commit 9087fc1

Browse files
yarokonDmitryAnansky
authored andcommitted
feat(respect): enhance OAS3 security scheme types and add getSecurityParameters utility function (#2077)
1 parent cb38dc0 commit 9087fc1

File tree

9 files changed

+275
-34
lines changed

9 files changed

+275
-34
lines changed

packages/core/src/rules/respect/no-x-security-scheme-name-without-openapi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const NoXSecuritySchemeNameWithoutOpenAPI: Arazzo1Rule = () => {
1818
}
1919

2020
for (const security of extendedSecurity) {
21-
if (security?.schemeName) {
21+
if ('schemeName' in security) {
2222
report({
2323
message:
2424
'The `schemeName` can be only used in Step with OpenAPI operation, please use `scheme` instead.',

packages/core/src/typings/arazzo.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Oas3SecurityScheme } from './openapi.js';
1+
import type { Oas3SecurityScheme, ApiKeyAuth, BasicAuth, BearerAuth } from './openapi.js';
22

33
export interface InfoObject {
44
title: string;
@@ -29,11 +29,42 @@ export interface Parameter {
2929
reference?: string;
3030
}
3131

32-
export interface ExtendedSecurity {
33-
schemeName?: string;
34-
values: Record<string, string>;
35-
schema?: Oas3SecurityScheme;
36-
}
32+
export type ExtendedSecurity =
33+
| {
34+
schemeName: string;
35+
values: Record<string, string>;
36+
}
37+
| {
38+
scheme: Oas3SecurityScheme;
39+
values: Record<string, string>;
40+
};
41+
42+
export type ResolvedSecurity =
43+
| {
44+
scheme: ApiKeyAuth;
45+
values: {
46+
value: string;
47+
};
48+
}
49+
| {
50+
scheme: BasicAuth;
51+
values: {
52+
username: string;
53+
password: string;
54+
};
55+
}
56+
| {
57+
scheme: BearerAuth;
58+
values: {
59+
token: string;
60+
};
61+
}
62+
| {
63+
scheme: Exclude<Oas3SecurityScheme, ApiKeyAuth | BasicAuth | BearerAuth>;
64+
values: {
65+
accessToken: string;
66+
};
67+
};
3768

3869
export interface ExtendedOperation {
3970
url: string;

packages/core/src/typings/openapi.ts

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -291,37 +291,83 @@ export interface Oas3SecurityRequirement {
291291
[name: string]: string[];
292292
}
293293

294-
export interface Oas3SecurityScheme {
295-
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect' | 'mutualTLS';
294+
type SecuritySchemeBase = {
296295
description?: string;
297-
name?: string;
298-
in?: 'query' | 'header' | 'cookie';
299-
scheme?: string;
296+
[key: `x-${string}`]: unknown;
297+
};
298+
299+
export type ApiKeyAuth = {
300+
type: 'apiKey';
301+
in: 'query' | 'header' | 'cookie';
302+
name: string;
303+
} & SecuritySchemeBase;
304+
305+
export type HttpAuth = {
306+
type: 'http';
307+
scheme: string;
308+
} & SecuritySchemeBase;
309+
310+
export type BasicAuth = {
311+
type: 'http';
312+
scheme: 'basic';
313+
} & SecuritySchemeBase;
314+
315+
export type BearerAuth = {
316+
type: 'http';
317+
scheme: 'bearer';
300318
bearerFormat?: string;
319+
} & SecuritySchemeBase;
320+
321+
export type DigestAuth = {
322+
type: 'http';
323+
scheme: 'digest';
324+
} & SecuritySchemeBase;
325+
326+
export type MutualTLSAuth = {
327+
type: 'mutualTLS';
328+
} & SecuritySchemeBase;
329+
330+
export type OAuth2Auth = {
331+
type: 'oauth2';
301332
flows: {
302333
implicit?: {
303-
refreshUrl?: string;
304-
scopes: Record<string, string>;
305334
authorizationUrl: string;
335+
scopes: Record<string, string>;
336+
refreshUrl?: string;
306337
};
307338
password?: {
308-
refreshUrl?: string;
309-
scopes: Record<string, string>;
310339
tokenUrl: string;
340+
scopes: Record<string, string>;
341+
refreshUrl?: string;
311342
};
312343
clientCredentials?: {
313-
refreshUrl?: string;
314-
scopes: Record<string, string>;
315344
tokenUrl: string;
345+
scopes: Record<string, string>;
346+
refreshUrl?: string;
316347
};
317348
authorizationCode?: {
318-
refreshUrl?: string;
319-
scopes: Record<string, string>;
349+
authorizationUrl: string;
320350
tokenUrl: string;
351+
scopes: Record<string, string>;
352+
refreshUrl?: string;
321353
};
322354
};
323-
openIdConnectUrl?: string;
324-
}
355+
} & SecuritySchemeBase;
356+
357+
export type OpenIDAuth = {
358+
type: 'openIdConnect';
359+
openIdConnectUrl: string;
360+
} & SecuritySchemeBase;
361+
362+
export type Oas3SecurityScheme =
363+
| ApiKeyAuth
364+
| HttpAuth
365+
| BasicAuth
366+
| BearerAuth
367+
| DigestAuth
368+
| MutualTLSAuth
369+
| OAuth2Auth
370+
| OpenIDAuth;
325371

326372
export interface Oas3Tag {
327373
name: string;

packages/core/src/visitors.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {
2727
Oas3RequestBody,
2828
Oas3Tag,
2929
OasRef,
30+
OAuth2Auth,
3031
Oas3SecurityScheme,
3132
Oas3SecurityRequirement,
3233
Oas3Encoding,
@@ -197,11 +198,12 @@ type Oas3FlatVisitor = {
197198
NamedSecuritySchemes?: VisitFunctionOrObject<Record<string, Oas3SecurityScheme>>;
198199
NamedLinks?: VisitFunctionOrObject<Record<string, Oas3Link>>;
199200
NamedCallbacks?: VisitFunctionOrObject<Record<string, Oas3Callback<Oas3Schema | Oas3_1Schema>>>;
200-
ImplicitFlow?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['implicit']>;
201-
PasswordFlow?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['password']>;
202-
ClientCredentials?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['clientCredentials']>;
203-
AuthorizationCode?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['authorizationCode']>;
204-
OAuth2Flows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
201+
202+
ImplicitFlow?: VisitFunctionOrObject<NonNullable<OAuth2Auth['flows']['implicit']>>;
203+
PasswordFlow?: VisitFunctionOrObject<NonNullable<OAuth2Auth['flows']['password']>>;
204+
ClientCredentials?: VisitFunctionOrObject<NonNullable<OAuth2Auth['flows']['clientCredentials']>>;
205+
AuthorizationCode?: VisitFunctionOrObject<NonNullable<OAuth2Auth['flows']['authorizationCode']>>;
206+
OAuth2Flows?: VisitFunctionOrObject<OAuth2Auth['flows']>;
205207
SecurityScheme?: VisitFunctionOrObject<Oas3SecurityScheme>;
206208
SpecExtension?: VisitFunctionOrObject<unknown>;
207209
};

packages/respect-core/src/arazzo-schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export const extendedSecurity = {
114114
},
115115
},
116116
required: ['values'],
117-
requiredOneOf: ['schemeName', 'schema'],
117+
requiredOneOf: ['schemeName', 'scheme'],
118118
} as const;
119119
export const extendedSecurityList = {
120120
type: 'array',
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { getSecurityParameters } from '../../config-parser/get-security-parameters';
3+
4+
describe('getSecurityParameters', () => {
5+
it('should return security parameters for API Key Auth', () => {
6+
const result = getSecurityParameters({
7+
scheme: {
8+
type: 'apiKey',
9+
in: 'query',
10+
name: 'api_key',
11+
},
12+
values: {
13+
value: '12345678-ABCD-90EF-GHIJ-1234567890KL',
14+
},
15+
});
16+
17+
expect(result).toEqual({
18+
in: 'query',
19+
name: 'api_key',
20+
value: '12345678-ABCD-90EF-GHIJ-1234567890KL',
21+
});
22+
});
23+
24+
it('should return security parameters for Basic Auth', () => {
25+
const result = getSecurityParameters({
26+
scheme: {
27+
type: 'http',
28+
scheme: 'basic',
29+
},
30+
values: {
31+
username: 'username',
32+
password: 'password',
33+
},
34+
});
35+
36+
expect(result).toEqual({
37+
in: 'header',
38+
name: 'Authorization',
39+
value: `Basic dXNlcm5hbWU6cGFzc3dvcmQ=`,
40+
});
41+
});
42+
43+
it('should return security parameters for Bearer Auth', () => {
44+
const result = getSecurityParameters({
45+
scheme: {
46+
type: 'http',
47+
scheme: 'bearer',
48+
bearerFormat: 'JWT',
49+
},
50+
values: {
51+
token:
52+
'eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.LlTGHPZRXbci-y349jXXN0byQniQQqwKGybzQCFIgY0',
53+
},
54+
});
55+
56+
expect(result).toEqual({
57+
in: 'header',
58+
name: 'Authorization',
59+
value:
60+
'Bearer eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UifQ.LlTGHPZRXbci-y349jXXN0byQniQQqwKGybzQCFIgY0',
61+
});
62+
});
63+
64+
it('should return security parameters for OpenID Auth', () => {
65+
const result = getSecurityParameters({
66+
scheme: {
67+
type: 'openIdConnect',
68+
openIdConnectUrl: 'https://example.com',
69+
},
70+
values: {
71+
accessToken: 'openid-token',
72+
},
73+
});
74+
75+
expect(result).toEqual({
76+
in: 'header',
77+
name: 'Authorization',
78+
value: 'Bearer openid-token',
79+
});
80+
});
81+
82+
it('should return security parameters for OAuth2 Auth', () => {
83+
const result = getSecurityParameters({
84+
scheme: {
85+
type: 'oauth2',
86+
flows: {
87+
authorizationCode: {
88+
authorizationUrl: 'https://example.com/authorize',
89+
tokenUrl: 'https://example.com/token',
90+
scopes: {
91+
read: 'Read access',
92+
write: 'Write access',
93+
},
94+
},
95+
},
96+
},
97+
values: {
98+
accessToken: 'oauth2-token',
99+
},
100+
});
101+
102+
expect(result).toEqual({
103+
in: 'header',
104+
name: 'Authorization',
105+
value: 'Bearer oauth2-token',
106+
});
107+
});
108+
});

packages/respect-core/src/modules/arazzo-description-generator/generate-inputs-arazzo-components.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function generateSecurityInputsArazzoComponents(
1010
continue;
1111
}
1212

13-
if (securityScheme?.scheme?.toLowerCase() === 'basic') {
13+
if (securityScheme.type === 'http' && securityScheme.scheme?.toLowerCase() === 'basic') {
1414
inputs[name] = {
1515
type: 'object',
1616
properties: {
@@ -21,7 +21,10 @@ export function generateSecurityInputsArazzoComponents(
2121
},
2222
},
2323
};
24-
} else if (securityScheme?.scheme?.toLowerCase() === 'bearer') {
24+
} else if (
25+
securityScheme.type === 'http' &&
26+
securityScheme.scheme?.toLowerCase() === 'bearer'
27+
) {
2528
inputs[name] = {
2629
type: 'object',
2730
properties: {

packages/respect-core/src/modules/arazzo-description-generator/generate-workflow-security-parameters.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,17 @@ export function generateWorkflowSecurityParameters(
3232
value: `$inputs.${securityName}`,
3333
in: securityScheme?.in || 'header',
3434
});
35-
} else if (securityScheme?.scheme === 'bearer') {
35+
} else if (securityScheme?.type === 'http' && securityScheme?.scheme === 'bearer') {
3636
parameters.push({
3737
name: 'Authorization',
3838
value: `Bearer {$inputs.${securityName}}`,
39-
in: securityScheme?.in || 'header',
39+
in: 'header',
4040
});
41-
} else if (securityScheme?.scheme === 'basic') {
41+
} else if (securityScheme?.type === 'http' && securityScheme?.scheme === 'basic') {
4242
parameters.push({
4343
name: 'Authorization',
4444
value: `Basic {$inputs.${securityName}}`,
45-
in: securityScheme?.in || 'header',
45+
in: 'header',
4646
});
4747
}
4848
}

0 commit comments

Comments
 (0)