@shin1ohno/sage 0.2.4 → 0.5.3
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/dist/cli/http-server-with-config.d.ts +38 -0
- package/dist/cli/http-server-with-config.d.ts.map +1 -0
- package/dist/cli/http-server-with-config.js +458 -0
- package/dist/cli/http-server-with-config.js.map +1 -0
- package/dist/cli/http-server.d.ts +74 -0
- package/dist/cli/http-server.d.ts.map +1 -0
- package/dist/cli/http-server.js +407 -0
- package/dist/cli/http-server.js.map +1 -0
- package/dist/cli/jwt-middleware.d.ts +36 -0
- package/dist/cli/jwt-middleware.d.ts.map +1 -0
- package/dist/cli/jwt-middleware.js +99 -0
- package/dist/cli/jwt-middleware.js.map +1 -0
- package/dist/cli/main-entry.d.ts +41 -0
- package/dist/cli/main-entry.d.ts.map +1 -0
- package/dist/cli/main-entry.js +80 -0
- package/dist/cli/main-entry.js.map +1 -0
- package/dist/cli/mcp-handler.d.ts +56 -0
- package/dist/cli/mcp-handler.d.ts.map +1 -0
- package/dist/cli/mcp-handler.js +2189 -0
- package/dist/cli/mcp-handler.js.map +1 -0
- package/dist/cli/parser.d.ts +43 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +162 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/remote-config-loader.d.ts +85 -0
- package/dist/cli/remote-config-loader.d.ts.map +1 -0
- package/dist/cli/remote-config-loader.js +129 -0
- package/dist/cli/remote-config-loader.js.map +1 -0
- package/dist/cli/secret-auth.d.ts +47 -0
- package/dist/cli/secret-auth.d.ts.map +1 -0
- package/dist/cli/secret-auth.js +165 -0
- package/dist/cli/secret-auth.js.map +1 -0
- package/dist/cli/sse-stream-handler.d.ts +45 -0
- package/dist/cli/sse-stream-handler.d.ts.map +1 -0
- package/dist/cli/sse-stream-handler.js +125 -0
- package/dist/cli/sse-stream-handler.js.map +1 -0
- package/dist/index.js +885 -209
- package/dist/index.js.map +1 -1
- package/dist/integrations/calendar-event-creator.d.ts +152 -0
- package/dist/integrations/calendar-event-creator.d.ts.map +1 -0
- package/dist/integrations/calendar-event-creator.js +507 -0
- package/dist/integrations/calendar-event-creator.js.map +1 -0
- package/dist/integrations/calendar-event-deleter.d.ts +137 -0
- package/dist/integrations/calendar-event-deleter.d.ts.map +1 -0
- package/dist/integrations/calendar-event-deleter.js +378 -0
- package/dist/integrations/calendar-event-deleter.js.map +1 -0
- package/dist/integrations/calendar-event-response.d.ts +213 -0
- package/dist/integrations/calendar-event-response.d.ts.map +1 -0
- package/dist/integrations/calendar-event-response.js +560 -0
- package/dist/integrations/calendar-event-response.js.map +1 -0
- package/dist/integrations/calendar-service.d.ts +85 -10
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js +317 -35
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/oauth/client-store.d.ts +36 -0
- package/dist/oauth/client-store.d.ts.map +1 -0
- package/dist/oauth/client-store.js +104 -0
- package/dist/oauth/client-store.js.map +1 -0
- package/dist/oauth/code-store.d.ts +48 -0
- package/dist/oauth/code-store.d.ts.map +1 -0
- package/dist/oauth/code-store.js +89 -0
- package/dist/oauth/code-store.js.map +1 -0
- package/dist/oauth/index.d.ts +13 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +21 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/oauth-handler.d.ts +101 -0
- package/dist/oauth/oauth-handler.d.ts.map +1 -0
- package/dist/oauth/oauth-handler.js +577 -0
- package/dist/oauth/oauth-handler.js.map +1 -0
- package/dist/oauth/oauth-server.d.ts +165 -0
- package/dist/oauth/oauth-server.d.ts.map +1 -0
- package/dist/oauth/oauth-server.js +489 -0
- package/dist/oauth/oauth-server.js.map +1 -0
- package/dist/oauth/pkce.d.ts +48 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +106 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/refresh-token-store.d.ts +45 -0
- package/dist/oauth/refresh-token-store.d.ts.map +1 -0
- package/dist/oauth/refresh-token-store.js +98 -0
- package/dist/oauth/refresh-token-store.js.map +1 -0
- package/dist/oauth/token-service.d.ts +46 -0
- package/dist/oauth/token-service.d.ts.map +1 -0
- package/dist/oauth/token-service.js +199 -0
- package/dist/oauth/token-service.js.map +1 -0
- package/dist/oauth/types.d.ts +264 -0
- package/dist/oauth/types.d.ts.map +1 -0
- package/dist/oauth/types.js +37 -0
- package/dist/oauth/types.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/manifest.json +12 -20
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth PKCE (S256) Implementation
|
|
3
|
+
* Requirements: 21.2, 26.4
|
|
4
|
+
*
|
|
5
|
+
* Implements PKCE (Proof Key for Code Exchange) using S256 method.
|
|
6
|
+
* Based on RFC 7636.
|
|
7
|
+
*/
|
|
8
|
+
import { CodeChallengeMethod } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Generate a cryptographically random code verifier
|
|
11
|
+
*
|
|
12
|
+
* @param length - Length of the verifier (default: 64, min: 43, max: 128)
|
|
13
|
+
* @returns A random code verifier string
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateCodeVerifier(length?: number): string;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a code challenge from a code verifier using S256 method
|
|
18
|
+
*
|
|
19
|
+
* S256: BASE64URL(SHA256(code_verifier))
|
|
20
|
+
*
|
|
21
|
+
* @param codeVerifier - The code verifier to hash
|
|
22
|
+
* @returns Base64URL encoded SHA256 hash of the verifier
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateCodeChallenge(codeVerifier: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Verify a code verifier against a code challenge
|
|
27
|
+
*
|
|
28
|
+
* @param codeVerifier - The code verifier from the token request
|
|
29
|
+
* @param codeChallenge - The code challenge from the authorization request
|
|
30
|
+
* @param method - The code challenge method (only 'S256' is supported)
|
|
31
|
+
* @returns True if the verifier matches the challenge
|
|
32
|
+
*/
|
|
33
|
+
export declare function verifyCodeChallenge(codeVerifier: string, codeChallenge: string, method: CodeChallengeMethod): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Validate code verifier format
|
|
36
|
+
*
|
|
37
|
+
* @param codeVerifier - The code verifier to validate
|
|
38
|
+
* @returns True if the verifier has valid format
|
|
39
|
+
*/
|
|
40
|
+
export declare function isValidCodeVerifier(codeVerifier: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Validate code challenge format
|
|
43
|
+
*
|
|
44
|
+
* @param codeChallenge - The code challenge to validate
|
|
45
|
+
* @returns True if the challenge has valid format
|
|
46
|
+
*/
|
|
47
|
+
export declare function isValidCodeChallenge(codeChallenge: string): boolean;
|
|
48
|
+
//# sourceMappingURL=pkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../src/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAkBjD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,MAAgC,GAAG,MAAM,CAYrF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CASlE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAajE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAanE"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth PKCE (S256) Implementation
|
|
3
|
+
* Requirements: 21.2, 26.4
|
|
4
|
+
*
|
|
5
|
+
* Implements PKCE (Proof Key for Code Exchange) using S256 method.
|
|
6
|
+
* Based on RFC 7636.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash, randomBytes } from 'crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Characters allowed in code verifier (unreserved characters per RFC 7636)
|
|
11
|
+
*/
|
|
12
|
+
const UNRESERVED_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
13
|
+
/**
|
|
14
|
+
* Default code verifier length
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_VERIFIER_LENGTH = 64;
|
|
17
|
+
/**
|
|
18
|
+
* Minimum and maximum verifier length per RFC 7636
|
|
19
|
+
*/
|
|
20
|
+
const MIN_VERIFIER_LENGTH = 43;
|
|
21
|
+
const MAX_VERIFIER_LENGTH = 128;
|
|
22
|
+
/**
|
|
23
|
+
* Generate a cryptographically random code verifier
|
|
24
|
+
*
|
|
25
|
+
* @param length - Length of the verifier (default: 64, min: 43, max: 128)
|
|
26
|
+
* @returns A random code verifier string
|
|
27
|
+
*/
|
|
28
|
+
export function generateCodeVerifier(length = DEFAULT_VERIFIER_LENGTH) {
|
|
29
|
+
// Clamp length to valid range
|
|
30
|
+
const validLength = Math.max(MIN_VERIFIER_LENGTH, Math.min(MAX_VERIFIER_LENGTH, length));
|
|
31
|
+
const bytes = randomBytes(validLength);
|
|
32
|
+
let verifier = '';
|
|
33
|
+
for (let i = 0; i < validLength; i++) {
|
|
34
|
+
verifier += UNRESERVED_CHARS[bytes[i] % UNRESERVED_CHARS.length];
|
|
35
|
+
}
|
|
36
|
+
return verifier;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generate a code challenge from a code verifier using S256 method
|
|
40
|
+
*
|
|
41
|
+
* S256: BASE64URL(SHA256(code_verifier))
|
|
42
|
+
*
|
|
43
|
+
* @param codeVerifier - The code verifier to hash
|
|
44
|
+
* @returns Base64URL encoded SHA256 hash of the verifier
|
|
45
|
+
*/
|
|
46
|
+
export function generateCodeChallenge(codeVerifier) {
|
|
47
|
+
// Calculate SHA256 hash
|
|
48
|
+
const hash = createHash('sha256').update(codeVerifier, 'ascii').digest();
|
|
49
|
+
// Convert to base64url encoding (no padding)
|
|
50
|
+
const base64 = hash.toString('base64');
|
|
51
|
+
const base64url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
52
|
+
return base64url;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Verify a code verifier against a code challenge
|
|
56
|
+
*
|
|
57
|
+
* @param codeVerifier - The code verifier from the token request
|
|
58
|
+
* @param codeChallenge - The code challenge from the authorization request
|
|
59
|
+
* @param method - The code challenge method (only 'S256' is supported)
|
|
60
|
+
* @returns True if the verifier matches the challenge
|
|
61
|
+
*/
|
|
62
|
+
export function verifyCodeChallenge(codeVerifier, codeChallenge, method) {
|
|
63
|
+
if (method !== 'S256') {
|
|
64
|
+
throw new Error('Only S256 code challenge method is supported');
|
|
65
|
+
}
|
|
66
|
+
// Generate challenge from verifier and compare
|
|
67
|
+
const expectedChallenge = generateCodeChallenge(codeVerifier);
|
|
68
|
+
return expectedChallenge === codeChallenge;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Validate code verifier format
|
|
72
|
+
*
|
|
73
|
+
* @param codeVerifier - The code verifier to validate
|
|
74
|
+
* @returns True if the verifier has valid format
|
|
75
|
+
*/
|
|
76
|
+
export function isValidCodeVerifier(codeVerifier) {
|
|
77
|
+
if (!codeVerifier) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
// Check length
|
|
81
|
+
if (codeVerifier.length < MIN_VERIFIER_LENGTH || codeVerifier.length > MAX_VERIFIER_LENGTH) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
// Check characters (unreserved characters only)
|
|
85
|
+
const validPattern = /^[A-Za-z0-9\-._~]+$/;
|
|
86
|
+
return validPattern.test(codeVerifier);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate code challenge format
|
|
90
|
+
*
|
|
91
|
+
* @param codeChallenge - The code challenge to validate
|
|
92
|
+
* @returns True if the challenge has valid format
|
|
93
|
+
*/
|
|
94
|
+
export function isValidCodeChallenge(codeChallenge) {
|
|
95
|
+
if (!codeChallenge) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
// S256 challenge should be base64url encoded SHA256 hash (43 characters)
|
|
99
|
+
if (codeChallenge.length !== 43) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// Check base64url format (no padding)
|
|
103
|
+
const validPattern = /^[A-Za-z0-9\-_]+$/;
|
|
104
|
+
return validPattern.test(codeChallenge);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/oauth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAGjD;;GAEG;AACH,MAAM,gBAAgB,GAAG,oEAAoE,CAAC;AAE9F;;GAEG;AACH,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAEnC;;GAEG;AACH,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB,uBAAuB;IAC3E,8BAA8B;IAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;IAEzF,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,QAAQ,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAoB;IACxD,wBAAwB;IACxB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;IAEzE,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEpF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAoB,EACpB,aAAqB,EACrB,MAA2B;IAE3B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,+CAA+C;IAC/C,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC9D,OAAO,iBAAiB,KAAK,aAAa,CAAC;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,eAAe;IACf,IAAI,YAAY,CAAC,MAAM,GAAG,mBAAmB,IAAI,YAAY,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC3F,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gDAAgD;IAChD,MAAM,YAAY,GAAG,qBAAqB,CAAC;IAC3C,OAAO,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,aAAqB;IACxD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,yEAAyE;IACzE,IAAI,aAAa,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,sCAAsC;IACtC,MAAM,YAAY,GAAG,mBAAmB,CAAC;IACzC,OAAO,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Refresh Token Store
|
|
3
|
+
* Requirements: 21.6, 26.3, 26.8
|
|
4
|
+
*
|
|
5
|
+
* Manages refresh token generation, storage, validation, and rotation.
|
|
6
|
+
*/
|
|
7
|
+
import { RefreshToken } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for Refresh Token Store
|
|
10
|
+
*/
|
|
11
|
+
export interface RefreshTokenStoreConfig {
|
|
12
|
+
expirySeconds: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for generating a refresh token
|
|
16
|
+
*/
|
|
17
|
+
export interface GenerateRefreshTokenOptions {
|
|
18
|
+
clientId: string;
|
|
19
|
+
userId: string;
|
|
20
|
+
scope: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result of validating a refresh token
|
|
24
|
+
*/
|
|
25
|
+
export interface RefreshTokenValidationResult {
|
|
26
|
+
valid: boolean;
|
|
27
|
+
tokenData?: RefreshToken;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Refresh Token Store Interface
|
|
32
|
+
*/
|
|
33
|
+
export interface RefreshTokenStore {
|
|
34
|
+
generateToken(options: GenerateRefreshTokenOptions): Promise<string>;
|
|
35
|
+
validateToken(token: string, clientId: string): Promise<RefreshTokenValidationResult>;
|
|
36
|
+
rotateToken(token: string, clientId: string): Promise<string | null>;
|
|
37
|
+
revokeToken(token: string): Promise<void>;
|
|
38
|
+
revokeAllForClient(clientId: string): Promise<void>;
|
|
39
|
+
cleanup(): Promise<number>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a Refresh Token Store instance
|
|
43
|
+
*/
|
|
44
|
+
export declare function createRefreshTokenStore(config: RefreshTokenStoreConfig): RefreshTokenStore;
|
|
45
|
+
//# sourceMappingURL=refresh-token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-token-store.d.ts","sourceRoot":"","sources":["../../src/oauth/refresh-token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IACtF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACrE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5B;AA6GD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,uBAAuB,GAAG,iBAAiB,CAE1F"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Refresh Token Store
|
|
3
|
+
* Requirements: 21.6, 26.3, 26.8
|
|
4
|
+
*
|
|
5
|
+
* Manages refresh token generation, storage, validation, and rotation.
|
|
6
|
+
*/
|
|
7
|
+
import { randomBytes } from 'crypto';
|
|
8
|
+
/**
|
|
9
|
+
* In-memory Refresh Token Store Implementation
|
|
10
|
+
*/
|
|
11
|
+
class InMemoryRefreshTokenStore {
|
|
12
|
+
tokens = new Map();
|
|
13
|
+
config;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
async generateToken(options) {
|
|
18
|
+
// Generate secure random token
|
|
19
|
+
const token = randomBytes(32).toString('base64url');
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const expiresAt = now + this.config.expirySeconds * 1000;
|
|
22
|
+
const tokenData = {
|
|
23
|
+
token,
|
|
24
|
+
client_id: options.clientId,
|
|
25
|
+
user_id: options.userId,
|
|
26
|
+
scope: options.scope,
|
|
27
|
+
created_at: now,
|
|
28
|
+
expires_at: expiresAt,
|
|
29
|
+
rotated: false,
|
|
30
|
+
};
|
|
31
|
+
this.tokens.set(token, tokenData);
|
|
32
|
+
return token;
|
|
33
|
+
}
|
|
34
|
+
async validateToken(token, clientId) {
|
|
35
|
+
const tokenData = this.tokens.get(token);
|
|
36
|
+
if (!tokenData) {
|
|
37
|
+
return { valid: false, error: 'invalid_grant' };
|
|
38
|
+
}
|
|
39
|
+
// Check if token has been rotated
|
|
40
|
+
if (tokenData.rotated) {
|
|
41
|
+
return { valid: false, error: 'invalid_grant' };
|
|
42
|
+
}
|
|
43
|
+
// Check if token has expired
|
|
44
|
+
if (Date.now() > tokenData.expires_at) {
|
|
45
|
+
this.tokens.delete(token);
|
|
46
|
+
return { valid: false, error: 'invalid_grant' };
|
|
47
|
+
}
|
|
48
|
+
// Verify client_id matches
|
|
49
|
+
if (tokenData.client_id !== clientId) {
|
|
50
|
+
return { valid: false, error: 'invalid_grant' };
|
|
51
|
+
}
|
|
52
|
+
return { valid: true, tokenData };
|
|
53
|
+
}
|
|
54
|
+
async rotateToken(token, clientId) {
|
|
55
|
+
const validationResult = await this.validateToken(token, clientId);
|
|
56
|
+
if (!validationResult.valid || !validationResult.tokenData) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Mark old token as rotated (Requirement 26.8)
|
|
60
|
+
const oldTokenData = this.tokens.get(token);
|
|
61
|
+
oldTokenData.rotated = true;
|
|
62
|
+
// Generate new token with same scope
|
|
63
|
+
const newToken = await this.generateToken({
|
|
64
|
+
clientId: validationResult.tokenData.client_id,
|
|
65
|
+
userId: validationResult.tokenData.user_id,
|
|
66
|
+
scope: validationResult.tokenData.scope,
|
|
67
|
+
});
|
|
68
|
+
return newToken;
|
|
69
|
+
}
|
|
70
|
+
async revokeToken(token) {
|
|
71
|
+
this.tokens.delete(token);
|
|
72
|
+
}
|
|
73
|
+
async revokeAllForClient(clientId) {
|
|
74
|
+
for (const [token, data] of this.tokens.entries()) {
|
|
75
|
+
if (data.client_id === clientId) {
|
|
76
|
+
this.tokens.delete(token);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async cleanup() {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
let cleanedCount = 0;
|
|
83
|
+
for (const [token, data] of this.tokens.entries()) {
|
|
84
|
+
if (data.expires_at < now || data.rotated) {
|
|
85
|
+
this.tokens.delete(token);
|
|
86
|
+
cleanedCount++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return cleanedCount;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create a Refresh Token Store instance
|
|
94
|
+
*/
|
|
95
|
+
export function createRefreshTokenStore(config) {
|
|
96
|
+
return new InMemoryRefreshTokenStore(config);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=refresh-token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-token-store.js","sourceRoot":"","sources":["../../src/oauth/refresh-token-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Service
|
|
3
|
+
* Requirements: 21.4, 21.5, 26.6, 26.7, 27.1-27.5
|
|
4
|
+
*
|
|
5
|
+
* Implements JWT access token generation and verification using RS256 signing.
|
|
6
|
+
*/
|
|
7
|
+
import { TokenResponse, VerifyTokenResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Token Service Configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface TokenServiceConfig {
|
|
12
|
+
issuer: string;
|
|
13
|
+
privateKey: string;
|
|
14
|
+
publicKey: string;
|
|
15
|
+
accessTokenExpiry: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Access Token Generation Options
|
|
19
|
+
*/
|
|
20
|
+
export interface GenerateAccessTokenOptions {
|
|
21
|
+
clientId: string;
|
|
22
|
+
userId: string;
|
|
23
|
+
scope: string;
|
|
24
|
+
audience: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Token Service Interface
|
|
28
|
+
*/
|
|
29
|
+
export interface TokenService {
|
|
30
|
+
generateAccessToken(options: GenerateAccessTokenOptions): Promise<TokenResponse>;
|
|
31
|
+
verifyAccessToken(token: string, expectedAudience?: string): Promise<VerifyTokenResult>;
|
|
32
|
+
extractTokenFromHeader(header: string | undefined): string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a Token Service instance
|
|
36
|
+
*/
|
|
37
|
+
export declare function createTokenService(config: TokenServiceConfig): TokenService;
|
|
38
|
+
/**
|
|
39
|
+
* Generate RSA key pair for JWT signing
|
|
40
|
+
* Requirement: RS256 signing
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateKeyPair(): Promise<{
|
|
43
|
+
privateKey: string;
|
|
44
|
+
publicKey: string;
|
|
45
|
+
}>;
|
|
46
|
+
//# sourceMappingURL=token-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-service.d.ts","sourceRoot":"","sources":["../../src/oauth/token-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAqB,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACjF,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxF,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC;CACnE;AAgND;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAc1F"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Service
|
|
3
|
+
* Requirements: 21.4, 21.5, 26.6, 26.7, 27.1-27.5
|
|
4
|
+
*
|
|
5
|
+
* Implements JWT access token generation and verification using RS256 signing.
|
|
6
|
+
*/
|
|
7
|
+
import { createSign, createVerify, generateKeyPairSync, randomUUID } from 'crypto';
|
|
8
|
+
/**
|
|
9
|
+
* Parse duration string to seconds
|
|
10
|
+
* Supports: s (seconds), m (minutes), h (hours), d (days), w (weeks)
|
|
11
|
+
*/
|
|
12
|
+
function parseDuration(duration) {
|
|
13
|
+
const match = duration.match(/^(\d+)([smhdw])$/);
|
|
14
|
+
if (!match) {
|
|
15
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
16
|
+
}
|
|
17
|
+
const value = parseInt(match[1], 10);
|
|
18
|
+
const unit = match[2];
|
|
19
|
+
switch (unit) {
|
|
20
|
+
case 's':
|
|
21
|
+
return value;
|
|
22
|
+
case 'm':
|
|
23
|
+
return value * 60;
|
|
24
|
+
case 'h':
|
|
25
|
+
return value * 3600;
|
|
26
|
+
case 'd':
|
|
27
|
+
return value * 86400;
|
|
28
|
+
case 'w':
|
|
29
|
+
return value * 604800;
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(`Unknown duration unit: ${unit}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Base64URL encode a string
|
|
36
|
+
*/
|
|
37
|
+
function base64UrlEncode(data) {
|
|
38
|
+
const base64 = typeof data === 'string' ? Buffer.from(data).toString('base64') : data.toString('base64');
|
|
39
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Base64URL decode a string
|
|
43
|
+
*/
|
|
44
|
+
function base64UrlDecode(data) {
|
|
45
|
+
// Add padding if needed
|
|
46
|
+
const padding = 4 - (data.length % 4);
|
|
47
|
+
const padded = padding < 4 ? data + '='.repeat(padding) : data;
|
|
48
|
+
const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
|
|
49
|
+
return Buffer.from(base64, 'base64');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create JWT token using RS256
|
|
53
|
+
*/
|
|
54
|
+
function createJWT(payload, privateKey) {
|
|
55
|
+
// Header
|
|
56
|
+
const header = {
|
|
57
|
+
alg: 'RS256',
|
|
58
|
+
typ: 'JWT',
|
|
59
|
+
};
|
|
60
|
+
// Encode header and payload
|
|
61
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(header));
|
|
62
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
|
|
63
|
+
// Create signature input
|
|
64
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
65
|
+
// Sign using RS256
|
|
66
|
+
const sign = createSign('RSA-SHA256');
|
|
67
|
+
sign.update(signatureInput);
|
|
68
|
+
const signature = sign.sign(privateKey);
|
|
69
|
+
// Encode signature
|
|
70
|
+
const encodedSignature = base64UrlEncode(signature);
|
|
71
|
+
return `${signatureInput}.${encodedSignature}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verify JWT token using RS256
|
|
75
|
+
*/
|
|
76
|
+
function verifyJWT(token, publicKey) {
|
|
77
|
+
const parts = token.split('.');
|
|
78
|
+
if (parts.length !== 3) {
|
|
79
|
+
return { valid: false, error: 'Invalid token format' };
|
|
80
|
+
}
|
|
81
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
82
|
+
try {
|
|
83
|
+
// Decode and verify header
|
|
84
|
+
const header = JSON.parse(base64UrlDecode(encodedHeader).toString());
|
|
85
|
+
if (header.alg !== 'RS256') {
|
|
86
|
+
return { valid: false, error: 'Invalid algorithm' };
|
|
87
|
+
}
|
|
88
|
+
// Verify signature
|
|
89
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
90
|
+
const signature = base64UrlDecode(encodedSignature);
|
|
91
|
+
const verify = createVerify('RSA-SHA256');
|
|
92
|
+
verify.update(signatureInput);
|
|
93
|
+
const isValid = verify.verify(publicKey, signature);
|
|
94
|
+
if (!isValid) {
|
|
95
|
+
return { valid: false, error: 'Invalid signature' };
|
|
96
|
+
}
|
|
97
|
+
// Decode payload
|
|
98
|
+
const payload = JSON.parse(base64UrlDecode(encodedPayload).toString());
|
|
99
|
+
return { valid: true, payload };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { valid: false, error: error instanceof Error ? error.message : 'Token verification failed' };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Token Service Implementation
|
|
107
|
+
*/
|
|
108
|
+
class TokenServiceImpl {
|
|
109
|
+
config;
|
|
110
|
+
expirySeconds;
|
|
111
|
+
constructor(config) {
|
|
112
|
+
this.config = config;
|
|
113
|
+
this.expirySeconds = parseDuration(config.accessTokenExpiry);
|
|
114
|
+
}
|
|
115
|
+
async generateAccessToken(options) {
|
|
116
|
+
const now = Math.floor(Date.now() / 1000);
|
|
117
|
+
const exp = now + this.expirySeconds;
|
|
118
|
+
const claims = {
|
|
119
|
+
iss: this.config.issuer,
|
|
120
|
+
sub: options.userId,
|
|
121
|
+
aud: options.audience,
|
|
122
|
+
exp,
|
|
123
|
+
iat: now,
|
|
124
|
+
jti: randomUUID(),
|
|
125
|
+
client_id: options.clientId,
|
|
126
|
+
scope: options.scope,
|
|
127
|
+
};
|
|
128
|
+
const accessToken = createJWT(claims, this.config.privateKey);
|
|
129
|
+
return {
|
|
130
|
+
access_token: accessToken,
|
|
131
|
+
token_type: 'Bearer',
|
|
132
|
+
expires_in: this.expirySeconds,
|
|
133
|
+
scope: options.scope,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async verifyAccessToken(token, expectedAudience) {
|
|
137
|
+
const result = verifyJWT(token, this.config.publicKey);
|
|
138
|
+
if (!result.valid) {
|
|
139
|
+
return { valid: false, error: result.error };
|
|
140
|
+
}
|
|
141
|
+
const payload = result.payload;
|
|
142
|
+
// Verify issuer
|
|
143
|
+
if (payload.iss !== this.config.issuer) {
|
|
144
|
+
return { valid: false, error: 'Invalid issuer' };
|
|
145
|
+
}
|
|
146
|
+
// Verify expiration
|
|
147
|
+
const now = Math.floor(Date.now() / 1000);
|
|
148
|
+
if (payload.exp < now) {
|
|
149
|
+
return { valid: false, error: 'Token expired' };
|
|
150
|
+
}
|
|
151
|
+
// Verify audience if specified
|
|
152
|
+
if (expectedAudience && payload.aud !== expectedAudience) {
|
|
153
|
+
return { valid: false, error: 'Invalid audience' };
|
|
154
|
+
}
|
|
155
|
+
return { valid: true, claims: payload };
|
|
156
|
+
}
|
|
157
|
+
extractTokenFromHeader(header) {
|
|
158
|
+
if (!header) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const parts = header.split(' ');
|
|
162
|
+
if (parts.length !== 2) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
if (parts[0].toLowerCase() !== 'bearer') {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const token = parts[1];
|
|
169
|
+
if (!token || token.trim() === '') {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return token;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a Token Service instance
|
|
177
|
+
*/
|
|
178
|
+
export function createTokenService(config) {
|
|
179
|
+
return new TokenServiceImpl(config);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Generate RSA key pair for JWT signing
|
|
183
|
+
* Requirement: RS256 signing
|
|
184
|
+
*/
|
|
185
|
+
export async function generateKeyPair() {
|
|
186
|
+
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
|
|
187
|
+
modulusLength: 2048,
|
|
188
|
+
publicKeyEncoding: {
|
|
189
|
+
type: 'spki',
|
|
190
|
+
format: 'pem',
|
|
191
|
+
},
|
|
192
|
+
privateKeyEncoding: {
|
|
193
|
+
type: 'pkcs8',
|
|
194
|
+
format: 'pem',
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
return { privateKey, publicKey };
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=token-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-service.js","sourceRoot":"","sources":["../../src/oauth/token-service.ts"],"names":[],"mappings":"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"}
|