mcp-creatio 0.4.0 → 0.4.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/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +18 -11
- package/dist/cli.js.map +1 -1
- package/dist/creatio/auth/auth.d.ts +2 -0
- package/dist/creatio/auth/auth.d.ts.map +1 -1
- package/dist/creatio/auth/auth.js.map +1 -1
- package/dist/creatio/auth/providers/base-provider.d.ts +1 -0
- package/dist/creatio/auth/providers/base-provider.d.ts.map +1 -1
- package/dist/creatio/auth/providers/base-provider.js +3 -0
- package/dist/creatio/auth/providers/base-provider.js.map +1 -1
- package/dist/creatio/auth/providers/oauth2-code-provider.d.ts +3 -0
- package/dist/creatio/auth/providers/oauth2-code-provider.d.ts.map +1 -1
- package/dist/creatio/auth/providers/oauth2-code-provider.js +30 -24
- package/dist/creatio/auth/providers/oauth2-code-provider.js.map +1 -1
- package/dist/creatio/services/http-client.d.ts.map +1 -1
- package/dist/creatio/services/http-client.js +0 -1
- package/dist/creatio/services/http-client.js.map +1 -1
- package/dist/creatio/services/metadata-store.d.ts +5 -0
- package/dist/creatio/services/metadata-store.d.ts.map +1 -1
- package/dist/creatio/services/metadata-store.js +18 -6
- package/dist/creatio/services/metadata-store.js.map +1 -1
- package/dist/creatio/services/odata-crud-provider.d.ts +2 -0
- package/dist/creatio/services/odata-crud-provider.d.ts.map +1 -1
- package/dist/creatio/services/odata-crud-provider.js +10 -1
- package/dist/creatio/services/odata-crud-provider.js.map +1 -1
- package/dist/server/http/creatio-oauth-handlers.d.ts +0 -1
- package/dist/server/http/creatio-oauth-handlers.d.ts.map +1 -1
- package/dist/server/http/creatio-oauth-handlers.js +30 -23
- package/dist/server/http/creatio-oauth-handlers.js.map +1 -1
- package/dist/server/http/httpServer.d.ts +9 -0
- package/dist/server/http/httpServer.d.ts.map +1 -1
- package/dist/server/http/httpServer.js +34 -11
- package/dist/server/http/httpServer.js.map +1 -1
- package/dist/server/http/mcp-handlers.d.ts.map +1 -1
- package/dist/server/http/mcp-handlers.js +4 -1
- package/dist/server/http/mcp-handlers.js.map +1 -1
- package/dist/server/http/mcp-oauth-handlers.d.ts.map +1 -1
- package/dist/server/http/mcp-oauth-handlers.js +18 -6
- package/dist/server/http/mcp-oauth-handlers.js.map +1 -1
- package/dist/server/http/middleware.d.ts +7 -0
- package/dist/server/http/middleware.d.ts.map +1 -1
- package/dist/server/http/middleware.js +23 -0
- package/dist/server/http/middleware.js.map +1 -1
- package/dist/server/http/rate-limiter.d.ts +24 -0
- package/dist/server/http/rate-limiter.d.ts.map +1 -0
- package/dist/server/http/rate-limiter.js +42 -0
- package/dist/server/http/rate-limiter.js.map +1 -0
- package/dist/server/oauth/oauth-server.d.ts +0 -1
- package/dist/server/oauth/oauth-server.d.ts.map +1 -1
- package/dist/server/oauth/oauth-server.js +11 -21
- package/dist/server/oauth/oauth-server.js.map +1 -1
- package/dist/server/oauth/storage.d.ts +0 -2
- package/dist/server/oauth/storage.d.ts.map +1 -1
- package/dist/server/oauth/storage.js +0 -6
- package/dist/server/oauth/storage.js.map +1 -1
- package/dist/server/oauth/validators.d.ts +6 -0
- package/dist/server/oauth/validators.d.ts.map +1 -1
- package/dist/server/oauth/validators.js +28 -0
- package/dist/server/oauth/validators.js.map +1 -1
- package/dist/services/session-context.d.ts +8 -7
- package/dist/services/session-context.d.ts.map +1 -1
- package/dist/services/session-context.js +7 -27
- package/dist/services/session-context.js.map +1 -1
- package/package.json +12 -3
- package/.dockerignore +0 -12
- package/.editorconfig +0 -14
- package/.eslintrc.cjs +0 -18
- package/.gitattributes +0 -8
- package/.github/workflows/docker-publish.yml +0 -50
- package/.prettierignore +0 -3
- package/.prettierrc +0 -9
- package/.vscode/launch.json +0 -23
- package/.vscode/mcp.json +0 -13
- package/.vscode/settings.json +0 -16
- package/Agent.md +0 -190
- package/Debug.md +0 -32
- package/Dockerfile +0 -23
- package/docs/coding-style.md +0 -30
- package/eslint.config.cjs +0 -95
- package/src/cli.ts +0 -162
- package/src/config-builder.ts +0 -76
- package/src/consts.ts +0 -3
- package/src/creatio/auth/auth-manager.ts +0 -27
- package/src/creatio/auth/auth.ts +0 -31
- package/src/creatio/auth/index.ts +0 -3
- package/src/creatio/auth/providers/base-oauth2-provider.ts +0 -62
- package/src/creatio/auth/providers/base-provider.ts +0 -42
- package/src/creatio/auth/providers/index.ts +0 -4
- package/src/creatio/auth/providers/legacy-provider.ts +0 -70
- package/src/creatio/auth/providers/oauth2-code-provider.ts +0 -252
- package/src/creatio/auth/providers/oauth2-provider.ts +0 -91
- package/src/creatio/auth/providers/type.ts +0 -5
- package/src/creatio/client-config.ts +0 -34
- package/src/creatio/engines/admin-operation/admin-operation-engine.ts +0 -44
- package/src/creatio/engines/configuration/configuration-engine.ts +0 -26
- package/src/creatio/engines/crud/crud-engine.ts +0 -47
- package/src/creatio/engines/engine-manager.ts +0 -157
- package/src/creatio/engines/engine-registry.ts +0 -39
- package/src/creatio/engines/engine.ts +0 -3
- package/src/creatio/engines/feature/feature-engine.ts +0 -20
- package/src/creatio/engines/index.ts +0 -10
- package/src/creatio/engines/process/process-engine.ts +0 -20
- package/src/creatio/engines/sys-settings/sys-settings-engine.ts +0 -41
- package/src/creatio/engines/user/user-engine.ts +0 -20
- package/src/creatio/index.ts +0 -6
- package/src/creatio/provider-context.ts +0 -21
- package/src/creatio/providers/admin-operation-provider.ts +0 -34
- package/src/creatio/providers/configuration-provider.ts +0 -22
- package/src/creatio/providers/crud-provider.ts +0 -45
- package/src/creatio/providers/feature-provider.ts +0 -10
- package/src/creatio/providers/index.ts +0 -7
- package/src/creatio/providers/process-provider.ts +0 -15
- package/src/creatio/providers/sys-settings-provider.ts +0 -63
- package/src/creatio/providers/user-provider.ts +0 -12
- package/src/creatio/services/admin-operation-service-provider.ts +0 -115
- package/src/creatio/services/configuration-service-provider.ts +0 -127
- package/src/creatio/services/creatio-service-context.ts +0 -55
- package/src/creatio/services/feature-service-provider.ts +0 -60
- package/src/creatio/services/http-client.ts +0 -174
- package/src/creatio/services/index.ts +0 -10
- package/src/creatio/services/metadata-store.ts +0 -181
- package/src/creatio/services/odata-crud-provider.ts +0 -210
- package/src/creatio/services/process-service-provider.ts +0 -76
- package/src/creatio/services/sys-settings-service-provider.ts +0 -192
- package/src/creatio/services/user-info-provider.ts +0 -41
- package/src/index.ts +0 -44
- package/src/log.ts +0 -183
- package/src/server/http/creatio-oauth-handlers.ts +0 -146
- package/src/server/http/httpServer.ts +0 -150
- package/src/server/http/index.ts +0 -5
- package/src/server/http/mcp-handlers.ts +0 -92
- package/src/server/http/mcp-oauth-handlers.ts +0 -108
- package/src/server/http/middleware.ts +0 -91
- package/src/server/index.ts +0 -2
- package/src/server/mcp/filters.ts +0 -97
- package/src/server/mcp/index.ts +0 -1
- package/src/server/mcp/prompts-data.ts +0 -1292
- package/src/server/mcp/server.ts +0 -442
- package/src/server/mcp/tools-data.ts +0 -748
- package/src/server/oauth/client-manager.ts +0 -47
- package/src/server/oauth/index.ts +0 -6
- package/src/server/oauth/oauth-server.ts +0 -185
- package/src/server/oauth/storage.ts +0 -106
- package/src/server/oauth/token-manager.ts +0 -80
- package/src/server/oauth/types.ts +0 -55
- package/src/server/oauth/validators.ts +0 -56
- package/src/services/index.ts +0 -2
- package/src/services/session-context.ts +0 -232
- package/src/services/token-refresh-scheduler.ts +0 -68
- package/src/types/index.ts +0 -1
- package/src/types/network.ts +0 -7
- package/src/utils/context.ts +0 -49
- package/src/utils/env.ts +0 -12
- package/src/utils/index.ts +0 -5
- package/src/utils/mcp.ts +0 -8
- package/src/utils/network.ts +0 -65
- package/src/utils/pkce.ts +0 -39
- package/src/version.ts +0 -15
- package/tsconfig.json +0 -28
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
|
|
3
|
-
import log from '../../log';
|
|
4
|
-
|
|
5
|
-
import type { OAuthClient } from './types';
|
|
6
|
-
|
|
7
|
-
export class OAuthClientManager {
|
|
8
|
-
public static autoRegisterClient(client_id: string, redirect_uri: string): OAuthClient {
|
|
9
|
-
let clientName = 'Unknown MCP Client';
|
|
10
|
-
const redirectUris = [redirect_uri];
|
|
11
|
-
if (client_id.includes('claude')) {
|
|
12
|
-
clientName = 'Claude Desktop';
|
|
13
|
-
} else if (client_id.includes('vscode')) {
|
|
14
|
-
clientName = 'VS Code';
|
|
15
|
-
} else if (client_id.includes('cursor')) {
|
|
16
|
-
clientName = 'Cursor';
|
|
17
|
-
}
|
|
18
|
-
const client: OAuthClient = {
|
|
19
|
-
client_id,
|
|
20
|
-
redirect_uris: redirectUris,
|
|
21
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
22
|
-
response_types: ['code'],
|
|
23
|
-
token_endpoint_auth_method: 'none',
|
|
24
|
-
created_at: Date.now(),
|
|
25
|
-
};
|
|
26
|
-
log.info('oauth.client.auto_registered', {
|
|
27
|
-
client_id,
|
|
28
|
-
client_name: clientName,
|
|
29
|
-
redirect_uris: redirectUris,
|
|
30
|
-
});
|
|
31
|
-
return client;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public static createClient(redirect_uris: string[]): OAuthClient {
|
|
35
|
-
const client_id = crypto.randomUUID();
|
|
36
|
-
const client: OAuthClient = {
|
|
37
|
-
client_id,
|
|
38
|
-
redirect_uris,
|
|
39
|
-
grant_types: ['authorization_code', 'refresh_token'],
|
|
40
|
-
response_types: ['code'],
|
|
41
|
-
token_endpoint_auth_method: 'none',
|
|
42
|
-
created_at: Date.now(),
|
|
43
|
-
};
|
|
44
|
-
log.info('oauth.client.registered', { client_id, redirect_uris });
|
|
45
|
-
return client;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
|
|
3
|
-
import log from '../../log';
|
|
4
|
-
|
|
5
|
-
import { OAuthClientManager } from './client-manager';
|
|
6
|
-
import { OAuthStorage } from './storage';
|
|
7
|
-
import { OAuthTokenManager } from './token-manager';
|
|
8
|
-
import { OAuthValidators } from './validators';
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
OAuthAccessToken,
|
|
12
|
-
OAuthAuthorizationRequest,
|
|
13
|
-
OAuthAuthorizationServerMetadata,
|
|
14
|
-
OAuthClient,
|
|
15
|
-
OAuthError,
|
|
16
|
-
OAuthTokenRequest,
|
|
17
|
-
} from './types';
|
|
18
|
-
|
|
19
|
-
export class OAuthServer {
|
|
20
|
-
private readonly _jwtSecret: string = crypto.randomBytes(32).toString('hex');
|
|
21
|
-
private readonly _storage = new OAuthStorage();
|
|
22
|
-
private readonly _tokenManager: OAuthTokenManager;
|
|
23
|
-
private readonly _accessTokens = new Map<string, OAuthAccessToken>();
|
|
24
|
-
private _baseUrl: string;
|
|
25
|
-
|
|
26
|
-
constructor(baseUrl: string = 'http://localhost:3000') {
|
|
27
|
-
this._baseUrl = baseUrl;
|
|
28
|
-
this._tokenManager = new OAuthTokenManager(this._jwtSecret);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private _autoRegisterClientIfNeeded(client_id: string, redirect_uri: string): boolean {
|
|
32
|
-
if (this._storage.hasClient(client_id)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const client = OAuthClientManager.autoRegisterClient(client_id, redirect_uri);
|
|
36
|
-
this._storage.addClient(client);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public getAuthorizationServerMetadata(): OAuthAuthorizationServerMetadata {
|
|
41
|
-
return {
|
|
42
|
-
issuer: this._baseUrl,
|
|
43
|
-
authorization_endpoint: `${this._baseUrl}/authorize`,
|
|
44
|
-
token_endpoint: `${this._baseUrl}/token`,
|
|
45
|
-
registration_endpoint: `${this._baseUrl}/register`,
|
|
46
|
-
response_types_supported: ['code'],
|
|
47
|
-
grant_types_supported: ['authorization_code'],
|
|
48
|
-
token_endpoint_auth_methods_supported: ['none', 'client_secret_post'],
|
|
49
|
-
code_challenge_methods_supported: ['S256'],
|
|
50
|
-
scopes_supported: ['openid'],
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public registerClient(redirect_uris: string[]): OAuthClient {
|
|
55
|
-
const client = OAuthClientManager.createClient(redirect_uris);
|
|
56
|
-
this._storage.addClient(client);
|
|
57
|
-
return client;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public validateAuthorizationRequest(params: OAuthAuthorizationRequest): OAuthError | null {
|
|
61
|
-
let client = this._storage.getClient(params.client_id);
|
|
62
|
-
if (!client) {
|
|
63
|
-
const wasRegistered = this._autoRegisterClientIfNeeded(
|
|
64
|
-
params.client_id,
|
|
65
|
-
params.redirect_uri,
|
|
66
|
-
);
|
|
67
|
-
if (wasRegistered) {
|
|
68
|
-
client = this._storage.getClient(params.client_id);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return OAuthValidators.validateAuthorizationRequest(params, client);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
public storeState(state: string, client_id: string): void {
|
|
75
|
-
this._storage.storeState(state, client_id);
|
|
76
|
-
log.info('oauth.state.stored', { state, client_id });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public validateState(state: string, client_id: string): boolean {
|
|
80
|
-
log.info('oauth.state.validate_attempt', {
|
|
81
|
-
state,
|
|
82
|
-
client_id,
|
|
83
|
-
storedStates: this._storage.getAllStates(),
|
|
84
|
-
});
|
|
85
|
-
const stateData = this._storage.getState(state);
|
|
86
|
-
if (!stateData) {
|
|
87
|
-
log.warn('oauth.state.not_found', {
|
|
88
|
-
state,
|
|
89
|
-
storedStates: this._storage.getAllStates(),
|
|
90
|
-
});
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
if (stateData.expires_at < Date.now()) {
|
|
94
|
-
this._storage.deleteState(state);
|
|
95
|
-
log.warn('oauth.state.expired', { state });
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
if (stateData.client_id !== client_id) {
|
|
99
|
-
log.warn('oauth.state.client_mismatch', {
|
|
100
|
-
state,
|
|
101
|
-
expected: stateData.client_id,
|
|
102
|
-
actual: client_id,
|
|
103
|
-
});
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
this._storage.deleteState(state);
|
|
107
|
-
log.info('oauth.state.validated_successfully', { state, client_id });
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public generateAuthorizationCode(
|
|
112
|
-
client_id: string,
|
|
113
|
-
redirect_uri: string,
|
|
114
|
-
code_challenge: string,
|
|
115
|
-
code_challenge_method: string,
|
|
116
|
-
userKey: string,
|
|
117
|
-
): string {
|
|
118
|
-
const code = crypto.randomBytes(32).toString('base64url');
|
|
119
|
-
this._storage.storeAuthorizationCode(
|
|
120
|
-
code,
|
|
121
|
-
client_id,
|
|
122
|
-
redirect_uri,
|
|
123
|
-
code_challenge,
|
|
124
|
-
code_challenge_method,
|
|
125
|
-
userKey,
|
|
126
|
-
);
|
|
127
|
-
log.info('oauth.authorization_code.generated', { client_id, userKey });
|
|
128
|
-
return code;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
public async exchangeCodeForToken(
|
|
132
|
-
params: OAuthTokenRequest,
|
|
133
|
-
): Promise<OAuthAccessToken | OAuthError> {
|
|
134
|
-
log.info('oauth.token.exchange_start', {
|
|
135
|
-
grant_type: params.grant_type,
|
|
136
|
-
code: params.code ? '***' + params.code.slice(-4) : 'missing',
|
|
137
|
-
client_id: params.client_id,
|
|
138
|
-
redirect_uri: params.redirect_uri,
|
|
139
|
-
has_code_verifier: !!params.code_verifier,
|
|
140
|
-
stored_codes: this._storage.getAllStoredCodes().map((k) => '***' + k.slice(-4)),
|
|
141
|
-
});
|
|
142
|
-
const validationError = OAuthValidators.validateTokenRequest(params);
|
|
143
|
-
if (validationError) {
|
|
144
|
-
return validationError;
|
|
145
|
-
}
|
|
146
|
-
const authCode = this._storage.getAuthorizationCode(params.code!);
|
|
147
|
-
if (!authCode) {
|
|
148
|
-
log.error('oauth.token.code_not_found', {
|
|
149
|
-
code: '***' + params.code!.slice(-4),
|
|
150
|
-
stored_codes: this._storage.getAllStoredCodes().map((k) => '***' + k.slice(-4)),
|
|
151
|
-
});
|
|
152
|
-
return { error: 'invalid_grant', error_description: 'Invalid authorization code' };
|
|
153
|
-
}
|
|
154
|
-
const codeValidationError = this._tokenManager.validateAuthCodeData(authCode, params);
|
|
155
|
-
if (codeValidationError) {
|
|
156
|
-
if (
|
|
157
|
-
codeValidationError.error === 'invalid_grant' &&
|
|
158
|
-
codeValidationError.error_description === 'Authorization code expired'
|
|
159
|
-
) {
|
|
160
|
-
this._storage.deleteAuthorizationCode(params.code!);
|
|
161
|
-
}
|
|
162
|
-
return codeValidationError;
|
|
163
|
-
}
|
|
164
|
-
const tokenResponse = this._tokenManager.createTokenResponse(
|
|
165
|
-
authCode.userKey,
|
|
166
|
-
params.client_id,
|
|
167
|
-
);
|
|
168
|
-
this._accessTokens.set(tokenResponse.access_token, tokenResponse);
|
|
169
|
-
this._storage.deleteAuthorizationCode(params.code!);
|
|
170
|
-
log.info('oauth.token.issued', { client_id: params.client_id, userKey: authCode.userKey });
|
|
171
|
-
return tokenResponse;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
public validateAccessToken(token: string): string | null {
|
|
175
|
-
return this._tokenManager.validateAccessToken(token);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
public getClient(client_id: string): OAuthClient | undefined {
|
|
179
|
-
return this._storage.getClient(client_id);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
public cleanup(): void {
|
|
183
|
-
this._storage.cleanup();
|
|
184
|
-
}
|
|
185
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import log from '../../log';
|
|
2
|
-
|
|
3
|
-
import type { OAuthClient } from './types';
|
|
4
|
-
|
|
5
|
-
export interface AuthorizationCodeData {
|
|
6
|
-
client_id: string;
|
|
7
|
-
redirect_uri: string;
|
|
8
|
-
code_challenge: string;
|
|
9
|
-
code_challenge_method: string;
|
|
10
|
-
userKey: string;
|
|
11
|
-
expires_at: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface StateData {
|
|
15
|
-
client_id: string;
|
|
16
|
-
expires_at: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class OAuthStorage {
|
|
20
|
-
private readonly _clients = new Map<string, OAuthClient>();
|
|
21
|
-
private readonly _authorizationCodes = new Map<string, AuthorizationCodeData>();
|
|
22
|
-
private readonly _authorizationStates = new Map<string, StateData>();
|
|
23
|
-
|
|
24
|
-
public addClient(client: OAuthClient): void {
|
|
25
|
-
this._clients.set(client.client_id, client);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public getClient(client_id: string): OAuthClient | undefined {
|
|
29
|
-
return this._clients.get(client_id);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public hasClient(client_id: string): boolean {
|
|
33
|
-
return this._clients.has(client_id);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public storeAuthorizationCode(
|
|
37
|
-
code: string,
|
|
38
|
-
client_id: string,
|
|
39
|
-
redirect_uri: string,
|
|
40
|
-
code_challenge: string,
|
|
41
|
-
code_challenge_method: string,
|
|
42
|
-
userKey: string,
|
|
43
|
-
expiresInMs: number = 10 * 60 * 1000,
|
|
44
|
-
): void {
|
|
45
|
-
const expires_at = Date.now() + expiresInMs;
|
|
46
|
-
this._authorizationCodes.set(code, {
|
|
47
|
-
client_id,
|
|
48
|
-
redirect_uri,
|
|
49
|
-
code_challenge,
|
|
50
|
-
code_challenge_method,
|
|
51
|
-
userKey,
|
|
52
|
-
expires_at,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public getAuthorizationCode(code: string): AuthorizationCodeData | undefined {
|
|
57
|
-
return this._authorizationCodes.get(code);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public deleteAuthorizationCode(code: string): void {
|
|
61
|
-
this._authorizationCodes.delete(code);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public storeState(
|
|
65
|
-
state: string,
|
|
66
|
-
client_id: string,
|
|
67
|
-
expiresInMs: number = 30 * 60 * 1000,
|
|
68
|
-
): void {
|
|
69
|
-
const expires_at = Date.now() + expiresInMs;
|
|
70
|
-
this._authorizationStates.set(state, { client_id, expires_at });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public getState(state: string): StateData | undefined {
|
|
74
|
-
return this._authorizationStates.get(state);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
public deleteState(state: string): void {
|
|
78
|
-
this._authorizationStates.delete(state);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
public getAllStates(): string[] {
|
|
82
|
-
return Array.from(this._authorizationStates.keys());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public getAllStoredCodes(): string[] {
|
|
86
|
-
return Array.from(this._authorizationCodes.keys());
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public cleanup(): void {
|
|
90
|
-
const now = Date.now();
|
|
91
|
-
for (const [code, data] of this._authorizationCodes.entries()) {
|
|
92
|
-
if (now > data.expires_at) {
|
|
93
|
-
this._authorizationCodes.delete(code);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
for (const [state, data] of this._authorizationStates.entries()) {
|
|
97
|
-
if (now > data.expires_at) {
|
|
98
|
-
this._authorizationStates.delete(state);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
log.info('oauth.storage.cleanup.completed', {
|
|
102
|
-
remaining_codes: this._authorizationCodes.size,
|
|
103
|
-
remaining_states: this._authorizationStates.size,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
|
|
3
|
-
import jwt from 'jsonwebtoken';
|
|
4
|
-
|
|
5
|
-
import log from '../../log';
|
|
6
|
-
|
|
7
|
-
import type { AuthorizationCodeData } from './storage';
|
|
8
|
-
import type { OAuthAccessToken, OAuthError, OAuthTokenRequest } from './types';
|
|
9
|
-
|
|
10
|
-
export class OAuthTokenManager {
|
|
11
|
-
private readonly _jwtSecret: string;
|
|
12
|
-
|
|
13
|
-
constructor(jwtSecret: string) {
|
|
14
|
-
this._jwtSecret = jwtSecret;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public generateAccessToken(userKey: string, client_id: string): string {
|
|
18
|
-
return jwt.sign({ userKey, client_id }, this._jwtSecret, { expiresIn: '1h' });
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public generateRefreshToken(): string {
|
|
22
|
-
return crypto.randomBytes(32).toString('base64url');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public validateAccessToken(token: string): string | null {
|
|
26
|
-
try {
|
|
27
|
-
const decoded = jwt.verify(token, this._jwtSecret) as any;
|
|
28
|
-
return decoded.userKey || null;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
log.warn('oauth.token.invalid', { error: String(error) });
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
public createTokenResponse(
|
|
36
|
-
userKey: string,
|
|
37
|
-
client_id: string,
|
|
38
|
-
refresh_token_required: boolean = true,
|
|
39
|
-
): OAuthAccessToken {
|
|
40
|
-
const access_token = this.generateAccessToken(userKey, client_id);
|
|
41
|
-
const expires_in = 3600;
|
|
42
|
-
const tokenResponse: OAuthAccessToken = {
|
|
43
|
-
access_token,
|
|
44
|
-
token_type: 'Bearer',
|
|
45
|
-
expires_in,
|
|
46
|
-
userKey,
|
|
47
|
-
};
|
|
48
|
-
if (refresh_token_required) {
|
|
49
|
-
tokenResponse.refresh_token = this.generateRefreshToken();
|
|
50
|
-
}
|
|
51
|
-
return tokenResponse;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public verifyPKCE(code_verifier: string, code_challenge: string): boolean {
|
|
55
|
-
const hash = crypto.createHash('sha256').update(code_verifier).digest('base64url');
|
|
56
|
-
return hash === code_challenge;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public validateAuthCodeData(
|
|
60
|
-
authCode: AuthorizationCodeData,
|
|
61
|
-
params: OAuthTokenRequest,
|
|
62
|
-
): OAuthError | null {
|
|
63
|
-
if (Date.now() > authCode.expires_at) {
|
|
64
|
-
return { error: 'invalid_grant', error_description: 'Authorization code expired' };
|
|
65
|
-
}
|
|
66
|
-
if (authCode.client_id !== params.client_id) {
|
|
67
|
-
return { error: 'invalid_grant', error_description: 'Client mismatch' };
|
|
68
|
-
}
|
|
69
|
-
if (authCode.redirect_uri !== params.redirect_uri) {
|
|
70
|
-
return { error: 'invalid_grant', error_description: 'Redirect URI mismatch' };
|
|
71
|
-
}
|
|
72
|
-
if (!params.code_verifier) {
|
|
73
|
-
return { error: 'invalid_request', error_description: 'Missing code_verifier' };
|
|
74
|
-
}
|
|
75
|
-
if (!this.verifyPKCE(params.code_verifier, authCode.code_challenge)) {
|
|
76
|
-
return { error: 'invalid_grant', error_description: 'PKCE verification failed' };
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
export interface OAuthClient {
|
|
2
|
-
client_id: string;
|
|
3
|
-
client_secret?: string;
|
|
4
|
-
redirect_uris: string[];
|
|
5
|
-
grant_types: string[];
|
|
6
|
-
response_types?: string[];
|
|
7
|
-
token_endpoint_auth_method?: string;
|
|
8
|
-
created_at: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface OAuthAuthorizationRequest {
|
|
12
|
-
client_id: string;
|
|
13
|
-
redirect_uri: string;
|
|
14
|
-
response_type: string;
|
|
15
|
-
state?: string;
|
|
16
|
-
code_challenge: string;
|
|
17
|
-
code_challenge_method: string;
|
|
18
|
-
scope?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface OAuthTokenRequest {
|
|
22
|
-
grant_type: string;
|
|
23
|
-
client_id: string;
|
|
24
|
-
code?: string;
|
|
25
|
-
redirect_uri?: string;
|
|
26
|
-
code_verifier?: string;
|
|
27
|
-
refresh_token?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface OAuthAccessToken {
|
|
31
|
-
access_token: string;
|
|
32
|
-
token_type: 'Bearer';
|
|
33
|
-
expires_in: number;
|
|
34
|
-
refresh_token?: string;
|
|
35
|
-
scope?: string;
|
|
36
|
-
userKey: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface OAuthError {
|
|
40
|
-
error: string;
|
|
41
|
-
error_description?: string;
|
|
42
|
-
error_uri?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface OAuthAuthorizationServerMetadata {
|
|
46
|
-
issuer: string;
|
|
47
|
-
authorization_endpoint: string;
|
|
48
|
-
token_endpoint: string;
|
|
49
|
-
registration_endpoint: string;
|
|
50
|
-
response_types_supported: string[];
|
|
51
|
-
grant_types_supported: string[];
|
|
52
|
-
token_endpoint_auth_methods_supported: string[];
|
|
53
|
-
code_challenge_methods_supported: string[];
|
|
54
|
-
scopes_supported?: string[];
|
|
55
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
OAuthAuthorizationRequest,
|
|
3
|
-
OAuthClient,
|
|
4
|
-
OAuthError,
|
|
5
|
-
OAuthTokenRequest,
|
|
6
|
-
} from './types';
|
|
7
|
-
export class OAuthValidators {
|
|
8
|
-
public static validateAuthorizationRequest(
|
|
9
|
-
params: OAuthAuthorizationRequest,
|
|
10
|
-
client: OAuthClient | undefined,
|
|
11
|
-
): OAuthError | null {
|
|
12
|
-
if (!client) {
|
|
13
|
-
return { error: 'invalid_client', error_description: 'Client not found' };
|
|
14
|
-
}
|
|
15
|
-
if (!client.redirect_uris.includes(params.redirect_uri)) {
|
|
16
|
-
return { error: 'invalid_request', error_description: 'Invalid redirect_uri' };
|
|
17
|
-
}
|
|
18
|
-
if (params.response_type !== 'code') {
|
|
19
|
-
return { error: 'unsupported_response_type' };
|
|
20
|
-
}
|
|
21
|
-
if (!params.code_challenge || params.code_challenge_method !== 'S256') {
|
|
22
|
-
return { error: 'invalid_request', error_description: 'PKCE required' };
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public static validateTokenRequest(params: OAuthTokenRequest): OAuthError | null {
|
|
28
|
-
if (params.grant_type !== 'authorization_code') {
|
|
29
|
-
return { error: 'unsupported_grant_type' };
|
|
30
|
-
}
|
|
31
|
-
if (!params.code || !params.code_verifier) {
|
|
32
|
-
return { error: 'invalid_request', error_description: 'Missing code or code_verifier' };
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public static validateClientRegistration(redirect_uris: unknown): string | null {
|
|
38
|
-
if (!redirect_uris || !Array.isArray(redirect_uris)) {
|
|
39
|
-
return 'redirect_uris is required and must be an array';
|
|
40
|
-
}
|
|
41
|
-
if (redirect_uris.length === 0) {
|
|
42
|
-
return 'redirect_uris must contain at least one URI';
|
|
43
|
-
}
|
|
44
|
-
for (const uri of redirect_uris) {
|
|
45
|
-
if (typeof uri !== 'string') {
|
|
46
|
-
return 'All redirect_uris must be strings';
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
new URL(uri);
|
|
50
|
-
} catch {
|
|
51
|
-
return `Invalid redirect_uri: ${uri}`;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
package/src/services/index.ts
DELETED