@frontmcp/utils 0.0.1 → 0.7.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.
- package/CHANGELOG.md +12 -0
- package/README.md +110 -0
- package/content/content.d.ts +43 -0
- package/content/index.d.ts +1 -0
- package/crypto/browser.d.ts +11 -0
- package/crypto/encrypted-blob.d.ts +157 -0
- package/crypto/index.d.ts +98 -0
- package/crypto/jwt-alg.d.ts +8 -0
- package/crypto/node.d.ts +59 -0
- package/crypto/pkce/index.d.ts +9 -0
- package/crypto/pkce/pkce.d.ts +140 -0
- package/crypto/runtime.d.ts +18 -0
- package/crypto/secret-persistence/index.d.ts +25 -0
- package/crypto/secret-persistence/persistence.d.ts +97 -0
- package/crypto/secret-persistence/schema.d.ts +34 -0
- package/crypto/secret-persistence/types.d.ts +65 -0
- package/crypto/types.d.ts +61 -0
- package/escape/escape.d.ts +101 -0
- package/escape/index.d.ts +1 -0
- package/esm/index.mjs +3264 -0
- package/esm/package.json +53 -0
- package/fs/fs.d.ts +254 -0
- package/fs/index.d.ts +1 -0
- package/http/http.d.ts +20 -0
- package/http/index.d.ts +1 -0
- package/index.d.ts +18 -0
- package/index.js +3425 -0
- package/naming/index.d.ts +1 -0
- package/naming/naming.d.ts +79 -0
- package/package.json +3 -2
- package/path/index.d.ts +1 -0
- package/path/path.d.ts +34 -0
- package/regex/index.d.ts +24 -0
- package/regex/patterns.d.ts +155 -0
- package/regex/safe-regex.d.ts +179 -0
- package/serialization/index.d.ts +1 -0
- package/serialization/serialization.d.ts +33 -0
- package/storage/adapters/base.d.ts +90 -0
- package/storage/adapters/index.d.ts +10 -0
- package/storage/adapters/memory.d.ts +99 -0
- package/storage/adapters/redis.d.ts +88 -0
- package/storage/adapters/upstash.d.ts +81 -0
- package/storage/adapters/vercel-kv.d.ts +69 -0
- package/storage/errors.d.ts +117 -0
- package/storage/factory.d.ts +70 -0
- package/storage/index.d.ts +13 -0
- package/storage/namespace.d.ts +88 -0
- package/storage/types.d.ts +428 -0
- package/storage/utils/index.d.ts +5 -0
- package/storage/utils/pattern.d.ts +71 -0
- package/storage/utils/ttl.d.ts +54 -0
- package/uri/index.d.ts +2 -0
- package/uri/uri-template.d.ts +92 -0
- package/uri/uri-validation.d.ts +46 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.0.1
|
|
4
|
+
|
|
5
|
+
- Initial release
|
|
6
|
+
- Extracted utilities from @frontmcp/sdk, @frontmcp/cli, and @frontmcp/adapters
|
|
7
|
+
- Added naming utilities: splitWords, toCase, sepFor, shortHash, ensureMaxLen, idFromString
|
|
8
|
+
- Added URI utilities: isValidMcpUri, extractUriScheme, isValidMcpUriTemplate, parseUriTemplate, matchUriTemplate, expandUriTemplate, extractTemplateParams, isUriTemplate
|
|
9
|
+
- Added path utilities: trimSlashes, joinPath
|
|
10
|
+
- Added content utilities: sanitizeToJson, inferMimeType
|
|
11
|
+
- Added HTTP utilities: validateBaseUrl
|
|
12
|
+
- Added FS utilities: fileExists, readJSON, writeJSON, ensureDir, isDirEmpty, runCmd
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @frontmcp/utils
|
|
2
|
+
|
|
3
|
+
Shared utility functions for the FrontMCP ecosystem. Provides generic, protocol-neutral utilities for string manipulation, URI handling, path operations, content processing, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @frontmcp/utils
|
|
9
|
+
# or
|
|
10
|
+
yarn add @frontmcp/utils
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
### Naming Utilities
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { splitWords, toCase, shortHash, ensureMaxLen, idFromString } from '@frontmcp/utils';
|
|
19
|
+
|
|
20
|
+
// Split strings into words (handles camelCase, PascalCase, delimiters)
|
|
21
|
+
splitWords('myFunctionName'); // ['my', 'Function', 'Name']
|
|
22
|
+
|
|
23
|
+
// Convert to different cases
|
|
24
|
+
toCase(['my', 'function'], 'snake'); // 'my_function'
|
|
25
|
+
toCase(['my', 'function'], 'kebab'); // 'my-function'
|
|
26
|
+
toCase(['my', 'function'], 'camel'); // 'myFunction'
|
|
27
|
+
|
|
28
|
+
// Generate short hashes
|
|
29
|
+
shortHash('some-string'); // '6-char hex hash'
|
|
30
|
+
|
|
31
|
+
// Truncate with hash for uniqueness
|
|
32
|
+
ensureMaxLen('very-long-name-that-exceeds-limit', 20);
|
|
33
|
+
|
|
34
|
+
// Sanitize to valid ID
|
|
35
|
+
idFromString('My Function Name!'); // 'My-Function-Name'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### URI Utilities
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import {
|
|
42
|
+
isValidMcpUri,
|
|
43
|
+
extractUriScheme,
|
|
44
|
+
parseUriTemplate,
|
|
45
|
+
matchUriTemplate,
|
|
46
|
+
expandUriTemplate,
|
|
47
|
+
} from '@frontmcp/utils';
|
|
48
|
+
|
|
49
|
+
// Validate RFC 3986 URIs
|
|
50
|
+
isValidMcpUri('https://example.com/resource'); // true
|
|
51
|
+
isValidMcpUri('/path/without/scheme'); // false
|
|
52
|
+
|
|
53
|
+
// Extract scheme
|
|
54
|
+
extractUriScheme('https://example.com'); // 'https'
|
|
55
|
+
|
|
56
|
+
// RFC 6570 URI Templates
|
|
57
|
+
const params = matchUriTemplate('users/{userId}/posts/{postId}', 'users/123/posts/456');
|
|
58
|
+
// { userId: '123', postId: '456' }
|
|
59
|
+
|
|
60
|
+
expandUriTemplate('users/{userId}', { userId: '123' }); // 'users/123'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Path Utilities
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { trimSlashes, joinPath } from '@frontmcp/utils';
|
|
67
|
+
|
|
68
|
+
trimSlashes('/path/to/resource/'); // 'path/to/resource'
|
|
69
|
+
joinPath('api', 'v1', 'users'); // '/api/v1/users'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Content Utilities
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { sanitizeToJson, inferMimeType } from '@frontmcp/utils';
|
|
76
|
+
|
|
77
|
+
// Sanitize values to JSON-safe objects
|
|
78
|
+
sanitizeToJson({ date: new Date(), fn: () => {} }); // { date: '2024-01-01T00:00:00.000Z' }
|
|
79
|
+
|
|
80
|
+
// Infer MIME type from extension
|
|
81
|
+
inferMimeType('document.json'); // 'application/json'
|
|
82
|
+
inferMimeType('image.png'); // 'image/png'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### HTTP Utilities
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { validateBaseUrl } from '@frontmcp/utils';
|
|
89
|
+
|
|
90
|
+
// Validate and normalize URLs
|
|
91
|
+
const url = validateBaseUrl('https://api.example.com');
|
|
92
|
+
// Throws for invalid URLs or unsupported protocols (file://, javascript:)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### File System Utilities
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { fileExists, readJSON, writeJSON, ensureDir, isDirEmpty } from '@frontmcp/utils';
|
|
99
|
+
|
|
100
|
+
// Async file operations
|
|
101
|
+
await fileExists('/path/to/file');
|
|
102
|
+
await readJSON<Config>('/path/to/config.json');
|
|
103
|
+
await writeJSON('/path/to/output.json', { key: 'value' });
|
|
104
|
+
await ensureDir('/path/to/directory');
|
|
105
|
+
await isDirEmpty('/path/to/directory');
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
Apache-2.0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content utilities for JSON sanitization and MIME type inference.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for safely converting JavaScript values to JSON-compatible
|
|
5
|
+
* formats and inferring content types from file extensions.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Sanitize arbitrary JS values into JSON-safe objects.
|
|
9
|
+
*
|
|
10
|
+
* Handles:
|
|
11
|
+
* - Functions and symbols → dropped (undefined)
|
|
12
|
+
* - BigInt → string
|
|
13
|
+
* - Date → ISO string
|
|
14
|
+
* - Error → { name, message, stack }
|
|
15
|
+
* - Map → plain object
|
|
16
|
+
* - Set → array
|
|
17
|
+
* - Circular references → dropped (undefined)
|
|
18
|
+
*
|
|
19
|
+
* @param value - Any JavaScript value
|
|
20
|
+
* @returns JSON-safe version of the value
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* sanitizeToJson({ date: new Date(), fn: () => {} })
|
|
24
|
+
* // { date: '2024-01-01T00:00:00.000Z' }
|
|
25
|
+
*
|
|
26
|
+
* sanitizeToJson(new Map([['key', 'value']]))
|
|
27
|
+
* // { key: 'value' }
|
|
28
|
+
*/
|
|
29
|
+
export declare function sanitizeToJson(value: unknown): unknown;
|
|
30
|
+
/**
|
|
31
|
+
* Infer MIME type from file extension or content.
|
|
32
|
+
*
|
|
33
|
+
* @param uri - The URI or filename to infer from
|
|
34
|
+
* @param content - Optional content to help infer type
|
|
35
|
+
* @returns Inferred MIME type, defaults to 'text/plain'
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* inferMimeType('document.json') // 'application/json'
|
|
39
|
+
* inferMimeType('image.png') // 'image/png'
|
|
40
|
+
* inferMimeType('unknown', '{"key": "value"}') // 'application/json'
|
|
41
|
+
* inferMimeType('unknown', '<html>') // 'text/html'
|
|
42
|
+
*/
|
|
43
|
+
export declare function inferMimeType(uri: string, content?: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { sanitizeToJson, inferMimeType } from './content';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Crypto Provider
|
|
3
|
+
*
|
|
4
|
+
* Implementation using @noble/hashes and @noble/ciphers for cross-platform support.
|
|
5
|
+
* These libraries work in both Node.js and browsers.
|
|
6
|
+
*/
|
|
7
|
+
import type { CryptoProvider } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* Browser-compatible crypto provider using @noble libraries.
|
|
10
|
+
*/
|
|
11
|
+
export declare const browserCrypto: CryptoProvider;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypted Blob Utilities
|
|
3
|
+
*
|
|
4
|
+
* Higher-level helpers for encrypting/decrypting JSON-serializable data
|
|
5
|
+
* using AES-256-GCM. All operations are synchronous.
|
|
6
|
+
*
|
|
7
|
+
* @module @frontmcp/utils/crypto
|
|
8
|
+
*/
|
|
9
|
+
import type { EncBlob } from './types';
|
|
10
|
+
export type EncryptedBlob = EncBlob;
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when encrypted blob operations fail.
|
|
13
|
+
*/
|
|
14
|
+
export declare class EncryptedBlobError extends Error {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Encrypt a value using AES-256-GCM.
|
|
19
|
+
*
|
|
20
|
+
* @param data - Value to encrypt (will be JSON serialized)
|
|
21
|
+
* @param key - 32-byte encryption key
|
|
22
|
+
* @returns Encrypted blob with base64url-encoded fields
|
|
23
|
+
* @throws EncryptedBlobError if key is invalid or data cannot be serialized
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const key = hkdfSha256(ikm, salt, info, 32);
|
|
28
|
+
* const blob = encryptValue({ secret: 'data' }, key);
|
|
29
|
+
* // blob.alg === 'A256GCM'
|
|
30
|
+
* // blob.iv, blob.tag, blob.data are base64url strings
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function encryptValue<T>(data: T, key: Uint8Array): EncryptedBlob;
|
|
34
|
+
/**
|
|
35
|
+
* Decrypt an encrypted blob and parse the JSON result.
|
|
36
|
+
*
|
|
37
|
+
* @param blob - Encrypted blob to decrypt
|
|
38
|
+
* @param key - 32-byte encryption key (must match encryption key)
|
|
39
|
+
* @returns Decrypted and parsed value
|
|
40
|
+
* @throws EncryptedBlobError if decryption or parsing fails
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const decrypted = decryptValue<MyType>(blob, key);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function decryptValue<T>(blob: EncryptedBlob, key: Uint8Array): T;
|
|
48
|
+
/**
|
|
49
|
+
* Try to decrypt an encrypted blob, returning null on failure.
|
|
50
|
+
*
|
|
51
|
+
* @param blob - Encrypted blob to decrypt
|
|
52
|
+
* @param key - 32-byte encryption key
|
|
53
|
+
* @returns Decrypted value or null if decryption/parsing fails
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = tryDecryptValue<MyType>(blob, key);
|
|
58
|
+
* if (result !== null) {
|
|
59
|
+
* // Use decrypted data
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function tryDecryptValue<T>(blob: EncryptedBlob, key: Uint8Array): T | null;
|
|
64
|
+
/**
|
|
65
|
+
* Serialize an encrypted blob to a JSON string for storage.
|
|
66
|
+
*
|
|
67
|
+
* @param blob - Encrypted blob to serialize
|
|
68
|
+
* @returns JSON string representation
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const blob = encryptValue(data, key);
|
|
73
|
+
* const str = serializeBlob(blob);
|
|
74
|
+
* // Store str in database/redis/etc.
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function serializeBlob(blob: EncryptedBlob): string;
|
|
78
|
+
/**
|
|
79
|
+
* Deserialize a JSON string back to an encrypted blob.
|
|
80
|
+
*
|
|
81
|
+
* @param str - JSON string to deserialize
|
|
82
|
+
* @returns Encrypted blob
|
|
83
|
+
* @throws EncryptedBlobError if string is not a valid encrypted blob
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const blob = deserializeBlob(storedString);
|
|
88
|
+
* const data = decryptValue(blob, key);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function deserializeBlob(str: string): EncryptedBlob;
|
|
92
|
+
/**
|
|
93
|
+
* Try to deserialize a JSON string to an encrypted blob, returning null on failure.
|
|
94
|
+
*
|
|
95
|
+
* @param str - JSON string to deserialize
|
|
96
|
+
* @returns Encrypted blob or null if invalid
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const blob = tryDeserializeBlob(storedString);
|
|
101
|
+
* if (blob !== null) {
|
|
102
|
+
* const data = tryDecryptValue(blob, key);
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function tryDeserializeBlob(str: string): EncryptedBlob | null;
|
|
107
|
+
/**
|
|
108
|
+
* Check if a value is a valid encrypted blob structure.
|
|
109
|
+
*
|
|
110
|
+
* @param value - Value to check
|
|
111
|
+
* @returns true if value is a valid EncryptedBlob
|
|
112
|
+
*/
|
|
113
|
+
export declare function isValidEncryptedBlob(value: unknown): value is EncryptedBlob;
|
|
114
|
+
/**
|
|
115
|
+
* Encrypt and serialize in one step.
|
|
116
|
+
*
|
|
117
|
+
* @param data - Value to encrypt
|
|
118
|
+
* @param key - 32-byte encryption key
|
|
119
|
+
* @returns JSON string of encrypted blob
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const encrypted = encryptAndSerialize({ secret: 'data' }, key);
|
|
124
|
+
* // Store encrypted string
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function encryptAndSerialize<T>(data: T, key: Uint8Array): string;
|
|
128
|
+
/**
|
|
129
|
+
* Deserialize and decrypt in one step.
|
|
130
|
+
*
|
|
131
|
+
* @param str - JSON string of encrypted blob
|
|
132
|
+
* @param key - 32-byte encryption key
|
|
133
|
+
* @returns Decrypted and parsed value
|
|
134
|
+
* @throws EncryptedBlobError if deserialization or decryption fails
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const data = deserializeAndDecrypt<MyType>(storedString, key);
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function deserializeAndDecrypt<T>(str: string, key: Uint8Array): T;
|
|
142
|
+
/**
|
|
143
|
+
* Try to deserialize and decrypt, returning null on any failure.
|
|
144
|
+
*
|
|
145
|
+
* @param str - JSON string of encrypted blob
|
|
146
|
+
* @param key - 32-byte encryption key
|
|
147
|
+
* @returns Decrypted value or null if any step fails
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const data = tryDeserializeAndDecrypt<MyType>(storedString, key);
|
|
152
|
+
* if (data !== null) {
|
|
153
|
+
* // Use decrypted data
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function tryDeserializeAndDecrypt<T>(str: string, key: Uint8Array): T | null;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Platform Crypto Module
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographic operations that work in both Node.js and browser environments.
|
|
5
|
+
* Uses native crypto in Node.js and @noble/hashes + @noble/ciphers in browsers.
|
|
6
|
+
*/
|
|
7
|
+
import type { CryptoProvider, EncBlob } from './types';
|
|
8
|
+
export { isRsaPssAlg, jwtAlgToNodeAlg } from './jwt-alg';
|
|
9
|
+
/**
|
|
10
|
+
* Get the crypto provider for the current runtime environment.
|
|
11
|
+
* Lazily initializes the provider.
|
|
12
|
+
*
|
|
13
|
+
* Note: this module intentionally avoids importing any Node-only crypto modules
|
|
14
|
+
* at module-load time so it can be used in browser builds without pulling them in.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCrypto(): CryptoProvider;
|
|
17
|
+
export declare function rsaVerify(jwtAlg: string, data: Buffer, publicJwk: JsonWebKey, signature: Buffer): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Generate a UUID v4 string.
|
|
20
|
+
*/
|
|
21
|
+
export declare function randomUUID(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Generate cryptographically secure random bytes.
|
|
24
|
+
* @param length - Number of bytes to generate (must be a positive integer)
|
|
25
|
+
* @throws Error if length is not a positive integer
|
|
26
|
+
*/
|
|
27
|
+
export declare function randomBytes(length: number): Uint8Array;
|
|
28
|
+
/**
|
|
29
|
+
* Compute SHA-256 hash.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sha256(data: string | Uint8Array): Uint8Array;
|
|
32
|
+
/**
|
|
33
|
+
* Compute SHA-256 hash and return as hex string.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sha256Hex(data: string | Uint8Array): string;
|
|
36
|
+
/**
|
|
37
|
+
* Compute HMAC-SHA256.
|
|
38
|
+
*/
|
|
39
|
+
export declare function hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
40
|
+
/**
|
|
41
|
+
* HKDF-SHA256 key derivation (RFC 5869).
|
|
42
|
+
* @param ikm - Input keying material
|
|
43
|
+
* @param salt - Salt value (can be empty)
|
|
44
|
+
* @param info - Context and application specific information
|
|
45
|
+
* @param length - Length of output keying material in bytes (1 to 8160)
|
|
46
|
+
* @throws Error if length is not a positive integer or exceeds HKDF limits
|
|
47
|
+
*/
|
|
48
|
+
export declare function hkdfSha256(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number): Uint8Array;
|
|
49
|
+
/**
|
|
50
|
+
* Encrypt using AES-256-GCM.
|
|
51
|
+
* @param key - 32-byte encryption key (AES-256)
|
|
52
|
+
* @param plaintext - Data to encrypt
|
|
53
|
+
* @param iv - 12-byte initialization vector (96 bits, recommended for GCM)
|
|
54
|
+
* @throws Error if key is not 32 bytes or IV is not 12 bytes
|
|
55
|
+
*/
|
|
56
|
+
export declare function encryptAesGcm(key: Uint8Array, plaintext: Uint8Array, iv: Uint8Array): {
|
|
57
|
+
ciphertext: Uint8Array;
|
|
58
|
+
tag: Uint8Array;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Decrypt using AES-256-GCM.
|
|
62
|
+
* @param key - 32-byte encryption key (AES-256)
|
|
63
|
+
* @param ciphertext - Encrypted data
|
|
64
|
+
* @param iv - 12-byte initialization vector (96 bits)
|
|
65
|
+
* @param tag - 16-byte authentication tag
|
|
66
|
+
* @throws Error if key is not 32 bytes, IV is not 12 bytes, or tag is not 16 bytes
|
|
67
|
+
*/
|
|
68
|
+
export declare function decryptAesGcm(key: Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array): Uint8Array;
|
|
69
|
+
/**
|
|
70
|
+
* Constant-time comparison to prevent timing attacks.
|
|
71
|
+
* @param a - First byte array to compare
|
|
72
|
+
* @param b - Second byte array to compare
|
|
73
|
+
* @throws Error if arrays have different lengths
|
|
74
|
+
*/
|
|
75
|
+
export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Convert a Uint8Array to hex string.
|
|
78
|
+
*/
|
|
79
|
+
export declare function bytesToHex(data: Uint8Array): string;
|
|
80
|
+
/**
|
|
81
|
+
* Encode a Uint8Array to base64url string.
|
|
82
|
+
* RFC 4648 Section 5: Base64 with URL and filename safe alphabet.
|
|
83
|
+
*/
|
|
84
|
+
export declare function base64urlEncode(data: Uint8Array): string;
|
|
85
|
+
/**
|
|
86
|
+
* Decode a base64url string to Uint8Array.
|
|
87
|
+
*/
|
|
88
|
+
export declare function base64urlDecode(data: string): Uint8Array;
|
|
89
|
+
/**
|
|
90
|
+
* Compute SHA-256 hash and return as base64url string.
|
|
91
|
+
* Commonly used for PKCE code_challenge (S256 method).
|
|
92
|
+
*/
|
|
93
|
+
export declare function sha256Base64url(data: string | Uint8Array): string;
|
|
94
|
+
export type { CryptoProvider, EncBlob };
|
|
95
|
+
export { isNode, isBrowser, assertNode } from './runtime';
|
|
96
|
+
export { type EncryptedBlob, EncryptedBlobError, encryptValue, decryptValue, tryDecryptValue, serializeBlob, deserializeBlob, tryDeserializeBlob, isValidEncryptedBlob, encryptAndSerialize, deserializeAndDecrypt, tryDeserializeAndDecrypt, } from './encrypted-blob';
|
|
97
|
+
export { MIN_CODE_VERIFIER_LENGTH, MAX_CODE_VERIFIER_LENGTH, DEFAULT_CODE_VERIFIER_LENGTH, PkceError, generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, generatePkcePair, isValidCodeVerifier, isValidCodeChallenge, type PkcePair, } from './pkce';
|
|
98
|
+
export { type SecretData, type SecretPersistenceOptions, type SecretValidationResult, secretDataSchema, validateSecretData, parseSecretData, isSecretPersistenceEnabled, resolveSecretPath, loadSecret, saveSecret, deleteSecret, generateSecret, createSecretData, getOrCreateSecret, clearCachedSecret, isSecretCached, } from './secret-persistence';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT algorithm helpers.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally dependency-free (no Node-only imports) so it can be safely re-used
|
|
5
|
+
* from both browser-compatible and Node-only code.
|
|
6
|
+
*/
|
|
7
|
+
export declare function jwtAlgToNodeAlg(jwtAlg: string): string;
|
|
8
|
+
export declare function isRsaPssAlg(jwtAlg: string): boolean;
|
package/crypto/node.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Crypto Provider
|
|
3
|
+
*
|
|
4
|
+
* Implementation using Node.js native crypto module.
|
|
5
|
+
*/
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
import type { CryptoProvider } from './types';
|
|
8
|
+
export { isRsaPssAlg, jwtAlgToNodeAlg } from './jwt-alg';
|
|
9
|
+
/**
|
|
10
|
+
* Node.js crypto provider implementation.
|
|
11
|
+
*/
|
|
12
|
+
export declare const nodeCrypto: CryptoProvider;
|
|
13
|
+
/**
|
|
14
|
+
* RSA JWK structure for public keys
|
|
15
|
+
*/
|
|
16
|
+
export interface RsaJwk {
|
|
17
|
+
kty: 'RSA';
|
|
18
|
+
kid: string;
|
|
19
|
+
alg: string;
|
|
20
|
+
use: 'sig';
|
|
21
|
+
n: string;
|
|
22
|
+
e: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* RSA key pair structure
|
|
26
|
+
*/
|
|
27
|
+
export interface RsaKeyPair {
|
|
28
|
+
/** Private key for signing */
|
|
29
|
+
privateKey: crypto.KeyObject;
|
|
30
|
+
/** Public key for verification */
|
|
31
|
+
publicKey: crypto.KeyObject;
|
|
32
|
+
/** Public key in JWK format */
|
|
33
|
+
publicJwk: RsaJwk;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate an RSA key pair with the specified modulus length
|
|
37
|
+
*
|
|
38
|
+
* @param modulusLength - Key size in bits (default: 2048, suitable for short-lived OAuth/JWT verification; use 3072+ for longer-term keys)
|
|
39
|
+
* @param alg - JWT algorithm (default: 'RS256')
|
|
40
|
+
* @returns RSA key pair with private, public keys and JWK
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateRsaKeyPair(modulusLength?: number, alg?: string): RsaKeyPair;
|
|
43
|
+
/**
|
|
44
|
+
* Sign data using RSA with the specified algorithm.
|
|
45
|
+
*
|
|
46
|
+
* For RSA-PSS (PS256/PS384/PS512), callers must pass appropriate padding/saltLength options.
|
|
47
|
+
*/
|
|
48
|
+
export declare function rsaSign(algorithm: string, data: Buffer, privateKey: crypto.KeyObject, options?: Omit<crypto.SignKeyObjectInput, 'key'>): Buffer;
|
|
49
|
+
export declare function rsaVerify(jwtAlg: string, data: Buffer, publicJwk: JsonWebKey, signature: Buffer): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Create a JWT signed with an RSA key
|
|
52
|
+
*
|
|
53
|
+
* @param payload - JWT payload
|
|
54
|
+
* @param privateKey - RSA private key
|
|
55
|
+
* @param kid - Key ID for the JWT header
|
|
56
|
+
* @param alg - JWT algorithm (default: 'RS256')
|
|
57
|
+
* @returns Signed JWT string
|
|
58
|
+
*/
|
|
59
|
+
export declare function createSignedJwt(payload: Record<string, unknown>, privateKey: crypto.KeyObject, kid: string, alg?: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities
|
|
3
|
+
*
|
|
4
|
+
* Implements RFC 7636 for secure authorization flows.
|
|
5
|
+
* Used for webhook callbacks, OAuth flows, and tool approval.
|
|
6
|
+
*
|
|
7
|
+
* @module @frontmcp/utils/pkce
|
|
8
|
+
*/
|
|
9
|
+
export { MIN_CODE_VERIFIER_LENGTH, MAX_CODE_VERIFIER_LENGTH, DEFAULT_CODE_VERIFIER_LENGTH, PkceError, generateCodeVerifier, generateCodeChallenge, verifyCodeChallenge, generatePkcePair, isValidCodeVerifier, isValidCodeChallenge, type PkcePair, } from './pkce';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities per RFC 7636
|
|
3
|
+
*
|
|
4
|
+
* PKCE is used to secure authorization flows by generating a cryptographically
|
|
5
|
+
* random code verifier and deriving a code challenge using SHA-256.
|
|
6
|
+
*
|
|
7
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Minimum length for code verifier per RFC 7636
|
|
11
|
+
*/
|
|
12
|
+
export declare const MIN_CODE_VERIFIER_LENGTH = 43;
|
|
13
|
+
/**
|
|
14
|
+
* Maximum length for code verifier per RFC 7636
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_CODE_VERIFIER_LENGTH = 128;
|
|
17
|
+
/**
|
|
18
|
+
* Default length for code verifier (64 chars provides ~384 bits of entropy)
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_CODE_VERIFIER_LENGTH = 64;
|
|
21
|
+
/**
|
|
22
|
+
* Error thrown when PKCE parameters are invalid
|
|
23
|
+
*/
|
|
24
|
+
export declare class PkceError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate a cryptographically random code verifier.
|
|
29
|
+
*
|
|
30
|
+
* The code verifier is a high-entropy cryptographic random string used in PKCE.
|
|
31
|
+
* It should be stored securely and never exposed to external systems.
|
|
32
|
+
*
|
|
33
|
+
* @param length - Length of verifier (43-128 chars per RFC 7636, default 64)
|
|
34
|
+
* @returns Base64url-encoded random string
|
|
35
|
+
* @throws {PkceError} If length is outside allowed range
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const verifier = generateCodeVerifier(); // 64 chars (default)
|
|
40
|
+
* const shortVerifier = generateCodeVerifier(43); // minimum length
|
|
41
|
+
* const longVerifier = generateCodeVerifier(128); // maximum length
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateCodeVerifier(length?: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Generate a code challenge from a code verifier using S256 method.
|
|
47
|
+
*
|
|
48
|
+
* The code challenge is derived by applying SHA-256 to the code verifier
|
|
49
|
+
* and base64url encoding the result. This is the only method specified
|
|
50
|
+
* in RFC 7636 for security-sensitive applications.
|
|
51
|
+
*
|
|
52
|
+
* @param codeVerifier - The code verifier string
|
|
53
|
+
* @returns Base64url-encoded SHA-256 hash of the verifier
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const verifier = generateCodeVerifier();
|
|
58
|
+
* const challenge = generateCodeChallenge(verifier);
|
|
59
|
+
* // Send `challenge` to external system, keep `verifier` secret
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function generateCodeChallenge(codeVerifier: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Verify that a code verifier matches a code challenge.
|
|
65
|
+
*
|
|
66
|
+
* This validates that SHA256(base64url(code_verifier)) === code_challenge.
|
|
67
|
+
* Used to verify callback responses in webhook/OAuth flows.
|
|
68
|
+
*
|
|
69
|
+
* @param codeVerifier - The original code verifier
|
|
70
|
+
* @param codeChallenge - The code challenge to verify against
|
|
71
|
+
* @returns true if the verifier produces the expected challenge
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // When callback arrives with code_verifier
|
|
76
|
+
* const isValid = verifyCodeChallenge(callbackVerifier, storedChallenge);
|
|
77
|
+
* if (!isValid) {
|
|
78
|
+
* throw new Error('PKCE validation failed');
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function verifyCodeChallenge(codeVerifier: string, codeChallenge: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Result of generating a PKCE pair
|
|
85
|
+
*/
|
|
86
|
+
export interface PkcePair {
|
|
87
|
+
/** The secret code verifier (keep secure, never expose externally) */
|
|
88
|
+
codeVerifier: string;
|
|
89
|
+
/** The code challenge derived from the verifier (safe to share) */
|
|
90
|
+
codeChallenge: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generate a complete PKCE pair (code verifier + code challenge).
|
|
94
|
+
*
|
|
95
|
+
* This is a convenience function that generates both values at once.
|
|
96
|
+
* The code_verifier should be stored securely (e.g., in Redis) while
|
|
97
|
+
* the code_challenge can be sent to external systems.
|
|
98
|
+
*
|
|
99
|
+
* @param length - Length of code verifier (default 64)
|
|
100
|
+
* @returns Object containing both codeVerifier and codeChallenge
|
|
101
|
+
* @throws {PkceError} If length is outside allowed range
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const { codeVerifier, codeChallenge } = generatePkcePair();
|
|
106
|
+
*
|
|
107
|
+
* // Store verifier in Redis with TTL
|
|
108
|
+
* await redis.set(`challenge:${codeChallenge}`, {
|
|
109
|
+
* verifier: codeVerifier,
|
|
110
|
+
* sessionId: session.id,
|
|
111
|
+
* expiresAt: Date.now() + 300000, // 5 minutes
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* // Send challenge to webhook (never send verifier!)
|
|
115
|
+
* await fetch(webhookUrl, {
|
|
116
|
+
* body: JSON.stringify({ code_challenge: codeChallenge, ... })
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare function generatePkcePair(length?: number): PkcePair;
|
|
121
|
+
/**
|
|
122
|
+
* Validate that a string is a valid code verifier.
|
|
123
|
+
*
|
|
124
|
+
* Checks that the string:
|
|
125
|
+
* - Has a length between 43 and 128 characters
|
|
126
|
+
* - Contains only unreserved characters per RFC 7636 (A-Z, a-z, 0-9, -, ., _, ~)
|
|
127
|
+
*
|
|
128
|
+
* @param value - The string to validate
|
|
129
|
+
* @returns true if valid, false otherwise
|
|
130
|
+
*/
|
|
131
|
+
export declare function isValidCodeVerifier(value: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Validate that a string is a valid code challenge.
|
|
134
|
+
*
|
|
135
|
+
* A valid code challenge is a base64url-encoded SHA-256 hash (43 characters).
|
|
136
|
+
*
|
|
137
|
+
* @param value - The string to validate
|
|
138
|
+
* @returns true if valid, false otherwise
|
|
139
|
+
*/
|
|
140
|
+
export declare function isValidCodeChallenge(value: string): boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Detection
|
|
3
|
+
*
|
|
4
|
+
* Utilities for detecting whether code is running in Node.js or browser environment.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Check if running in Node.js environment.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isNode(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Check if running in a browser-like environment with Web Crypto API.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isBrowser(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Assert that code is running in Node.js.
|
|
16
|
+
* @throws Error if not in Node.js environment
|
|
17
|
+
*/
|
|
18
|
+
export declare function assertNode(feature: string): void;
|