@apiquest/plugin-auth 1.0.1
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.
Potentially problematic release.
This version of @apiquest/plugin-auth might be problematic. Click here for more details.
- package/LICENSE.txt +661 -0
- package/dist/apikey-auth.d.ts +3 -0
- package/dist/apikey-auth.d.ts.map +1 -0
- package/dist/basic-auth.d.ts +3 -0
- package/dist/basic-auth.d.ts.map +1 -0
- package/dist/bearer-auth.d.ts +3 -0
- package/dist/bearer-auth.d.ts.map +1 -0
- package/dist/helpers.d.ts +3 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12676 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth2-auth.d.ts +35 -0
- package/dist/oauth2-auth.d.ts.map +1 -0
- package/dist/src/apikey-auth.d.ts +3 -0
- package/dist/src/apikey-auth.d.ts.map +1 -0
- package/dist/src/basic-auth.d.ts +3 -0
- package/dist/src/basic-auth.d.ts.map +1 -0
- package/dist/src/bearer-auth.d.ts +3 -0
- package/dist/src/bearer-auth.d.ts.map +1 -0
- package/dist/src/helpers.d.ts +3 -0
- package/dist/src/helpers.d.ts.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/oauth2-auth.d.ts +35 -0
- package/dist/src/oauth2-auth.d.ts.map +1 -0
- package/esbuild.config.js +16 -0
- package/package.json +61 -0
- package/rollup.config.js +31 -0
- package/src/apikey-auth.ts +79 -0
- package/src/basic-auth.ts +65 -0
- package/src/bearer-auth.ts +54 -0
- package/src/helpers.ts +8 -0
- package/src/index.ts +32 -0
- package/src/oauth2-auth.ts +339 -0
- package/tsconfig.json +19 -0
- package/tsconfig.test.json +5 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IAuthPlugin } from '@apiquest/types';
|
|
2
|
+
/**
|
|
3
|
+
* OAuth2 configuration interface
|
|
4
|
+
*
|
|
5
|
+
* Client credential placement:
|
|
6
|
+
* - clientId/clientSecret: Always required, contain the actual credential values
|
|
7
|
+
* - clientAuthentication: Where/how to send credentials to token endpoint
|
|
8
|
+
* - 'body': Send as client_id/client_secret in form body (default, standard OAuth2)
|
|
9
|
+
* - 'basic': Send as HTTP Basic Auth header (Base64 encoded clientId:clientSecret)
|
|
10
|
+
* - 'header': Send as custom headers (names specified by clientIdField/clientSecretField)
|
|
11
|
+
* - 'query': Send as query parameters (names specified by clientIdField/clientSecretField)
|
|
12
|
+
* - clientIdField/clientSecretField: Custom field names for 'header' or 'query' placement
|
|
13
|
+
* - extraHeaders/extraBody/extraQuery: Additional parameters for token request
|
|
14
|
+
* - cacheToken: Whether to cache token across requests (default: true)
|
|
15
|
+
*/
|
|
16
|
+
export interface OAuth2Config {
|
|
17
|
+
grantType: 'client_credentials' | 'password' | 'authorization_code';
|
|
18
|
+
accessTokenUrl: string;
|
|
19
|
+
clientId: string;
|
|
20
|
+
clientSecret: string;
|
|
21
|
+
username?: string;
|
|
22
|
+
password?: string;
|
|
23
|
+
scope?: string;
|
|
24
|
+
authorizationCode?: string;
|
|
25
|
+
redirectUri?: string;
|
|
26
|
+
clientAuthentication?: 'body' | 'basic' | 'header' | 'query';
|
|
27
|
+
clientIdField?: string;
|
|
28
|
+
clientSecretField?: string;
|
|
29
|
+
extraHeaders?: Record<string, string>;
|
|
30
|
+
extraBody?: Record<string, unknown>;
|
|
31
|
+
extraQuery?: Record<string, string>;
|
|
32
|
+
cacheToken?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const oauth2Auth: IAuthPlugin;
|
|
35
|
+
//# sourceMappingURL=oauth2-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2-auth.d.ts","sourceRoot":"","sources":["../src/oauth2-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAA4D,MAAM,iBAAiB,CAAC;AAG7G;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,oBAAoB,GAAG,UAAU,GAAG,oBAAoB,CAAC;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,oBAAoB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkKD,eAAO,MAAM,UAAU,EAAE,WAyIxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apikey-auth.d.ts","sourceRoot":"","sources":["../../src/apikey-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA4D,MAAM,iBAAiB,CAAC;AAE7G,eAAO,MAAM,UAAU,EAAE,WA6ExB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basic-auth.d.ts","sourceRoot":"","sources":["../../src/basic-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA4D,MAAM,iBAAiB,CAAC;AAE7G,eAAO,MAAM,SAAS,EAAE,WAsEvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer-auth.d.ts","sourceRoot":"","sources":["../../src/bearer-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA4D,MAAM,iBAAiB,CAAC;AAE7G,eAAO,MAAM,UAAU,EAAE,WAyDxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AACA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE5E"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthPlugin } from '@apiquest/types';
|
|
2
|
+
export { bearerAuth } from './bearer-auth.js';
|
|
3
|
+
export { basicAuth } from './basic-auth.js';
|
|
4
|
+
export { apiKeyAuth } from './apikey-auth.js';
|
|
5
|
+
export { oauth2Auth, type OAuth2Config } from './oauth2-auth.js';
|
|
6
|
+
export { isNullOrEmpty, isNullOrWhitespace } from './helpers.js';
|
|
7
|
+
export declare const authPlugins: IAuthPlugin[];
|
|
8
|
+
export declare function getAuthPlugin(type: string): IAuthPlugin | undefined;
|
|
9
|
+
export default authPlugins;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AASjE,eAAO,MAAM,WAAW,EAAE,WAAW,EAKpC,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEnE;AAGD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IAuthPlugin } from '@apiquest/types';
|
|
2
|
+
/**
|
|
3
|
+
* OAuth2 configuration interface
|
|
4
|
+
*
|
|
5
|
+
* Client credential placement:
|
|
6
|
+
* - clientId/clientSecret: Always required, contain the actual credential values
|
|
7
|
+
* - clientAuthentication: Where/how to send credentials to token endpoint
|
|
8
|
+
* - 'body': Send as client_id/client_secret in form body (default, standard OAuth2)
|
|
9
|
+
* - 'basic': Send as HTTP Basic Auth header (Base64 encoded clientId:clientSecret)
|
|
10
|
+
* - 'header': Send as custom headers (names specified by clientIdField/clientSecretField)
|
|
11
|
+
* - 'query': Send as query parameters (names specified by clientIdField/clientSecretField)
|
|
12
|
+
* - clientIdField/clientSecretField: Custom field names for 'header' or 'query' placement
|
|
13
|
+
* - extraHeaders/extraBody/extraQuery: Additional parameters for token request
|
|
14
|
+
* - cacheToken: Whether to cache token across requests (default: true)
|
|
15
|
+
*/
|
|
16
|
+
export interface OAuth2Config {
|
|
17
|
+
grantType: 'client_credentials' | 'password' | 'authorization_code';
|
|
18
|
+
accessTokenUrl: string;
|
|
19
|
+
clientId: string;
|
|
20
|
+
clientSecret: string;
|
|
21
|
+
username?: string;
|
|
22
|
+
password?: string;
|
|
23
|
+
scope?: string;
|
|
24
|
+
authorizationCode?: string;
|
|
25
|
+
redirectUri?: string;
|
|
26
|
+
clientAuthentication?: 'body' | 'basic' | 'header' | 'query';
|
|
27
|
+
clientIdField?: string;
|
|
28
|
+
clientSecretField?: string;
|
|
29
|
+
extraHeaders?: Record<string, string>;
|
|
30
|
+
extraBody?: Record<string, unknown>;
|
|
31
|
+
extraQuery?: Record<string, string>;
|
|
32
|
+
cacheToken?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const oauth2Auth: IAuthPlugin;
|
|
35
|
+
//# sourceMappingURL=oauth2-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2-auth.d.ts","sourceRoot":"","sources":["../../src/oauth2-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAA4D,MAAM,iBAAiB,CAAC;AAG7G;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,oBAAoB,GAAG,UAAU,GAAG,oBAAoB,CAAC;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,oBAAoB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA0KD,eAAO,MAAM,UAAU,EAAE,WA2IxB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import esbuild from 'esbuild';
|
|
2
|
+
|
|
3
|
+
await esbuild.build({
|
|
4
|
+
entryPoints: ['src/index.ts'],
|
|
5
|
+
bundle: true,
|
|
6
|
+
outfile: 'dist/index.js',
|
|
7
|
+
format: 'esm',
|
|
8
|
+
platform: 'node',
|
|
9
|
+
target: 'node18',
|
|
10
|
+
// Externalize peerDependencies - runner will provide these
|
|
11
|
+
external: ['@apiquest/fracture'],
|
|
12
|
+
minify: false,
|
|
13
|
+
sourcemap: true,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
console.log('✓ Built plugin-auth');
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apiquest/plugin-auth",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Authentication plugins for ApiQuest (Bearer, Basic, OAuth2, API Key)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "rollup -c && tsc --emitDeclarationOnly",
|
|
10
|
+
"dev": "rollup -c --watch",
|
|
11
|
+
"test": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"apiquest",
|
|
15
|
+
"auth",
|
|
16
|
+
"authentication",
|
|
17
|
+
"bearer",
|
|
18
|
+
"oauth2"
|
|
19
|
+
],
|
|
20
|
+
"author": "ApiQuest Contributors",
|
|
21
|
+
"license": "AGPL-3.0-or-later",
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@apiquest/types": "^1.0.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"got": "^14.6.6"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@apiquest/types": "workspace:*",
|
|
30
|
+
"@rollup/plugin-commonjs": "^25.0.0",
|
|
31
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
32
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
33
|
+
"rollup": "^4.0.0",
|
|
34
|
+
"tslib": "^2.7.0",
|
|
35
|
+
"typescript": "^5.0.0",
|
|
36
|
+
"vitest": "^4.0.18"
|
|
37
|
+
},
|
|
38
|
+
"apiquest": {
|
|
39
|
+
"type": "auth",
|
|
40
|
+
"runtime": [
|
|
41
|
+
"fracture"
|
|
42
|
+
],
|
|
43
|
+
"capabilities": {
|
|
44
|
+
"provides": {
|
|
45
|
+
"authTypes": [
|
|
46
|
+
"bearer",
|
|
47
|
+
"basic",
|
|
48
|
+
"apikey",
|
|
49
|
+
"oauth2"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"supports": {
|
|
53
|
+
"protocols": [
|
|
54
|
+
"http",
|
|
55
|
+
"graphql",
|
|
56
|
+
"grpc"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import typescript from '@rollup/plugin-typescript';
|
|
2
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
3
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
input: 'src/index.ts',
|
|
7
|
+
output: {
|
|
8
|
+
file: 'dist/index.js',
|
|
9
|
+
format: 'esm',
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
},
|
|
12
|
+
external: [
|
|
13
|
+
// Externalize peer dependencies
|
|
14
|
+
'@apiquest/fracture',
|
|
15
|
+
],
|
|
16
|
+
plugins: [
|
|
17
|
+
// Resolve node modules
|
|
18
|
+
resolve({
|
|
19
|
+
preferBuiltins: true, // Prefer Node.js built-in modules
|
|
20
|
+
exportConditions: ['node', 'import', 'default'],
|
|
21
|
+
}),
|
|
22
|
+
// Convert CommonJS to ESM (for axios and other CJS dependencies)
|
|
23
|
+
commonjs(),
|
|
24
|
+
// Compile TypeScript
|
|
25
|
+
typescript({
|
|
26
|
+
tsconfig: './tsconfig.json',
|
|
27
|
+
sourceMap: true,
|
|
28
|
+
declaration: false, // We'll use tsc for declarations
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// API Key Authentication
|
|
2
|
+
import type { IAuthPlugin, Request, Auth, RuntimeOptions, ValidationResult, ILogger } from '@apiquest/types';
|
|
3
|
+
|
|
4
|
+
export const apiKeyAuth: IAuthPlugin = {
|
|
5
|
+
name: 'API Key',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
description: 'API Key authentication (via header or query parameter)',
|
|
8
|
+
authTypes: ['apikey'],
|
|
9
|
+
protocols: ['http', 'graphql', 'grpc'],
|
|
10
|
+
dataSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
required: ['key', 'value'],
|
|
13
|
+
properties: {
|
|
14
|
+
key: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'API key name'
|
|
17
|
+
},
|
|
18
|
+
value: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'API key value'
|
|
21
|
+
},
|
|
22
|
+
in: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
enum: ['header', 'query'],
|
|
25
|
+
default: 'header',
|
|
26
|
+
description: 'Where to add the API key'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
validate(auth: Auth, options: RuntimeOptions): ValidationResult {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if ((auth.data?.key ?? null) === null) {
|
|
34
|
+
errors.push({
|
|
35
|
+
message: 'API key name is required',
|
|
36
|
+
location: '',
|
|
37
|
+
source: 'auth' as const
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if ((auth.data?.value ?? null) === null) {
|
|
41
|
+
errors.push({
|
|
42
|
+
message: 'API key value is required',
|
|
43
|
+
location: '',
|
|
44
|
+
source: 'auth' as const
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return errors.length > 0 ? { valid: false, errors } : { valid: true };
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
async apply(request: Request, auth: Auth, options: RuntimeOptions, logger?: ILogger): Promise<Request> {
|
|
51
|
+
const key = auth.data?.key as string | undefined;
|
|
52
|
+
const value = auth.data?.value as string | undefined;
|
|
53
|
+
const rawLocation = auth.data?.in as string | undefined;
|
|
54
|
+
const location = rawLocation ?? 'header';
|
|
55
|
+
|
|
56
|
+
logger?.debug('API key auth apply started', { location });
|
|
57
|
+
|
|
58
|
+
if (key === undefined || value === undefined) {
|
|
59
|
+
logger?.error('API key auth missing key or value');
|
|
60
|
+
throw new Error('API key and value are required');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (location === 'header') {
|
|
64
|
+
request.data.headers ??= {};
|
|
65
|
+
(request.data.headers as Record<string, string>)[key] = value;
|
|
66
|
+
logger?.trace('API key applied to headers', { header: key });
|
|
67
|
+
} else if (location === 'query') {
|
|
68
|
+
const url = new URL(request.data.url as string);
|
|
69
|
+
url.searchParams.set(key, value);
|
|
70
|
+
request.data.url = url.toString();
|
|
71
|
+
logger?.trace('API key applied to query string', { key });
|
|
72
|
+
} else {
|
|
73
|
+
logger?.error('Invalid API key location', { location });
|
|
74
|
+
throw new Error(`Invalid API key location: ${location}. Must be 'header' or 'query'`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return request;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Basic Authentication
|
|
2
|
+
import type { IAuthPlugin, Request, Auth, RuntimeOptions, ValidationResult, ILogger } from '@apiquest/types';
|
|
3
|
+
|
|
4
|
+
export const basicAuth: IAuthPlugin = {
|
|
5
|
+
name: 'Basic Authentication',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
description: 'HTTP Basic authentication (Authorization: Basic base64(username:password))',
|
|
8
|
+
authTypes: ['basic'],
|
|
9
|
+
protocols: ['http', 'graphql', 'grpc'],
|
|
10
|
+
dataSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
required: ['username', 'password'],
|
|
13
|
+
properties: {
|
|
14
|
+
username: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Username'
|
|
17
|
+
},
|
|
18
|
+
password: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Password'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
validate(auth: Auth, options: RuntimeOptions): ValidationResult {
|
|
26
|
+
const errors = [];
|
|
27
|
+
if ((auth.data?.username ?? null) === null) {
|
|
28
|
+
errors.push({
|
|
29
|
+
message: 'Username is required for basic auth',
|
|
30
|
+
location: '',
|
|
31
|
+
source: 'auth' as const
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if ((auth.data?.password ?? null) === null) {
|
|
35
|
+
errors.push({
|
|
36
|
+
message: 'Password is required for basic auth',
|
|
37
|
+
location: '',
|
|
38
|
+
source: 'auth' as const
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return errors.length > 0 ? { valid: false, errors } : { valid: true };
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
async apply(request: Request, auth: Auth, options: RuntimeOptions, logger?: ILogger): Promise<Request> {
|
|
45
|
+
if ((request.data.headers as Record<string, unknown> | null | undefined)?.['Authorization'] !== undefined) {
|
|
46
|
+
logger?.trace('Authorization header already present, skipping basic auth apply');
|
|
47
|
+
return request;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const username = auth.data?.username as string | undefined;
|
|
51
|
+
const password = auth.data?.password as string | undefined;
|
|
52
|
+
|
|
53
|
+
if (username === undefined || password === undefined) {
|
|
54
|
+
logger?.error('Basic auth missing username or password');
|
|
55
|
+
throw new Error('Username and password are required for basic auth');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
59
|
+
request.data.headers ??= {};
|
|
60
|
+
(request.data.headers as Record<string, string>)['Authorization'] = `Basic ${credentials}`;
|
|
61
|
+
logger?.debug('Basic auth header applied');
|
|
62
|
+
|
|
63
|
+
return request;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Bearer Token Authentication
|
|
2
|
+
import type { IAuthPlugin, Request, Auth, RuntimeOptions, ValidationResult, ILogger } from '@apiquest/types';
|
|
3
|
+
|
|
4
|
+
export const bearerAuth: IAuthPlugin = {
|
|
5
|
+
name: 'Bearer Token',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
description: 'Bearer token authentication (Authorization: Bearer <token>)',
|
|
8
|
+
authTypes: ['bearer'],
|
|
9
|
+
protocols: ['http', 'graphql', 'grpc'],
|
|
10
|
+
dataSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
required: ['token'],
|
|
13
|
+
properties: {
|
|
14
|
+
token: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Bearer token value'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
validate(auth: Auth, options: RuntimeOptions): ValidationResult {
|
|
22
|
+
if ((auth.data?.token ?? null) === null) {
|
|
23
|
+
return {
|
|
24
|
+
valid: false,
|
|
25
|
+
errors: [{
|
|
26
|
+
message: 'Bearer token is required',
|
|
27
|
+
location: '',
|
|
28
|
+
source: 'auth'
|
|
29
|
+
}]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { valid: true };
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async apply(request: Request, auth: Auth, options: RuntimeOptions, logger?: ILogger): Promise<Request> {
|
|
36
|
+
if ((request.data.headers as Record<string, unknown> | null | undefined)?.['Authorization'] !== undefined) {
|
|
37
|
+
logger?.trace('Authorization header already present, skipping bearer auth apply');
|
|
38
|
+
return request;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const token = auth.data?.token as string | undefined;
|
|
42
|
+
|
|
43
|
+
if (token === undefined) {
|
|
44
|
+
logger?.error('Bearer auth missing token');
|
|
45
|
+
throw new Error('Bearer token is required');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
request.data.headers ??= {};
|
|
49
|
+
(request.data.headers as Record<string, string>)['Authorization'] = `Bearer ${token}`;
|
|
50
|
+
logger?.debug('Bearer auth header applied');
|
|
51
|
+
|
|
52
|
+
return request;
|
|
53
|
+
}
|
|
54
|
+
};
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Helper functions for string validation
|
|
2
|
+
export function isNullOrEmpty(value: string | null | undefined): boolean {
|
|
3
|
+
return value === null || value === undefined || value === '';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function isNullOrWhitespace(value: string | null | undefined): boolean {
|
|
7
|
+
return value === null || value === undefined || value.trim() === '';
|
|
8
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Authentication Plugins for ApiQuest
|
|
2
|
+
import type { IAuthPlugin } from '@apiquest/types';
|
|
3
|
+
|
|
4
|
+
// Export individual auth plugins
|
|
5
|
+
export { bearerAuth } from './bearer-auth.js';
|
|
6
|
+
export { basicAuth } from './basic-auth.js';
|
|
7
|
+
export { apiKeyAuth } from './apikey-auth.js';
|
|
8
|
+
export { oauth2Auth, type OAuth2Config } from './oauth2-auth.js';
|
|
9
|
+
|
|
10
|
+
// Export helpers
|
|
11
|
+
export { isNullOrEmpty, isNullOrWhitespace } from './helpers.js';
|
|
12
|
+
|
|
13
|
+
// Import all plugins for registry
|
|
14
|
+
import { bearerAuth } from './bearer-auth.js';
|
|
15
|
+
import { basicAuth } from './basic-auth.js';
|
|
16
|
+
import { apiKeyAuth } from './apikey-auth.js';
|
|
17
|
+
import { oauth2Auth } from './oauth2-auth.js';
|
|
18
|
+
|
|
19
|
+
// Auth Plugin Registry
|
|
20
|
+
export const authPlugins: IAuthPlugin[] = [
|
|
21
|
+
bearerAuth,
|
|
22
|
+
basicAuth,
|
|
23
|
+
apiKeyAuth,
|
|
24
|
+
oauth2Auth
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export function getAuthPlugin(type: string): IAuthPlugin | undefined {
|
|
28
|
+
return authPlugins.find(p => p.authTypes.includes(type));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Export individual plugins and registry
|
|
32
|
+
export default authPlugins;
|