@scalar/mock-server 0.2.64 → 0.2.65
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 +11 -0
- package/README.md +1 -1
- package/dist/createMockServer.d.ts +3 -9
- package/dist/createMockServer.d.ts.map +1 -1
- package/dist/createMockServer.js +18 -114
- package/dist/routes/mockAnyResponse.d.ts +8 -0
- package/dist/routes/mockAnyResponse.d.ts.map +1 -0
- package/dist/routes/mockAnyResponse.js +72 -0
- package/dist/routes/respondWithAuthorizePage.d.ts +6 -0
- package/dist/routes/respondWithAuthorizePage.d.ts.map +1 -0
- package/dist/routes/respondWithAuthorizePage.js +105 -0
- package/dist/routes/respondWithOpenApiDocument.d.ts +14 -0
- package/dist/routes/respondWithOpenApiDocument.d.ts.map +1 -0
- package/dist/routes/respondWithOpenApiDocument.js +38 -0
- package/dist/routes/respondWithToken.d.ts +15 -0
- package/dist/routes/respondWithToken.d.ts.map +1 -0
- package/dist/routes/respondWithToken.js +47 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/createOpenAPIDocument.d.ts +4 -0
- package/dist/utils/createOpenAPIDocument.d.ts.map +1 -0
- package/dist/utils/getOpenAuthTokenUrls.d.ts +7 -0
- package/dist/utils/getOpenAuthTokenUrls.d.ts.map +1 -0
- package/dist/utils/getOpenAuthTokenUrls.js +55 -0
- package/dist/utils/handleAuthentication.d.ts +10 -0
- package/dist/utils/handleAuthentication.d.ts.map +1 -0
- package/dist/utils/handleAuthentication.js +103 -0
- package/dist/utils/index.d.ts +1 -7
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/logAuthenticationInstructions.d.ts +6 -0
- package/dist/utils/logAuthenticationInstructions.d.ts.map +1 -0
- package/dist/utils/logAuthenticationInstructions.js +112 -0
- package/dist/utils/setupAuthenticationRoutes.d.ts +7 -0
- package/dist/utils/setupAuthenticationRoutes.d.ts.map +1 -0
- package/dist/utils/setupAuthenticationRoutes.js +64 -0
- package/package.json +3 -3
- package/dist/utils/anyBasicAuthentication.d.ts +0 -6
- package/dist/utils/anyBasicAuthentication.d.ts.map +0 -1
- package/dist/utils/anyBasicAuthentication.js +0 -25
- package/dist/utils/anyOpenAuthPasswordGrantAuthentication.d.ts +0 -6
- package/dist/utils/anyOpenAuthPasswordGrantAuthentication.d.ts.map +0 -1
- package/dist/utils/anyOpenAuthPasswordGrantAuthentication.js +0 -22
- package/dist/utils/findPreferredResponseKey.test.d.ts +0 -2
- package/dist/utils/findPreferredResponseKey.test.d.ts.map +0 -1
- package/dist/utils/getOpenAuthTokenUrl.d.ts +0 -3
- package/dist/utils/getOpenAuthTokenUrl.d.ts.map +0 -1
- package/dist/utils/getOpenAuthTokenUrl.js +0 -21
- package/dist/utils/honoRouteFromPath.test.d.ts +0 -2
- package/dist/utils/honoRouteFromPath.test.d.ts.map +0 -1
- package/dist/utils/isBasicAuthenticationRequired.d.ts +0 -3
- package/dist/utils/isBasicAuthenticationRequired.d.ts.map +0 -1
- package/dist/utils/isBasicAuthenticationRequired.js +0 -13
- package/dist/utils/isOpenAuthPasswordGrantRequired.d.ts +0 -3
- package/dist/utils/isOpenAuthPasswordGrantRequired.d.ts.map +0 -1
- package/dist/utils/isOpenAuthPasswordGrantRequired.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @scalar/mock-server
|
|
2
2
|
|
|
3
|
+
## 0.2.65
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4cf8c52: feat: mock the OAuth code flow
|
|
8
|
+
- Updated dependencies [7323370]
|
|
9
|
+
- Updated dependencies [d7a6c55]
|
|
10
|
+
- Updated dependencies [69bda25]
|
|
11
|
+
- @scalar/openapi-parser@0.8.8
|
|
12
|
+
- @scalar/oas-utils@0.2.61
|
|
13
|
+
|
|
3
14
|
## 0.2.64
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ A powerful Node.js server that generates and returns realistic mock data based o
|
|
|
19
19
|
|
|
20
20
|
## Quickstart
|
|
21
21
|
|
|
22
|
-
It’s part of [our Scalar CLI](https://github.com/scalar/scalar/tree/main/packages/cli),
|
|
22
|
+
It’s part of [our Scalar CLI](https://github.com/scalar/scalar/tree/main/packages/cli), you can boot it literllay in seconds:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npx @scalar/cli mock openapi.json --watch
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { MockServerOptions } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Create a mock server instance
|
|
5
5
|
*/
|
|
6
|
-
export declare function createMockServer(options
|
|
7
|
-
specification: string | Record<string, any>;
|
|
8
|
-
onRequest?: (data: {
|
|
9
|
-
context: Context;
|
|
10
|
-
operation: OpenAPI.Operation;
|
|
11
|
-
}) => void;
|
|
12
|
-
}): Promise<Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">>;
|
|
6
|
+
export declare function createMockServer(options: MockServerOptions): Promise<Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">>;
|
|
13
7
|
//# sourceMappingURL=createMockServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createMockServer.d.ts","sourceRoot":"","sources":["../src/createMockServer.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"createMockServer.d.ts","sourceRoot":"","sources":["../src/createMockServer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAA;AAKzC,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAQ5D;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,uFAoDhE"}
|
package/dist/createMockServer.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { getExampleFromSchema } from '@scalar/oas-utils/spec-getters';
|
|
2
1
|
import { openapi } from '@scalar/openapi-parser';
|
|
3
2
|
import { Hono } from 'hono';
|
|
4
|
-
import { accepts } from 'hono/accepts';
|
|
5
3
|
import { cors } from 'hono/cors';
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { anyOpenAuthPasswordGrantAuthentication } from './utils/anyOpenAuthPasswordGrantAuthentication.js';
|
|
9
|
-
import { findPreferredResponseKey } from './utils/findPreferredResponseKey.js';
|
|
10
|
-
import { getOpenAuthTokenUrl } from './utils/getOpenAuthTokenUrl.js';
|
|
4
|
+
import { mockAnyResponse } from './routes/mockAnyResponse.js';
|
|
5
|
+
import { respondWithOpenApiDocument } from './routes/respondWithOpenApiDocument.js';
|
|
11
6
|
import { getOperations } from './utils/getOperations.js';
|
|
7
|
+
import { handleAuthentication } from './utils/handleAuthentication.js';
|
|
12
8
|
import { honoRouteFromPath } from './utils/honoRouteFromPath.js';
|
|
13
9
|
import { isAuthenticationRequired } from './utils/isAuthenticationRequired.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
10
|
+
import { logAuthenticationInstructions } from './utils/logAuthenticationInstructions.js';
|
|
11
|
+
import { setupAuthenticationRoutes } from './utils/setupAuthenticationRoutes.js';
|
|
16
12
|
|
|
17
13
|
/**
|
|
18
14
|
* Create a mock server instance
|
|
@@ -26,42 +22,10 @@ async function createMockServer(options) {
|
|
|
26
22
|
.get();
|
|
27
23
|
// CORS headers
|
|
28
24
|
app.use(cors());
|
|
29
|
-
|
|
30
|
-
app
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
const { specification } = await openapi().load(options.specification).get();
|
|
35
|
-
return c.json(specification);
|
|
36
|
-
});
|
|
37
|
-
// OpenAPI YAML file
|
|
38
|
-
app.get('/openapi.yaml', async (c) => {
|
|
39
|
-
if (!options?.specification) {
|
|
40
|
-
return c.text('Not found', 404);
|
|
41
|
-
}
|
|
42
|
-
const specification = await openapi().load(options.specification).toYaml();
|
|
43
|
-
return c.text(specification);
|
|
44
|
-
});
|
|
45
|
-
// OpenAuth2 token endpoint
|
|
46
|
-
const tokenUrl = getOpenAuthTokenUrl(schema);
|
|
47
|
-
if (typeof tokenUrl === 'string') {
|
|
48
|
-
app.post(tokenUrl, async (c) => {
|
|
49
|
-
return c.json({
|
|
50
|
-
access_token: 'super-secret-token',
|
|
51
|
-
token_type: 'Bearer',
|
|
52
|
-
expires_in: 3600,
|
|
53
|
-
refresh_token: 'secret-refresh-token',
|
|
54
|
-
}, 200,
|
|
55
|
-
/**
|
|
56
|
-
* When responding with an access token, the server must also include the additional Cache-Control: no-store
|
|
57
|
-
* HTTP header to ensure clients do not cache this request.
|
|
58
|
-
* @see https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
|
|
59
|
-
*/
|
|
60
|
-
{
|
|
61
|
-
'Cache-Control': 'no-store',
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
}
|
|
25
|
+
/** Authentication methods defined in the OpenAPI document */
|
|
26
|
+
setupAuthenticationRoutes(app, schema);
|
|
27
|
+
logAuthenticationInstructions(schema?.components?.securitySchemes ||
|
|
28
|
+
{});
|
|
65
29
|
/** Paths specified in the OpenAPI document */
|
|
66
30
|
const paths = schema?.paths ?? {};
|
|
67
31
|
Object.keys(paths).forEach((path) => {
|
|
@@ -70,78 +34,18 @@ async function createMockServer(options) {
|
|
|
70
34
|
methods.forEach((method) => {
|
|
71
35
|
const route = honoRouteFromPath(path);
|
|
72
36
|
const operation = schema?.paths?.[path]?.[method];
|
|
73
|
-
// Check if authentication is required
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const requiresBasicAuthentication = isBasicAuthenticationRequired(operation, schema);
|
|
77
|
-
// Add HTTP basic authentication
|
|
78
|
-
if (requiresAuthentication && requiresBasicAuthentication) {
|
|
79
|
-
app[method](route, anyBasicAuthentication());
|
|
80
|
-
}
|
|
81
|
-
// Check whether we need OpenAuth password grant authentication
|
|
82
|
-
const requiresOpenAuthPasswordGrant = isOpenAuthPasswordGrantRequired(operation, schema);
|
|
83
|
-
// Add HTTP basic authentication
|
|
84
|
-
if (requiresAuthentication && requiresOpenAuthPasswordGrant) {
|
|
85
|
-
app[method](route, anyOpenAuthPasswordGrantAuthentication());
|
|
37
|
+
// Check if authentication is required for this operation
|
|
38
|
+
if (isAuthenticationRequired(operation.security)) {
|
|
39
|
+
app[method](route, handleAuthentication(schema, operation));
|
|
86
40
|
}
|
|
87
|
-
//
|
|
88
|
-
app[method](route, (c) =>
|
|
89
|
-
// Call onRequest callback
|
|
90
|
-
if (options?.onRequest) {
|
|
91
|
-
options.onRequest({
|
|
92
|
-
context: c,
|
|
93
|
-
operation,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
// Response
|
|
97
|
-
// default, 200, 201 …
|
|
98
|
-
const preferredResponseKey = findPreferredResponseKey(Object.keys(operation.responses ?? {}));
|
|
99
|
-
const preferredResponse = preferredResponseKey
|
|
100
|
-
? operation.responses?.[preferredResponseKey]
|
|
101
|
-
: null;
|
|
102
|
-
const supportedContentTypes = Object.keys(preferredResponse?.content ?? {});
|
|
103
|
-
// Headers
|
|
104
|
-
const headers = preferredResponse?.headers ?? {};
|
|
105
|
-
Object.keys(headers).forEach((header) => {
|
|
106
|
-
c.header(header, headers[header].schema
|
|
107
|
-
? getExampleFromSchema(headers[header].schema)
|
|
108
|
-
: null);
|
|
109
|
-
});
|
|
110
|
-
// Content-Type
|
|
111
|
-
const acceptedContentType = accepts(c, {
|
|
112
|
-
header: 'Accept',
|
|
113
|
-
supports: supportedContentTypes,
|
|
114
|
-
default: supportedContentTypes.includes('application/json')
|
|
115
|
-
? 'application/json'
|
|
116
|
-
: supportedContentTypes[0],
|
|
117
|
-
});
|
|
118
|
-
c.header('Content-Type', acceptedContentType);
|
|
119
|
-
const acceptedResponse = preferredResponse?.content?.[acceptedContentType];
|
|
120
|
-
// Body
|
|
121
|
-
const body = acceptedResponse?.example
|
|
122
|
-
? acceptedResponse.example
|
|
123
|
-
: acceptedResponse?.schema
|
|
124
|
-
? getExampleFromSchema(acceptedResponse.schema, {
|
|
125
|
-
emptyString: '…',
|
|
126
|
-
variables: c.req.param(),
|
|
127
|
-
})
|
|
128
|
-
: null;
|
|
129
|
-
// Status code
|
|
130
|
-
const statusCode = parseInt(preferredResponseKey === 'default'
|
|
131
|
-
? '200'
|
|
132
|
-
: (preferredResponseKey ?? '200'), 10);
|
|
133
|
-
c.status(statusCode);
|
|
134
|
-
return c.body(typeof body === 'object'
|
|
135
|
-
? // XML
|
|
136
|
-
acceptedContentType?.includes('xml')
|
|
137
|
-
? `<?xml version="1.0" encoding="UTF-8"?>${objectToXML(body)}`
|
|
138
|
-
: // JSON
|
|
139
|
-
JSON.stringify(body, null, 2)
|
|
140
|
-
: // String
|
|
141
|
-
body);
|
|
142
|
-
});
|
|
41
|
+
// Actual route
|
|
42
|
+
app[method](route, (c) => mockAnyResponse(c, operation, options));
|
|
143
43
|
});
|
|
144
44
|
});
|
|
45
|
+
// OpenAPI JSON file
|
|
46
|
+
app.get('/openapi.json', (c) => respondWithOpenApiDocument(c, options?.specification, 'json'));
|
|
47
|
+
// OpenAPI YAML file
|
|
48
|
+
app.get('/openapi.yaml', (c) => respondWithOpenApiDocument(c, options?.specification, 'yaml'));
|
|
145
49
|
return app;
|
|
146
50
|
}
|
|
147
51
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OpenAPI } from '@scalar/openapi-types';
|
|
2
|
+
import type { Context } from 'hono';
|
|
3
|
+
import type { MockServerOptions } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Mock any response
|
|
6
|
+
*/
|
|
7
|
+
export declare function mockAnyResponse(c: Context, operation: OpenAPI.Operation, options: MockServerOptions): Response;
|
|
8
|
+
//# sourceMappingURL=mockAnyResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockAnyResponse.d.ts","sourceRoot":"","sources":["../../src/routes/mockAnyResponse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAMnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAGjD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,OAAO,EAAE,iBAAiB,YAiF3B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { getExampleFromSchema } from '@scalar/oas-utils/spec-getters';
|
|
2
|
+
import { accepts } from 'hono/accepts';
|
|
3
|
+
import objectToXML from 'object-to-xml';
|
|
4
|
+
import { findPreferredResponseKey } from '../utils/findPreferredResponseKey.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mock any response
|
|
8
|
+
*/
|
|
9
|
+
function mockAnyResponse(c, operation, options) {
|
|
10
|
+
// Call onRequest callback
|
|
11
|
+
if (options?.onRequest) {
|
|
12
|
+
options.onRequest({
|
|
13
|
+
context: c,
|
|
14
|
+
operation,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
// Response
|
|
18
|
+
// default, 200, 201 …
|
|
19
|
+
const preferredResponseKey = findPreferredResponseKey(Object.keys(operation.responses ?? {}));
|
|
20
|
+
const preferredResponse = preferredResponseKey
|
|
21
|
+
? operation.responses?.[preferredResponseKey]
|
|
22
|
+
: null;
|
|
23
|
+
if (!preferredResponse) {
|
|
24
|
+
c.status(500);
|
|
25
|
+
return c.json({ error: 'No response defined for this operation.' });
|
|
26
|
+
}
|
|
27
|
+
const supportedContentTypes = Object.keys(preferredResponse?.content ?? {});
|
|
28
|
+
// Headers
|
|
29
|
+
const headers = preferredResponse?.headers ?? {};
|
|
30
|
+
Object.keys(headers).forEach((header) => {
|
|
31
|
+
const value = headers[header].schema
|
|
32
|
+
? getExampleFromSchema(headers[header].schema)
|
|
33
|
+
: null;
|
|
34
|
+
if (value !== null) {
|
|
35
|
+
c.header(header, value);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// Content-Type
|
|
39
|
+
const acceptedContentType = accepts(c, {
|
|
40
|
+
header: 'Accept',
|
|
41
|
+
supports: supportedContentTypes,
|
|
42
|
+
default: supportedContentTypes.includes('application/json')
|
|
43
|
+
? 'application/json'
|
|
44
|
+
: supportedContentTypes[0],
|
|
45
|
+
});
|
|
46
|
+
c.header('Content-Type', acceptedContentType);
|
|
47
|
+
const acceptedResponse = preferredResponse?.content?.[acceptedContentType];
|
|
48
|
+
// Body
|
|
49
|
+
const body = acceptedResponse?.example
|
|
50
|
+
? acceptedResponse.example
|
|
51
|
+
: acceptedResponse?.schema
|
|
52
|
+
? getExampleFromSchema(acceptedResponse.schema, {
|
|
53
|
+
emptyString: '…',
|
|
54
|
+
variables: c.req.param(),
|
|
55
|
+
})
|
|
56
|
+
: null;
|
|
57
|
+
// Status code
|
|
58
|
+
const statusCode = parseInt(preferredResponseKey === 'default'
|
|
59
|
+
? '200'
|
|
60
|
+
: (preferredResponseKey ?? '200'), 10);
|
|
61
|
+
c.status(statusCode);
|
|
62
|
+
return c.body(typeof body === 'object'
|
|
63
|
+
? // XML
|
|
64
|
+
acceptedContentType?.includes('xml')
|
|
65
|
+
? `<?xml version="1.0" encoding="UTF-8"?>${objectToXML(body)}`
|
|
66
|
+
: // JSON
|
|
67
|
+
JSON.stringify(body, null, 2)
|
|
68
|
+
: // String
|
|
69
|
+
body);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { mockAnyResponse };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
/**
|
|
3
|
+
* Responds with an HTML page that simulates an OAuth 2.0 authorization page.
|
|
4
|
+
*/
|
|
5
|
+
export declare function respondWithAuthorizePage(c: Context, title?: string): Response;
|
|
6
|
+
//# sourceMappingURL=respondWithAuthorizePage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"respondWithAuthorizePage.d.ts","sourceRoot":"","sources":["../../src/routes/respondWithAuthorizePage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAKnC;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,GAAE,MAAW,YAoCtE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Always responds with this code */
|
|
2
|
+
const EXAMPLE_AUTHORIZATION_CODE = 'super-secret-token';
|
|
3
|
+
/**
|
|
4
|
+
* Responds with an HTML page that simulates an OAuth 2.0 authorization page.
|
|
5
|
+
*/
|
|
6
|
+
function respondWithAuthorizePage(c, title = '') {
|
|
7
|
+
const redirectUri = c.req.query('redirect_uri');
|
|
8
|
+
const state = c.req.query('state');
|
|
9
|
+
if (!redirectUri) {
|
|
10
|
+
return c.html(generateErrorHtml('Missing redirect_uri parameter', 'This parameter is required for the OAuth 2.0 authorization flow to function correctly. Please provide a valid redirect URI in your request.'), 400);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
// Validate redirect URI against allowed domains
|
|
14
|
+
const redirectUrl = new URL(redirectUri);
|
|
15
|
+
redirectUrl.searchParams.set('code', EXAMPLE_AUTHORIZATION_CODE);
|
|
16
|
+
if (state) {
|
|
17
|
+
redirectUrl.searchParams.set('state', state);
|
|
18
|
+
}
|
|
19
|
+
const htmlContent = generateAuthorizationHtml(redirectUrl.toString(), title);
|
|
20
|
+
return c.html(htmlContent);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return c.html(generateErrorHtml('Invalid redirect_uri format', 'Please provide a valid URL. The redirect_uri parameter must be a properly formatted URL that includes the protocol (e.g., https://) and a valid domain. This is essential for the OAuth 2.0 flow to securely redirect after authorization.'), 400);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function generateAuthorizationHtml(redirectUrl, title = '') {
|
|
27
|
+
return `
|
|
28
|
+
<!DOCTYPE html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="UTF-8">
|
|
32
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
33
|
+
<title>OAuth 2.0 Authorization</title>
|
|
34
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
35
|
+
</head>
|
|
36
|
+
<body class="flex justify-center items-center h-screen bg-gray-100">
|
|
37
|
+
|
|
38
|
+
<div class="flex flex-col">
|
|
39
|
+
<div class="mb-5 flex justify-center items-center gap-2">
|
|
40
|
+
<img src="https://scalar.com/logo-dark.svg" class="w-6 inline-block" />
|
|
41
|
+
<div class="font-medium truncate max-w-[26ch] text-lg">
|
|
42
|
+
${title}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="bg-gray-50 rounded-lg p-1 rounded-lg w-[28rem] shadow">
|
|
46
|
+
<div class="">
|
|
47
|
+
<h1 class="text font-medium text-gray-800 px-6 pt-2 pb-3 flex gap-3 rounded-t-lg">
|
|
48
|
+
OAuth 2.0 Authorization
|
|
49
|
+
</h1>
|
|
50
|
+
<div class="bg-white rounded">
|
|
51
|
+
<div class="text-gray-600 text-base px-6 py-5 flex flex-col gap-3">
|
|
52
|
+
<p>
|
|
53
|
+
This application is requesting access to your account. By granting authorization, you allow the application to perform certain actions on your behalf.
|
|
54
|
+
</p>
|
|
55
|
+
<p>
|
|
56
|
+
If you’re comfortable with the access being requested, click the button below to grant authorization:
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="px-6 py-4 pt-0 flex justify-between">
|
|
60
|
+
<a href="javascript:history.back()" class="inline-block px-6 py-2 text-gray-600 rounded border" aria-label="Cancel authorization">
|
|
61
|
+
Cancel
|
|
62
|
+
</a>
|
|
63
|
+
<a href="${redirectUrl}" class="inline-block px-6 py-2 bg-black text-white rounded transition-colors duration-300 hover:bg-gray-800" aria-label="Authorize application">
|
|
64
|
+
Authorize
|
|
65
|
+
</a>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<p class="text-xs text-gray-400 mt-5 text-center">
|
|
72
|
+
This authorization page is provided by @scalar/mock-server
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
</div>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
function generateErrorHtml(title, message) {
|
|
81
|
+
return `<html>
|
|
82
|
+
<html lang="en">
|
|
83
|
+
<head>
|
|
84
|
+
<meta charset="UTF-8">
|
|
85
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
86
|
+
<title>OAuth 2.0 Authorization</title>
|
|
87
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<div class="p-4 m-8 flex flex-col gap-4 text-lg">
|
|
91
|
+
<h1 class="font-bold">
|
|
92
|
+
Error: ${title}
|
|
93
|
+
</h1>
|
|
94
|
+
<p>
|
|
95
|
+
${message}
|
|
96
|
+
</p>
|
|
97
|
+
<p>
|
|
98
|
+
Example: <code class="bg-gray-100 py-1 px-2 rounded text-base"><a href="?redirect_uri=https://example.com/callback">?redirect_uri=https://example.com/callback</a></code>
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</body>
|
|
102
|
+
</html>`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { respondWithAuthorizePage };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
/**
|
|
3
|
+
* OpenAPI endpoints
|
|
4
|
+
*/
|
|
5
|
+
export declare function respondWithOpenApiDocument(c: Context, input?: string | Record<string, any>, format?: 'json' | 'yaml'): Promise<(Response & import("hono").TypedResponse<{
|
|
6
|
+
[x: string]: any;
|
|
7
|
+
}, import("hono/utils/http-status").StatusCode, "json">) | (Response & import("hono").TypedResponse<string, import("hono/utils/http-status").StatusCode, "text">) | (Response & import("hono").TypedResponse<{
|
|
8
|
+
error: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}, 500, "json">) | (Response & import("hono").TypedResponse<{
|
|
11
|
+
error: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}, 400, "json">)>;
|
|
14
|
+
//# sourceMappingURL=respondWithOpenApiDocument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"respondWithOpenApiDocument.d.ts","sourceRoot":"","sources":["../../src/routes/respondWithOpenApiDocument.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,CAAC,EAAE,OAAO,EACV,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACpC,MAAM,GAAE,MAAM,GAAG,MAAe;;;;;;;;kBAwCjC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { openapi } from '@scalar/openapi-parser';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OpenAPI endpoints
|
|
5
|
+
*/
|
|
6
|
+
async function respondWithOpenApiDocument(c, input, format = 'json') {
|
|
7
|
+
if (!input) {
|
|
8
|
+
return c.text('Not found', 404);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const { specification } = await openapi().load(input).get();
|
|
12
|
+
// JSON
|
|
13
|
+
if (format === 'json') {
|
|
14
|
+
c.header('Content-Type', 'application/json');
|
|
15
|
+
return c.json(specification);
|
|
16
|
+
}
|
|
17
|
+
// YAML
|
|
18
|
+
try {
|
|
19
|
+
const yamlSpecification = await openapi().load(input).toYaml();
|
|
20
|
+
c.header('Content-Type', 'text/yaml');
|
|
21
|
+
return c.text(yamlSpecification);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return c.json({
|
|
25
|
+
error: 'Failed to convert specification to YAML',
|
|
26
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
27
|
+
}, 500);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return c.json({
|
|
32
|
+
error: 'Failed to parse OpenAPI specification',
|
|
33
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
34
|
+
}, 400);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { respondWithOpenApiDocument };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
/**
|
|
3
|
+
* Responds with a JSON object simulating an OAuth 2.0 token response.
|
|
4
|
+
*/
|
|
5
|
+
export declare function respondWithToken(c: Context): (Response & import("hono").TypedResponse<{
|
|
6
|
+
error: string;
|
|
7
|
+
error_description: string;
|
|
8
|
+
}, 400, "json">) | (Response & import("hono").TypedResponse<{
|
|
9
|
+
access_token: string;
|
|
10
|
+
token_type: string;
|
|
11
|
+
expires_in: number;
|
|
12
|
+
refresh_token: string;
|
|
13
|
+
scope: string;
|
|
14
|
+
}, import("hono/utils/http-status").StatusCode, "json">);
|
|
15
|
+
//# sourceMappingURL=respondWithToken.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"respondWithToken.d.ts","sourceRoot":"","sources":["../../src/routes/respondWithToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAKnC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO;;;;;;;;;yDAuD1C"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Always responds with this token */
|
|
2
|
+
const EXAMPLE_ACCESS_TOKEN = 'super-secret-access-token';
|
|
3
|
+
/**
|
|
4
|
+
* Responds with a JSON object simulating an OAuth 2.0 token response.
|
|
5
|
+
*/
|
|
6
|
+
function respondWithToken(c) {
|
|
7
|
+
const grantType = c.req.query('grant_type');
|
|
8
|
+
if (!grantType) {
|
|
9
|
+
return c.json({
|
|
10
|
+
error: 'invalid_request',
|
|
11
|
+
error_description: 'Missing grant_type parameter',
|
|
12
|
+
}, 400);
|
|
13
|
+
}
|
|
14
|
+
// Validate supported grant types
|
|
15
|
+
const supportedGrantTypes = [
|
|
16
|
+
'authorization_code',
|
|
17
|
+
'client_credentials',
|
|
18
|
+
'refresh_token',
|
|
19
|
+
];
|
|
20
|
+
if (!supportedGrantTypes.includes(grantType)) {
|
|
21
|
+
return c.json({
|
|
22
|
+
error: 'unsupported_grant_type',
|
|
23
|
+
error_description: `Grant type must be one of: ${supportedGrantTypes.join(', ')}`,
|
|
24
|
+
}, 400);
|
|
25
|
+
}
|
|
26
|
+
// Validate required parameters for each grant type
|
|
27
|
+
if (grantType === 'authorization_code' && !c.req.query('code')) {
|
|
28
|
+
return c.json({
|
|
29
|
+
error: 'invalid_request',
|
|
30
|
+
error_description: 'Missing code parameter',
|
|
31
|
+
}, 400);
|
|
32
|
+
}
|
|
33
|
+
// Simulate token generation
|
|
34
|
+
const tokenResponse = {
|
|
35
|
+
access_token: EXAMPLE_ACCESS_TOKEN,
|
|
36
|
+
token_type: 'Bearer',
|
|
37
|
+
expires_in: 3600,
|
|
38
|
+
refresh_token: 'example-refresh-token',
|
|
39
|
+
scope: c.req.query('scope') ?? 'read write',
|
|
40
|
+
};
|
|
41
|
+
// Security headers
|
|
42
|
+
c.header('Cache-Control', 'no-store');
|
|
43
|
+
c.header('Pragma', 'no-cache');
|
|
44
|
+
return c.json(tokenResponse);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { respondWithToken };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
+
import type { OpenAPI } from '@scalar/openapi-types';
|
|
2
|
+
import { type Context } from 'hono';
|
|
1
3
|
/** Available HTTP methods for Hono routes */
|
|
2
4
|
export declare const httpMethods: readonly ["get", "put", "post", "delete", "options", "patch"];
|
|
3
5
|
/** Valid HTTP method */
|
|
4
6
|
export type HttpMethod = (typeof httpMethods)[number];
|
|
7
|
+
export type MockServerOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* The OpenAPI specification to use for mocking.
|
|
10
|
+
* Can be a string (URL or file path) or an object.
|
|
11
|
+
*/
|
|
12
|
+
specification: string | Record<string, any>;
|
|
13
|
+
/**
|
|
14
|
+
* Callback function to be called before each request is processed.
|
|
15
|
+
*/
|
|
16
|
+
onRequest?: (data: {
|
|
17
|
+
context: Context;
|
|
18
|
+
operation: OpenAPI.Operation;
|
|
19
|
+
}) => void;
|
|
20
|
+
};
|
|
5
21
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,eAAO,MAAM,WAAW,+DAOd,CAAA;AAEV,wBAAwB;AACxB,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,KAAK,OAAO,EAAQ,MAAM,MAAM,CAAA;AAEzC,6CAA6C;AAC7C,eAAO,MAAM,WAAW,+DAOd,CAAA;AAEV,wBAAwB;AACxB,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAErD,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE3C;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAA;KAAE,KAAK,IAAI,CAAA;CAC/E,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { OpenAPIV3, OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
/** Helper function create an OpenAPI document with security schemss */
|
|
3
|
+
export declare function createOpenAPIDocument(securitySchemes: Record<string, OpenAPIV3.SecuritySchemeObject | OpenAPIV3_1.SecuritySchemeObject>): OpenAPIV3.Document;
|
|
4
|
+
//# sourceMappingURL=createOpenAPIDocument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createOpenAPIDocument.d.ts","sourceRoot":"","sources":["../../src/utils/createOpenAPIDocument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnE,uEAAuE;AACvE,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,CACrB,MAAM,EACN,SAAS,CAAC,oBAAoB,GAAG,WAAW,CAAC,oBAAoB,CAClE,GACA,SAAS,CAAC,QAAQ,CAMpB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OpenAPI } from '@scalar/openapi-types';
|
|
2
|
+
/**
|
|
3
|
+
* Extract path from URL
|
|
4
|
+
*/
|
|
5
|
+
export declare function getPathFromUrl(url: string): string;
|
|
6
|
+
export declare function getOpenAuthTokenUrls(schema?: OpenAPI.Document): string[];
|
|
7
|
+
//# sourceMappingURL=getOpenAuthTokenUrls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getOpenAuthTokenUrls.d.ts","sourceRoot":"","sources":["../../src/utils/getOpenAuthTokenUrls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAclD;AAiBD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,EAAE,CAgCxE"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract path from URL
|
|
3
|
+
*/
|
|
4
|
+
function getPathFromUrl(url) {
|
|
5
|
+
try {
|
|
6
|
+
// Handle relative URLs by prepending a base
|
|
7
|
+
const urlObject = url.startsWith('http')
|
|
8
|
+
? new URL(url)
|
|
9
|
+
: new URL(url, 'http://example.com');
|
|
10
|
+
// Normalize: remove trailing slash except for root path
|
|
11
|
+
const path = urlObject.pathname;
|
|
12
|
+
return path === '/' ? path : path.replace(/\/$/, '');
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
// If URL is invalid, return the original string
|
|
16
|
+
return url;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns all token URLs mentioned in the securitySchemes, without the domain
|
|
21
|
+
*/
|
|
22
|
+
// Type guard for OAuth2 security scheme
|
|
23
|
+
function isOAuth2Scheme(scheme) {
|
|
24
|
+
return scheme.type === 'oauth2';
|
|
25
|
+
}
|
|
26
|
+
// Validate token URL
|
|
27
|
+
function isValidTokenUrl(url) {
|
|
28
|
+
return url.trim().length > 0;
|
|
29
|
+
}
|
|
30
|
+
function getOpenAuthTokenUrls(schema) {
|
|
31
|
+
if (!schema?.components?.securitySchemes) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const securitySchemes = schema.components.securitySchemes;
|
|
35
|
+
// Use Set from the start for better memory efficiency
|
|
36
|
+
const tokenUrls = new Set();
|
|
37
|
+
// Iterate through all security schemes
|
|
38
|
+
for (const scheme of Object.values(securitySchemes)) {
|
|
39
|
+
if (!isOAuth2Scheme(scheme))
|
|
40
|
+
continue;
|
|
41
|
+
const flows = scheme.flows; // Type assertion no longer needed
|
|
42
|
+
// Helper to safely add valid token URLs
|
|
43
|
+
const addTokenUrl = (url) => {
|
|
44
|
+
if (url && isValidTokenUrl(url)) {
|
|
45
|
+
tokenUrls.add(getPathFromUrl(url));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
addTokenUrl(flows?.password?.tokenUrl);
|
|
49
|
+
addTokenUrl(flows?.clientCredentials?.tokenUrl);
|
|
50
|
+
addTokenUrl(flows?.authorizationCode?.tokenUrl);
|
|
51
|
+
}
|
|
52
|
+
return Array.from(tokenUrls);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { getOpenAuthTokenUrls, getPathFromUrl };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenAPI } from '@scalar/openapi-types';
|
|
2
|
+
import type { Context } from 'hono';
|
|
3
|
+
/**
|
|
4
|
+
* Handles authentication for incoming requests based on the OpenAPI specification.
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleAuthentication(schema?: OpenAPI.Document, operation?: OpenAPI.Operation): (c: Context, next: () => Promise<void>) => Promise<(Response & import("hono").TypedResponse<{
|
|
7
|
+
error: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}, 401, "json">) | undefined>;
|
|
10
|
+
//# sourceMappingURL=handleAuthentication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleAuthentication.d.ts","sourceRoot":"","sources":["../../src/utils/handleAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,EACzB,SAAS,CAAC,EAAE,OAAO,CAAC,SAAS,OAEZ,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC;;;8BA8GpD"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { getCookie } from 'hono/cookie';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles authentication for incoming requests based on the OpenAPI specification.
|
|
5
|
+
*/
|
|
6
|
+
function handleAuthentication(schema, operation) {
|
|
7
|
+
return async (c, next) => {
|
|
8
|
+
const operationSecuritySchemes = operation?.security || schema?.security;
|
|
9
|
+
if (operationSecuritySchemes && operationSecuritySchemes.length > 0) {
|
|
10
|
+
let isAuthenticated = false;
|
|
11
|
+
let authScheme = '';
|
|
12
|
+
for (const securityRequirement of operationSecuritySchemes) {
|
|
13
|
+
let securitySchemeAuthenticated = true;
|
|
14
|
+
for (const [schemeName] of Object.entries(securityRequirement)) {
|
|
15
|
+
const scheme = schema?.components?.securitySchemes?.[schemeName];
|
|
16
|
+
if (scheme) {
|
|
17
|
+
switch (scheme.type) {
|
|
18
|
+
case 'http':
|
|
19
|
+
if (scheme.scheme === 'basic') {
|
|
20
|
+
authScheme = 'Basic';
|
|
21
|
+
const authHeader = c.req.header('Authorization');
|
|
22
|
+
if (authHeader?.startsWith('Basic ')) {
|
|
23
|
+
isAuthenticated = true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (scheme.scheme === 'bearer') {
|
|
27
|
+
authScheme = 'Bearer';
|
|
28
|
+
const authHeader = c.req.header('Authorization');
|
|
29
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
30
|
+
isAuthenticated = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case 'apiKey':
|
|
35
|
+
authScheme = `ApiKey ${scheme.name}`;
|
|
36
|
+
if (scheme.in === 'header') {
|
|
37
|
+
const apiKey = c.req.header(scheme.name);
|
|
38
|
+
if (apiKey) {
|
|
39
|
+
isAuthenticated = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (scheme.in === 'query') {
|
|
43
|
+
const apiKey = c.req.query(scheme.name);
|
|
44
|
+
if (apiKey) {
|
|
45
|
+
isAuthenticated = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (scheme.in === 'cookie') {
|
|
49
|
+
const apiKey = getCookie(c, scheme.name);
|
|
50
|
+
if (apiKey) {
|
|
51
|
+
isAuthenticated = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case 'oauth2':
|
|
56
|
+
authScheme = 'Bearer';
|
|
57
|
+
// Handle OAuth 2.0 flows, including password grant
|
|
58
|
+
if (c.req.header('Authorization')?.startsWith('Bearer ')) {
|
|
59
|
+
isAuthenticated = true;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!isAuthenticated) {
|
|
65
|
+
securitySchemeAuthenticated = false;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (securitySchemeAuthenticated) {
|
|
70
|
+
isAuthenticated = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!isAuthenticated) {
|
|
75
|
+
let wwwAuthenticateValue = authScheme;
|
|
76
|
+
switch (authScheme) {
|
|
77
|
+
case 'Basic':
|
|
78
|
+
wwwAuthenticateValue +=
|
|
79
|
+
' realm="Scalar Mock Server", charset="UTF-8"';
|
|
80
|
+
break;
|
|
81
|
+
case 'Bearer':
|
|
82
|
+
wwwAuthenticateValue +=
|
|
83
|
+
' realm="Scalar Mock Server", error="invalid_token", error_description="The access token is invalid or has expired"';
|
|
84
|
+
break;
|
|
85
|
+
case 'ApiKey':
|
|
86
|
+
wwwAuthenticateValue += ` realm="Scalar Mock Server", error="invalid_token", error_description="Invalid or missing API key"`;
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
wwwAuthenticateValue = 'Bearer realm="Scalar Mock Server"';
|
|
90
|
+
}
|
|
91
|
+
c.header('WWW-Authenticate', wwwAuthenticateValue);
|
|
92
|
+
return c.json({
|
|
93
|
+
error: 'Unauthorized',
|
|
94
|
+
message: 'Authentication is required to access this resource.',
|
|
95
|
+
}, 401);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// If all checks pass, continue to the next middleware
|
|
99
|
+
await next();
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { handleAuthentication };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
export * from './anyBasicAuthentication.js';
|
|
2
|
-
export * from './anyOpenAuthPasswordGrantAuthentication.js';
|
|
3
|
-
export * from './findPreferredResponseKey.test';
|
|
4
1
|
export * from './findPreferredResponseKey.js';
|
|
5
|
-
export * from './
|
|
2
|
+
export * from './getOpenAuthTokenUrls.js';
|
|
6
3
|
export * from './getOperations.js';
|
|
7
|
-
export * from './honoRouteFromPath.test';
|
|
8
4
|
export * from './honoRouteFromPath.js';
|
|
9
5
|
export * from './isAuthenticationRequired.js';
|
|
10
|
-
export * from './isBasicAuthenticationRequired.js';
|
|
11
|
-
export * from './isOpenAuthPasswordGrantRequired.js';
|
|
12
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
/**
|
|
3
|
+
* Log authentication instructions for different security schemes
|
|
4
|
+
*/
|
|
5
|
+
export declare function logAuthenticationInstructions(securitySchemes: Record<string, OpenAPIV3_1.SecuritySchemeObject>): void;
|
|
6
|
+
//# sourceMappingURL=logAuthenticationInstructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logAuthenticationInstructions.d.ts","sourceRoot":"","sources":["../../src/utils/logAuthenticationInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,QAoIlE"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { getPathFromUrl } from './getOpenAuthTokenUrls.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Log authentication instructions for different security schemes
|
|
5
|
+
*/
|
|
6
|
+
function logAuthenticationInstructions(securitySchemes) {
|
|
7
|
+
if (!securitySchemes || Object.keys(securitySchemes).length === 0) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
console.log('Authentication:');
|
|
11
|
+
console.log();
|
|
12
|
+
Object.entries(securitySchemes).forEach(([_, scheme]) => {
|
|
13
|
+
switch (scheme.type) {
|
|
14
|
+
case 'apiKey':
|
|
15
|
+
if (scheme.in === 'header') {
|
|
16
|
+
console.log('✅ API Key Authentication');
|
|
17
|
+
console.log(` Use any API key in the ${scheme.name} header`);
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(` ${scheme.name}: YOUR_API_KEY_HERE`);
|
|
20
|
+
console.log();
|
|
21
|
+
}
|
|
22
|
+
else if (scheme.in === 'query') {
|
|
23
|
+
console.log(`✅ API Key Authentication`);
|
|
24
|
+
console.log(` Use any API key in the ${scheme.name} query parameter:`);
|
|
25
|
+
console.log();
|
|
26
|
+
console.log(` ?${scheme.name}=YOUR_API_KEY_HERE`);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
else if (scheme.in === 'cookie') {
|
|
30
|
+
console.log(`✅ API Key Authentication`);
|
|
31
|
+
console.log(` Use any API key in the ${scheme.name} cookie:`);
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(` Cookie: ${scheme.name}=YOUR_API_KEY_HERE`);
|
|
34
|
+
console.log();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.error(`❌ Unsupported API Key Location: ${scheme.in}`);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case 'http':
|
|
41
|
+
if (scheme.scheme === 'basic') {
|
|
42
|
+
console.log('✅ HTTP Basic Authentication');
|
|
43
|
+
console.log(' Use an Authorization header with any credentials ("username:password" in base64):');
|
|
44
|
+
console.log();
|
|
45
|
+
console.log(' Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=');
|
|
46
|
+
console.log();
|
|
47
|
+
}
|
|
48
|
+
else if (scheme.scheme === 'bearer') {
|
|
49
|
+
console.log('✅ Bearer Token Authentication');
|
|
50
|
+
console.log(' Use an Authorization header with any bearer token');
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(' Authorization: Bearer YOUR_TOKEN_HERE');
|
|
53
|
+
console.log();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.error('❌ Unknown Security Scheme:', scheme);
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
case 'oauth2':
|
|
60
|
+
if (scheme.flows) {
|
|
61
|
+
Object.keys(scheme.flows).forEach((flow) => {
|
|
62
|
+
switch (flow) {
|
|
63
|
+
case 'implicit':
|
|
64
|
+
console.log('✅ OAuth 2.0 Implicit Flow');
|
|
65
|
+
console.log(' Use the following URL to initiate the OAuth 2.0 Implicit Flow:');
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(` GET ${scheme?.flows?.implicit?.authorizationUrl || '/oauth/authorize'}?response_type=token&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=YOUR_SCOPES`);
|
|
68
|
+
console.log();
|
|
69
|
+
break;
|
|
70
|
+
case 'password':
|
|
71
|
+
console.log('✅ OAuth 2.0 Password Flow');
|
|
72
|
+
console.log(' Use the following URL to obtain an access token:');
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(` POST ${getPathFromUrl(scheme?.flows?.password?.tokenUrl || '/oauth/token')}`);
|
|
75
|
+
console.log(' Content-Type: application/x-www-form-urlencoded');
|
|
76
|
+
console.log();
|
|
77
|
+
console.log(' grant_type=password&username=YOUR_USERNAME&password=YOUR_PASSWORD&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET');
|
|
78
|
+
console.log();
|
|
79
|
+
break;
|
|
80
|
+
case 'clientCredentials':
|
|
81
|
+
console.log('✅ OAuth 2.0 Client Credentials Flow');
|
|
82
|
+
console.log(' Use the following URL to obtain an access token:');
|
|
83
|
+
console.log();
|
|
84
|
+
console.log(` POST ${getPathFromUrl(scheme?.flows?.clientCredentials?.tokenUrl || '/oauth/token')}`);
|
|
85
|
+
console.log(' Content-Type: application/x-www-form-urlencoded');
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(' grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET');
|
|
88
|
+
console.log();
|
|
89
|
+
break;
|
|
90
|
+
case 'authorizationCode':
|
|
91
|
+
console.log('✅ OAuth 2.0 Authorization Code Flow');
|
|
92
|
+
console.log(' Use the following URL to initiate the OAuth 2.0 Authorization Code Flow:');
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(' GET', `${getPathFromUrl(scheme?.flows?.authorizationCode?.authorizationUrl || '/oauth/authorize')}?redirect_uri=https://YOUR_REDIRECT_URI_HERE`);
|
|
95
|
+
console.log();
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
console.warn(`Unsupported OAuth 2.0 flow: ${flow}`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case 'openIdConnect':
|
|
104
|
+
console.log('⚠️ OpenID Connect Authentication');
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
console.warn(`Unsupported security scheme type: ${scheme.type}`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { logAuthenticationInstructions };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OpenAPI } from '@scalar/openapi-types';
|
|
2
|
+
import type { Hono } from 'hono';
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to set up authentication routes for OAuth 2.0 flows
|
|
5
|
+
*/
|
|
6
|
+
export declare function setupAuthenticationRoutes(app: Hono, schema?: OpenAPI.Document): void;
|
|
7
|
+
//# sourceMappingURL=setupAuthenticationRoutes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupAuthenticationRoutes.d.ts","sourceRoot":"","sources":["../../src/utils/setupAuthenticationRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAMhC;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,IAAI,EACT,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,QA6E1B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { respondWithAuthorizePage } from '../routes/respondWithAuthorizePage.js';
|
|
2
|
+
import { respondWithToken } from '../routes/respondWithToken.js';
|
|
3
|
+
import { getOpenAuthTokenUrls, getPathFromUrl } from './getOpenAuthTokenUrls.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Helper function to set up authentication routes for OAuth 2.0 flows
|
|
7
|
+
*/
|
|
8
|
+
function setupAuthenticationRoutes(app, schema) {
|
|
9
|
+
const securitySchemes = schema?.components?.securitySchemes || {};
|
|
10
|
+
// Set up authentication routes for OAuth 2.0 flows
|
|
11
|
+
getOpenAuthTokenUrls(schema).forEach((tokenUrl) => {
|
|
12
|
+
app.post(tokenUrl, (c) => {
|
|
13
|
+
return c.json({
|
|
14
|
+
access_token: 'super-secret-access-token',
|
|
15
|
+
token_type: 'Bearer',
|
|
16
|
+
expires_in: 3600,
|
|
17
|
+
refresh_token: 'example-refresh-token',
|
|
18
|
+
}, 200, {
|
|
19
|
+
/**
|
|
20
|
+
* When responding with an access token, the server must also include the additional
|
|
21
|
+
* Cache-Control: no-store HTTP header to ensure clients do not cache this request.
|
|
22
|
+
*
|
|
23
|
+
* @see https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
|
|
24
|
+
*/
|
|
25
|
+
'Cache-Control': 'no-store',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
// Set up routes for different OAuth 2.0 flows
|
|
30
|
+
const authorizeUrls = new Set();
|
|
31
|
+
const tokenUrls = new Set();
|
|
32
|
+
Object.entries(securitySchemes).forEach(([_, scheme]) => {
|
|
33
|
+
if (scheme.type === 'oauth2') {
|
|
34
|
+
if (scheme.flows?.authorizationCode) {
|
|
35
|
+
const authorizeRoute = scheme.flows.authorizationCode.authorizationUrl ?? '/oauth/authorize';
|
|
36
|
+
const tokenRoute = scheme.flows.authorizationCode.tokenUrl ?? '/oauth/token';
|
|
37
|
+
authorizeUrls.add(getPathFromUrl(authorizeRoute));
|
|
38
|
+
tokenUrls.add(tokenRoute);
|
|
39
|
+
}
|
|
40
|
+
if (scheme.flows?.implicit) {
|
|
41
|
+
const authorizeRoute = scheme.flows.implicit.authorizationUrl ?? '/oauth/authorize';
|
|
42
|
+
authorizeUrls.add(getPathFromUrl(authorizeRoute));
|
|
43
|
+
}
|
|
44
|
+
if (scheme.flows?.password) {
|
|
45
|
+
const tokenRoute = scheme.flows.password.tokenUrl ?? '/oauth/token';
|
|
46
|
+
tokenUrls.add(tokenRoute);
|
|
47
|
+
}
|
|
48
|
+
if (scheme.flows?.clientCredentials) {
|
|
49
|
+
const tokenRoute = scheme.flows.clientCredentials.tokenUrl ?? '/oauth/token';
|
|
50
|
+
tokenUrls.add(tokenRoute);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Set up unique authorization routes
|
|
55
|
+
authorizeUrls.forEach((authorizeUrl) => {
|
|
56
|
+
app.get(authorizeUrl, (c) => respondWithAuthorizePage(c, schema?.info?.title));
|
|
57
|
+
});
|
|
58
|
+
// Set up unique token routes
|
|
59
|
+
tokenUrls.forEach((tokenUrl) => {
|
|
60
|
+
app.post(tokenUrl, respondWithToken);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { setupAuthenticationRoutes };
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"swagger",
|
|
17
17
|
"cli"
|
|
18
18
|
],
|
|
19
|
-
"version": "0.2.
|
|
19
|
+
"version": "0.2.65",
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"hono": "^4.6.5",
|
|
40
40
|
"object-to-xml": "^2.0.0",
|
|
41
|
-
"@scalar/oas-utils": "0.2.
|
|
42
|
-
"@scalar/openapi-parser": "0.8.
|
|
41
|
+
"@scalar/oas-utils": "0.2.61",
|
|
42
|
+
"@scalar/openapi-parser": "0.8.8",
|
|
43
43
|
"@scalar/openapi-types": "0.1.4"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Context } from 'hono';
|
|
2
|
-
/**
|
|
3
|
-
* Middleware to check for any basic authentication header
|
|
4
|
-
*/
|
|
5
|
-
export declare function anyBasicAuthentication(): (ctx: Context, next: () => Promise<void>) => Promise<void>;
|
|
6
|
-
//# sourceMappingURL=anyBasicAuthentication.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"anyBasicAuthentication.d.ts","sourceRoot":"","sources":["../../src/utils/anyBasicAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;AACH,wBAAgB,sBAAsB,UACR,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,mBAiB/D"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { HTTPException } from 'hono/http-exception';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Middleware to check for any basic authentication header
|
|
5
|
-
*/
|
|
6
|
-
function anyBasicAuthentication() {
|
|
7
|
-
return async function (ctx, next) {
|
|
8
|
-
// Check if the request has an Authorization header
|
|
9
|
-
// Note: We don’t care *what* credentials are sent, though.
|
|
10
|
-
if (ctx.req.header('Authorization')?.startsWith('Basic ')) {
|
|
11
|
-
return await next();
|
|
12
|
-
}
|
|
13
|
-
// Unauthorized
|
|
14
|
-
throw new HTTPException(401, {
|
|
15
|
-
res: new Response('Unauthorized', {
|
|
16
|
-
status: 401,
|
|
17
|
-
headers: {
|
|
18
|
-
'WWW-Authenticate': 'Basic realm="Authentication Required"',
|
|
19
|
-
},
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export { anyBasicAuthentication };
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Context } from 'hono';
|
|
2
|
-
/**
|
|
3
|
-
* Middleware to check for any bearer authentication header
|
|
4
|
-
*/
|
|
5
|
-
export declare function anyOpenAuthPasswordGrantAuthentication(): (ctx: Context, next: () => Promise<void>) => Promise<void>;
|
|
6
|
-
//# sourceMappingURL=anyOpenAuthPasswordGrantAuthentication.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"anyOpenAuthPasswordGrantAuthentication.d.ts","sourceRoot":"","sources":["../../src/utils/anyOpenAuthPasswordGrantAuthentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;AACH,wBAAgB,sCAAsC,UACxB,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,mBAc/D"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { HTTPException } from 'hono/http-exception';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Middleware to check for any bearer authentication header
|
|
5
|
-
*/
|
|
6
|
-
function anyOpenAuthPasswordGrantAuthentication() {
|
|
7
|
-
return async function (ctx, next) {
|
|
8
|
-
// Check if the request has an Authorization header
|
|
9
|
-
// Note: We don’t care *what* credentials are sent, though.
|
|
10
|
-
if (ctx.req.header('Authorization')?.startsWith('Bearer ')) {
|
|
11
|
-
return await next();
|
|
12
|
-
}
|
|
13
|
-
// Unauthorized
|
|
14
|
-
throw new HTTPException(401, {
|
|
15
|
-
res: new Response('Unauthorized', {
|
|
16
|
-
status: 401,
|
|
17
|
-
}),
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { anyOpenAuthPasswordGrantAuthentication };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"findPreferredResponseKey.test.d.ts","sourceRoot":"","sources":["../../src/utils/findPreferredResponseKey.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getOpenAuthTokenUrl.d.ts","sourceRoot":"","sources":["../../src/utils/getOpenAuthTokenUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,8BAgC5D"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
function getOpenAuthTokenUrl(schema) {
|
|
2
|
-
const securitySchemes = schema?.components?.securitySchemes;
|
|
3
|
-
if (securitySchemes === undefined) {
|
|
4
|
-
return false;
|
|
5
|
-
}
|
|
6
|
-
// TODO: Make this work with other OpenAuth workflows
|
|
7
|
-
const openAuthPasswordGrant = Object.values(securitySchemes).filter((securityScheme) => {
|
|
8
|
-
if (securityScheme.type === 'oauth2' &&
|
|
9
|
-
securityScheme.flows?.password !== undefined) {
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
return false;
|
|
13
|
-
});
|
|
14
|
-
if (!openAuthPasswordGrant.length) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
// @ts-expect-error TypeScript, I know it’s there (or undefined, both is fine).
|
|
18
|
-
return openAuthPasswordGrant[0]?.flows?.password?.tokenUrl;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { getOpenAuthTokenUrl };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"honoRouteFromPath.test.d.ts","sourceRoot":"","sources":["../../src/utils/honoRouteFromPath.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isBasicAuthenticationRequired.d.ts","sourceRoot":"","sources":["../../src/utils/isBasicAuthenticationRequired.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,WAwB1B"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
function isBasicAuthenticationRequired(operation, schema) {
|
|
2
|
-
const allowedSecuritySchemes = operation.security?.map((securityScheme) => {
|
|
3
|
-
return Object.keys(securityScheme)[0];
|
|
4
|
-
});
|
|
5
|
-
// Check if one of them is HTTP Basic Auth
|
|
6
|
-
const httpBasicAuthIsRequired = allowedSecuritySchemes?.findIndex((securitySchemeKey) => {
|
|
7
|
-
const securityScheme = schema?.components?.securitySchemes?.[securitySchemeKey];
|
|
8
|
-
return (securityScheme?.type === 'http' && securityScheme?.scheme === 'basic');
|
|
9
|
-
}) >= 0;
|
|
10
|
-
return httpBasicAuthIsRequired;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export { isBasicAuthenticationRequired };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isOpenAuthPasswordGrantRequired.d.ts","sourceRoot":"","sources":["../../src/utils/isOpenAuthPasswordGrantRequired.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,WAyB1B"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
function isOpenAuthPasswordGrantRequired(operation, schema) {
|
|
2
|
-
const allowedSecuritySchemes = operation.security?.map((securityScheme) => {
|
|
3
|
-
return Object.keys(securityScheme)[0];
|
|
4
|
-
});
|
|
5
|
-
// Check if one of them is OpenAuth2 Password Grant
|
|
6
|
-
const passwordGrantRequired = allowedSecuritySchemes?.findIndex((securitySchemeKey) => {
|
|
7
|
-
const securityScheme = schema?.components?.securitySchemes?.[securitySchemeKey];
|
|
8
|
-
return (securityScheme?.type === 'oauth2' &&
|
|
9
|
-
securityScheme?.flows?.password !== undefined);
|
|
10
|
-
}) >= 0;
|
|
11
|
-
return passwordGrantRequired;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export { isOpenAuthPasswordGrantRequired };
|