@rekog/mcp-nest 1.7.1 → 1.7.2
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/README.md +1 -1
- package/dist/authz/adapters/github.adapter.d.ts +40 -0
- package/dist/authz/adapters/github.adapter.d.ts.map +1 -0
- package/dist/authz/adapters/github.adapter.js +212 -0
- package/dist/authz/adapters/github.adapter.js.map +1 -0
- package/dist/authz/adapters/oidc.adapter.d.ts +21 -0
- package/dist/authz/adapters/oidc.adapter.d.ts.map +1 -0
- package/dist/authz/adapters/oidc.adapter.js +125 -0
- package/dist/authz/adapters/oidc.adapter.js.map +1 -0
- package/dist/authz/controllers/resource-metadata.controller.d.ts +62 -0
- package/dist/authz/controllers/resource-metadata.controller.d.ts.map +1 -0
- package/dist/authz/controllers/resource-metadata.controller.js +84 -0
- package/dist/authz/controllers/resource-metadata.controller.js.map +1 -0
- package/dist/authz/controllers/well-known.controller.d.ts +63 -0
- package/dist/authz/controllers/well-known.controller.d.ts.map +1 -0
- package/dist/authz/controllers/well-known.controller.js +131 -0
- package/dist/authz/controllers/well-known.controller.js.map +1 -0
- package/dist/authz/examples/oidc-provider-example.d.ts +3 -0
- package/dist/authz/examples/oidc-provider-example.d.ts.map +1 -0
- package/dist/authz/examples/oidc-provider-example.js +51 -0
- package/dist/authz/examples/oidc-provider-example.js.map +1 -0
- package/dist/authz/guards/jwt-auth.guard.js +1 -1
- package/dist/authz/guards/jwt-auth.guard.js.map +1 -1
- package/dist/authz/mcp-oauth.controller.d.ts +20 -11
- package/dist/authz/mcp-oauth.controller.d.ts.map +1 -1
- package/dist/authz/mcp-oauth.controller.js +91 -17
- package/dist/authz/mcp-oauth.controller.js.map +1 -1
- package/dist/authz/mcp-oauth.module.d.ts.map +1 -1
- package/dist/authz/mcp-oauth.module.js +2 -2
- package/dist/authz/mcp-oauth.module.js.map +1 -1
- package/dist/authz/oidc-provider.controller.d.ts +48 -0
- package/dist/authz/oidc-provider.controller.d.ts.map +1 -0
- package/dist/authz/oidc-provider.controller.js +194 -0
- package/dist/authz/oidc-provider.controller.js.map +1 -0
- package/dist/authz/oidc-provider.module.d.ts +11 -0
- package/dist/authz/oidc-provider.module.d.ts.map +1 -0
- package/dist/authz/oidc-provider.module.js +275 -0
- package/dist/authz/oidc-provider.module.js.map +1 -0
- package/dist/authz/providers/oauth-provider.interface.d.ts +1 -1
- package/dist/authz/providers/oauth-provider.interface.d.ts.map +1 -1
- package/dist/authz/providers/oauth-provider.interface.js.map +1 -1
- package/dist/authz/services/client.service.d.ts.map +1 -1
- package/dist/authz/services/client.service.js +14 -1
- package/dist/authz/services/client.service.js.map +1 -1
- package/dist/authz/services/jwt-token.service.d.ts +1 -0
- package/dist/authz/services/jwt-token.service.d.ts.map +1 -1
- package/dist/authz/services/jwt-token.service.js +10 -6
- package/dist/authz/services/jwt-token.service.js.map +1 -1
- package/dist/authz/stores/oauth-store.interface.d.ts +1 -0
- package/dist/authz/stores/oauth-store.interface.d.ts.map +1 -1
- package/dist/authz/stores/oauth-store.interface.js.map +1 -1
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts +1 -0
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts.map +1 -1
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.js +4 -0
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.js.map +1 -1
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts +1 -0
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts.map +1 -1
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.js +4 -0
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.js.map +1 -1
- package/dist/mcp/decorators/prompt.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/resource-template.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/resource.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/tool.decorator.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/authz/guards/jwt-auth.guard.ts +1 -1
- package/src/authz/mcp-oauth.controller.ts +152 -15
- package/src/authz/mcp-oauth.module.ts +4 -2
- package/src/authz/providers/oauth-provider.interface.ts +1 -1
- package/src/authz/services/client.service.ts +25 -1
- package/src/authz/services/jwt-token.service.ts +16 -8
- package/src/authz/stores/oauth-store.interface.ts +1 -0
- package/src/authz/stores/typeorm/entities/authorization-code.entity.ts +3 -0
- package/src/authz/stores/typeorm/entities/oauth-client.entity.ts +3 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { OAuthModuleOptions } from '../providers/oauth-provider.interface';
|
|
2
|
+
export declare class WellKnownController {
|
|
3
|
+
private readonly options;
|
|
4
|
+
constructor(options: OAuthModuleOptions);
|
|
5
|
+
getAuthorizationServerMetadata(): {
|
|
6
|
+
issuer: string;
|
|
7
|
+
authorization_endpoint: string;
|
|
8
|
+
token_endpoint: string;
|
|
9
|
+
userinfo_endpoint: string;
|
|
10
|
+
jwks_uri: string;
|
|
11
|
+
registration_endpoint: string;
|
|
12
|
+
scopes_supported: string[];
|
|
13
|
+
response_types_supported: string[];
|
|
14
|
+
grant_types_supported: string[];
|
|
15
|
+
subject_types_supported: string[];
|
|
16
|
+
token_endpoint_auth_methods_supported: string[];
|
|
17
|
+
code_challenge_methods_supported: string[];
|
|
18
|
+
claims_supported: string[];
|
|
19
|
+
introspection_endpoint: string;
|
|
20
|
+
revocation_endpoint: string;
|
|
21
|
+
service_documentation: string;
|
|
22
|
+
end_session_endpoint: string;
|
|
23
|
+
check_session_iframe: string;
|
|
24
|
+
'x-mcp-capabilities': string[];
|
|
25
|
+
'x-mcp-version': string;
|
|
26
|
+
'x-api-version': string;
|
|
27
|
+
};
|
|
28
|
+
getOpenIdConfiguration(): {
|
|
29
|
+
url: string;
|
|
30
|
+
statusCode: number;
|
|
31
|
+
};
|
|
32
|
+
getOAuthAuthorizationServer(): {
|
|
33
|
+
issuer: string;
|
|
34
|
+
authorization_endpoint: string;
|
|
35
|
+
token_endpoint: string;
|
|
36
|
+
userinfo_endpoint: string;
|
|
37
|
+
jwks_uri: string;
|
|
38
|
+
registration_endpoint: string;
|
|
39
|
+
scopes_supported: string[];
|
|
40
|
+
response_types_supported: string[];
|
|
41
|
+
grant_types_supported: string[];
|
|
42
|
+
subject_types_supported: string[];
|
|
43
|
+
token_endpoint_auth_methods_supported: string[];
|
|
44
|
+
code_challenge_methods_supported: string[];
|
|
45
|
+
claims_supported: string[];
|
|
46
|
+
introspection_endpoint: string;
|
|
47
|
+
revocation_endpoint: string;
|
|
48
|
+
service_documentation: string;
|
|
49
|
+
end_session_endpoint: string;
|
|
50
|
+
check_session_iframe: string;
|
|
51
|
+
'x-mcp-capabilities': string[];
|
|
52
|
+
'x-mcp-version': string;
|
|
53
|
+
'x-api-version': string;
|
|
54
|
+
};
|
|
55
|
+
getWebFinger(): {
|
|
56
|
+
subject: string;
|
|
57
|
+
links: {
|
|
58
|
+
rel: string;
|
|
59
|
+
href: string;
|
|
60
|
+
}[];
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=well-known.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"well-known.controller.d.ts","sourceRoot":"","sources":["../../../src/authz/controllers/well-known.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAQ3E,qBACa,mBAAmB;IAEI,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,kBAAkB;IAU9E,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;IAwE9B,sBAAsB;;;;IAWtB,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;IAS3B,YAAY;;;;;;;CAWb"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WellKnownController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
let WellKnownController = class WellKnownController {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
}
|
|
21
|
+
getAuthorizationServerMetadata() {
|
|
22
|
+
return {
|
|
23
|
+
issuer: this.options.serverUrl,
|
|
24
|
+
authorization_endpoint: `${this.options.serverUrl}/oidc/auth`,
|
|
25
|
+
token_endpoint: `${this.options.serverUrl}/oidc/token`,
|
|
26
|
+
userinfo_endpoint: `${this.options.serverUrl}/oidc/me`,
|
|
27
|
+
jwks_uri: `${this.options.serverUrl}/oidc/jwks`,
|
|
28
|
+
registration_endpoint: `${this.options.serverUrl}/oidc/reg`,
|
|
29
|
+
scopes_supported: [
|
|
30
|
+
'openid',
|
|
31
|
+
'profile',
|
|
32
|
+
'email',
|
|
33
|
+
'offline_access'
|
|
34
|
+
],
|
|
35
|
+
response_types_supported: [
|
|
36
|
+
'code',
|
|
37
|
+
'id_token',
|
|
38
|
+
'code id_token'
|
|
39
|
+
],
|
|
40
|
+
grant_types_supported: [
|
|
41
|
+
'authorization_code',
|
|
42
|
+
'refresh_token'
|
|
43
|
+
],
|
|
44
|
+
subject_types_supported: [
|
|
45
|
+
'public'
|
|
46
|
+
],
|
|
47
|
+
token_endpoint_auth_methods_supported: [
|
|
48
|
+
'client_secret_basic',
|
|
49
|
+
'client_secret_post',
|
|
50
|
+
'none'
|
|
51
|
+
],
|
|
52
|
+
code_challenge_methods_supported: [
|
|
53
|
+
'plain',
|
|
54
|
+
'S256'
|
|
55
|
+
],
|
|
56
|
+
claims_supported: [
|
|
57
|
+
'sub',
|
|
58
|
+
'name',
|
|
59
|
+
'preferred_username',
|
|
60
|
+
'email',
|
|
61
|
+
'email_verified'
|
|
62
|
+
],
|
|
63
|
+
introspection_endpoint: `${this.options.serverUrl}/oidc/introspect`,
|
|
64
|
+
revocation_endpoint: `${this.options.serverUrl}/oidc/revoke`,
|
|
65
|
+
service_documentation: `${this.options.serverUrl}/docs`,
|
|
66
|
+
end_session_endpoint: `${this.options.serverUrl}/oidc/session/end`,
|
|
67
|
+
check_session_iframe: `${this.options.serverUrl}/oidc/session/check`,
|
|
68
|
+
'x-mcp-capabilities': [
|
|
69
|
+
'tools',
|
|
70
|
+
'resources',
|
|
71
|
+
'prompts',
|
|
72
|
+
'server-side-events',
|
|
73
|
+
'streamable-http'
|
|
74
|
+
],
|
|
75
|
+
'x-mcp-version': '2024-11-05',
|
|
76
|
+
'x-api-version': '1.0'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
getOpenIdConfiguration() {
|
|
80
|
+
return {
|
|
81
|
+
url: `/oidc/.well-known/openid_configuration`,
|
|
82
|
+
statusCode: 302
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
getOAuthAuthorizationServer() {
|
|
86
|
+
return this.getAuthorizationServerMetadata();
|
|
87
|
+
}
|
|
88
|
+
getWebFinger() {
|
|
89
|
+
return {
|
|
90
|
+
subject: this.options.serverUrl,
|
|
91
|
+
links: [
|
|
92
|
+
{
|
|
93
|
+
rel: 'http://openid.net/specs/connect/1.0/issuer',
|
|
94
|
+
href: this.options.serverUrl
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
exports.WellKnownController = WellKnownController;
|
|
101
|
+
__decorate([
|
|
102
|
+
(0, common_1.Get)('.well-known/oauth-authorization-server'),
|
|
103
|
+
__metadata("design:type", Function),
|
|
104
|
+
__metadata("design:paramtypes", []),
|
|
105
|
+
__metadata("design:returntype", void 0)
|
|
106
|
+
], WellKnownController.prototype, "getAuthorizationServerMetadata", null);
|
|
107
|
+
__decorate([
|
|
108
|
+
(0, common_1.Get)('.well-known/openid_configuration'),
|
|
109
|
+
(0, common_1.Redirect)(),
|
|
110
|
+
__metadata("design:type", Function),
|
|
111
|
+
__metadata("design:paramtypes", []),
|
|
112
|
+
__metadata("design:returntype", void 0)
|
|
113
|
+
], WellKnownController.prototype, "getOpenIdConfiguration", null);
|
|
114
|
+
__decorate([
|
|
115
|
+
(0, common_1.Get)('.well-known/oauth_authorization_server'),
|
|
116
|
+
__metadata("design:type", Function),
|
|
117
|
+
__metadata("design:paramtypes", []),
|
|
118
|
+
__metadata("design:returntype", void 0)
|
|
119
|
+
], WellKnownController.prototype, "getOAuthAuthorizationServer", null);
|
|
120
|
+
__decorate([
|
|
121
|
+
(0, common_1.Get)('.well-known/webfinger'),
|
|
122
|
+
__metadata("design:type", Function),
|
|
123
|
+
__metadata("design:paramtypes", []),
|
|
124
|
+
__metadata("design:returntype", void 0)
|
|
125
|
+
], WellKnownController.prototype, "getWebFinger", null);
|
|
126
|
+
exports.WellKnownController = WellKnownController = __decorate([
|
|
127
|
+
(0, common_1.Controller)(),
|
|
128
|
+
__param(0, (0, common_1.Inject)('OAUTH_MODULE_OPTIONS')),
|
|
129
|
+
__metadata("design:paramtypes", [Object])
|
|
130
|
+
], WellKnownController);
|
|
131
|
+
//# sourceMappingURL=well-known.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"well-known.controller.js","sourceRoot":"","sources":["../../../src/authz/controllers/well-known.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmE;AAU5D,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,YACmD,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAC3E,CAAC;IASJ,8BAA8B;QAC5B,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC9B,sBAAsB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,YAAY;YAC7D,cAAc,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,aAAa;YACtD,iBAAiB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,UAAU;YACtD,QAAQ,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,YAAY;YAC/C,qBAAqB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,WAAW;YAC3D,gBAAgB,EAAE;gBAChB,QAAQ;gBACR,SAAS;gBACT,OAAO;gBACP,gBAAgB;aACjB;YACD,wBAAwB,EAAE;gBACxB,MAAM;gBACN,UAAU;gBACV,eAAe;aAChB;YACD,qBAAqB,EAAE;gBACrB,oBAAoB;gBACpB,eAAe;aAChB;YACD,uBAAuB,EAAE;gBACvB,QAAQ;aACT;YACD,qCAAqC,EAAE;gBACrC,qBAAqB;gBACrB,oBAAoB;gBACpB,MAAM;aACP;YACD,gCAAgC,EAAE;gBAChC,OAAO;gBACP,MAAM;aACP;YACD,gBAAgB,EAAE;gBAChB,KAAK;gBACL,MAAM;gBACN,oBAAoB;gBACpB,OAAO;gBACP,gBAAgB;aACjB;YAED,sBAAsB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,kBAAkB;YACnE,mBAAmB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,cAAc;YAC5D,qBAAqB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,OAAO;YAGvD,oBAAoB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,mBAAmB;YAClE,oBAAoB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,qBAAqB;YAGpE,oBAAoB,EAAE;gBACpB,OAAO;gBACP,WAAW;gBACX,SAAS;gBACT,oBAAoB;gBACpB,iBAAiB;aAClB;YACD,eAAe,EAAE,YAAY;YAC7B,eAAe,EAAE,KAAK;SACvB,CAAC;IACJ,CAAC;IAUD,sBAAsB;QACpB,OAAO;YACL,GAAG,EAAE,wCAAwC;YAC7C,UAAU,EAAE,GAAG;SAChB,CAAC;IACJ,CAAC;IAMD,2BAA2B;QACzB,OAAO,IAAI,CAAC,8BAA8B,EAAE,CAAC;IAC/C,CAAC;IAOD,YAAY;QACV,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC/B,KAAK,EAAE;gBACL;oBACE,GAAG,EAAE,4CAA4C;oBACjD,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;iBAC7B;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AAnHY,kDAAmB;AAY9B;IADC,IAAA,YAAG,EAAC,wCAAwC,CAAC;;;;yEA+D7C;AAUD;IAFC,IAAA,YAAG,EAAC,kCAAkC,CAAC;IACvC,IAAA,iBAAQ,GAAE;;;;iEAMV;AAMD;IADC,IAAA,YAAG,EAAC,wCAAwC,CAAC;;;;sEAG7C;AAOD;IADC,IAAA,YAAG,EAAC,uBAAuB,CAAC;;;;uDAW5B;8BAlHU,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;IAGR,WAAA,IAAA,eAAM,EAAC,sBAAsB,CAAC,CAAA;;GAFtB,mBAAmB,CAmH/B","sourcesContent":["import { Controller, Get, Inject, Redirect } from '@nestjs/common';\nimport { OAuthModuleOptions } from '../providers/oauth-provider.interface';\n\n/**\n * Root Well-Known Endpoints Controller\n * \n * Handles OAuth 2.0 and OIDC well-known endpoints at the root level\n * as required by RFC 8414 and RFC 8623\n */\n@Controller()\nexport class WellKnownController {\n constructor(\n @Inject('OAUTH_MODULE_OPTIONS') private readonly options: OAuthModuleOptions,\n ) {}\n\n /**\n * OAuth Authorization Server Metadata Endpoint (RFC 8414)\n * MUST be at /.well-known/oauth-authorization-server (not prefixed)\n * \n * This returns the same metadata as the OIDC provider but at the correct root path\n */\n @Get('.well-known/oauth-authorization-server')\n getAuthorizationServerMetadata() {\n return {\n issuer: this.options.serverUrl,\n authorization_endpoint: `${this.options.serverUrl}/oidc/auth`,\n token_endpoint: `${this.options.serverUrl}/oidc/token`,\n userinfo_endpoint: `${this.options.serverUrl}/oidc/me`,\n jwks_uri: `${this.options.serverUrl}/oidc/jwks`,\n registration_endpoint: `${this.options.serverUrl}/oidc/reg`,\n scopes_supported: [\n 'openid',\n 'profile',\n 'email',\n 'offline_access'\n ],\n response_types_supported: [\n 'code',\n 'id_token',\n 'code id_token'\n ],\n grant_types_supported: [\n 'authorization_code',\n 'refresh_token'\n ],\n subject_types_supported: [\n 'public'\n ],\n token_endpoint_auth_methods_supported: [\n 'client_secret_basic',\n 'client_secret_post',\n 'none'\n ],\n code_challenge_methods_supported: [\n 'plain',\n 'S256'\n ],\n claims_supported: [\n 'sub',\n 'name',\n 'preferred_username',\n 'email',\n 'email_verified'\n ],\n // Additional OAuth 2.0 Authorization Server metadata\n introspection_endpoint: `${this.options.serverUrl}/oidc/introspect`,\n revocation_endpoint: `${this.options.serverUrl}/oidc/revoke`,\n service_documentation: `${this.options.serverUrl}/docs`,\n \n // OIDC-specific metadata\n end_session_endpoint: `${this.options.serverUrl}/oidc/session/end`,\n check_session_iframe: `${this.options.serverUrl}/oidc/session/check`,\n \n // MCP-specific extensions (non-standard but useful)\n 'x-mcp-capabilities': [\n 'tools',\n 'resources', \n 'prompts',\n 'server-side-events',\n 'streamable-http'\n ],\n 'x-mcp-version': '2024-11-05',\n 'x-api-version': '1.0'\n };\n }\n\n /**\n * OpenID Connect Discovery Document (RFC 8414 Section 3)\n * Should be at /.well-known/openid_configuration \n * \n * We redirect to the OIDC provider's discovery endpoint\n */\n @Get('.well-known/openid_configuration')\n @Redirect()\n getOpenIdConfiguration() {\n return {\n url: `/oidc/.well-known/openid_configuration`,\n statusCode: 302\n };\n }\n\n /**\n * Alternative OAuth Discovery endpoint (some implementations check here)\n */\n @Get('.well-known/oauth_authorization_server')\n getOAuthAuthorizationServer() {\n return this.getAuthorizationServerMetadata();\n }\n\n /**\n * WebFinger endpoint for OpenID Connect Discovery (RFC 7033)\n * Some clients use this for discovery\n */\n @Get('.well-known/webfinger')\n getWebFinger() {\n return {\n subject: this.options.serverUrl,\n links: [\n {\n rel: 'http://openid.net/specs/connect/1.0/issuer',\n href: this.options.serverUrl\n }\n ]\n };\n }\n}"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-provider-example.d.ts","sourceRoot":"","sources":["../../../src/authz/examples/oidc-provider-example.ts"],"names":[],"mappings":"AASA,qBAsDa,yBAAyB;CAAG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AppModuleWithOidcProvider = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const oidc_provider_module_1 = require("../oidc-provider.module");
|
|
12
|
+
const github_provider_1 = require("../providers/github.provider");
|
|
13
|
+
let AppModuleWithOidcProvider = class AppModuleWithOidcProvider {
|
|
14
|
+
};
|
|
15
|
+
exports.AppModuleWithOidcProvider = AppModuleWithOidcProvider;
|
|
16
|
+
exports.AppModuleWithOidcProvider = AppModuleWithOidcProvider = __decorate([
|
|
17
|
+
(0, common_1.Module)({
|
|
18
|
+
imports: [
|
|
19
|
+
oidc_provider_module_1.McpOidcProviderModule.forRoot({
|
|
20
|
+
clientId: process.env.GITHUB_CLIENT_ID,
|
|
21
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
22
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
23
|
+
serverUrl: process.env.SERVER_URL || 'https://localhost:3000',
|
|
24
|
+
resource: process.env.RESOURCE_URL || 'https://localhost:3000/mcp',
|
|
25
|
+
jwtIssuer: process.env.JWT_ISSUER || 'https://localhost:3000',
|
|
26
|
+
jwtAudience: process.env.JWT_AUDIENCE || 'mcp-client',
|
|
27
|
+
jwtAccessTokenExpiresIn: '1h',
|
|
28
|
+
jwtRefreshTokenExpiresIn: '30d',
|
|
29
|
+
cookieMaxAge: 24 * 60 * 60 * 1000,
|
|
30
|
+
cookieSecure: process.env.NODE_ENV === 'production',
|
|
31
|
+
oauthSessionExpiresIn: 10 * 60 * 1000,
|
|
32
|
+
authCodeExpiresIn: 10 * 60 * 1000,
|
|
33
|
+
provider: github_provider_1.GitHubOAuthProvider,
|
|
34
|
+
apiPrefix: 'oidc',
|
|
35
|
+
endpoints: {
|
|
36
|
+
wellKnownAuthorizationServerMetadata: '/.well-known/oauth-authorization-server',
|
|
37
|
+
register: '/reg',
|
|
38
|
+
authorize: '/auth',
|
|
39
|
+
callback: '/callback',
|
|
40
|
+
token: '/token',
|
|
41
|
+
validate: '/introspect',
|
|
42
|
+
revoke: '/revoke',
|
|
43
|
+
},
|
|
44
|
+
storeConfiguration: {
|
|
45
|
+
type: 'memory',
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
], AppModuleWithOidcProvider);
|
|
51
|
+
//# sourceMappingURL=oidc-provider-example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-provider-example.js","sourceRoot":"","sources":["../../../src/authz/examples/oidc-provider-example.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kEAAgE;AAChE,kEAAmE;AA6D5D,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;CAAG,CAAA;AAA5B,8DAAyB;oCAAzB,yBAAyB;IAtDrC,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YAEP,4CAAqB,CAAC,OAAO,CAAC;gBAE5B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAiB;gBACvC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAqB;gBAG/C,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAW;gBAGlC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,wBAAwB;gBAC7D,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,4BAA4B;gBAGlE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,wBAAwB;gBAC7D,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY;gBAGrD,uBAAuB,EAAE,IAAI;gBAC7B,wBAAwB,EAAE,KAAK;gBAG/B,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;gBACjC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;gBACnD,qBAAqB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;gBACrC,iBAAiB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;gBAGjC,QAAQ,EAAE,qCAAmB;gBAG7B,SAAS,EAAE,MAAM;gBAGjB,SAAS,EAAE;oBACT,oCAAoC,EAClC,yCAAyC;oBAC3C,QAAQ,EAAE,MAAM;oBAChB,SAAS,EAAE,OAAO;oBAClB,QAAQ,EAAE,WAAW;oBACrB,KAAK,EAAE,QAAQ;oBACf,QAAQ,EAAE,aAAa;oBACvB,MAAM,EAAE,SAAS;iBAClB;gBAGD,kBAAkB,EAAE;oBAClB,IAAI,EAAE,QAAQ;iBACf;aACF,CAAC;SACH;KACF,CAAC;GACW,yBAAyB,CAAG","sourcesContent":["import { Module } from '@nestjs/common';\nimport { McpOidcProviderModule } from '../oidc-provider.module';\nimport { GitHubOAuthProvider } from '../providers/github.provider';\n\n/**\n * Example configuration for using the new OIDC Provider\n * instead of the legacy custom OAuth implementation\n */\n\n@Module({\n imports: [\n // Replace McpAuthModule.forRoot with McpOidcProviderModule.forRoot\n McpOidcProviderModule.forRoot({\n // GitHub OAuth App credentials\n clientId: process.env.GITHUB_CLIENT_ID!,\n clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n\n // JWT signing secret (should be at least 32 characters)\n jwtSecret: process.env.JWT_SECRET!,\n\n // Server configuration\n serverUrl: process.env.SERVER_URL || 'https://localhost:3000',\n resource: process.env.RESOURCE_URL || 'https://localhost:3000/mcp',\n\n // OIDC Provider configuration\n jwtIssuer: process.env.JWT_ISSUER || 'https://localhost:3000',\n jwtAudience: process.env.JWT_AUDIENCE || 'mcp-client',\n\n // Token expiration settings\n jwtAccessTokenExpiresIn: '1h',\n jwtRefreshTokenExpiresIn: '30d',\n\n // Cookie and session settings\n cookieMaxAge: 24 * 60 * 60 * 1000, // 24 hours\n cookieSecure: process.env.NODE_ENV === 'production',\n oauthSessionExpiresIn: 10 * 60 * 1000, // 10 minutes\n authCodeExpiresIn: 10 * 60 * 1000, // 10 minutes\n\n // GitHub provider configuration\n provider: GitHubOAuthProvider,\n\n // Optional: API prefix for OIDC endpoints (defaults to 'oidc')\n apiPrefix: 'oidc',\n\n // Optional: Custom endpoint configuration\n endpoints: {\n wellKnownAuthorizationServerMetadata:\n '/.well-known/oauth-authorization-server',\n register: '/reg',\n authorize: '/auth',\n callback: '/callback',\n token: '/token',\n validate: '/introspect',\n revoke: '/revoke',\n },\n\n // Optional: Storage configuration (defaults to memory)\n storeConfiguration: {\n type: 'memory', // or 'typeorm' for persistent storage\n },\n }),\n ],\n})\nexport class AppModuleWithOidcProvider {}\n\n/**\n * Environment variables required:\n *\n * GITHUB_CLIENT_ID=your_github_oauth_app_client_id\n * GITHUB_CLIENT_SECRET=your_github_oauth_app_client_secret\n * JWT_SECRET=your_jwt_signing_secret_at_least_32_characters_long\n * SERVER_URL=https://your-server.com (optional, defaults to localhost:3000)\n * RESOURCE_URL=https://your-server.com/mcp (optional)\n * JWT_ISSUER=https://your-server.com (optional)\n * JWT_AUDIENCE=mcp-client (optional)\n * NODE_ENV=production (for secure cookies)\n */\n\n/**\n * OIDC Endpoints provided by this configuration:\n *\n * Well-known metadata:\n * - GET /.well-known/oauth-authorization-server\n * - GET /.well-known/oauth-protected-resource\n * - GET /oidc/.well-known/openid_configuration\n *\n * OAuth 2.0 / OIDC endpoints:\n * - GET /oidc/auth - Authorization endpoint\n * - POST /oidc/token - Token endpoint\n * - GET /oidc/me - UserInfo endpoint\n * - GET /oidc/jwks - JSON Web Key Set\n * - POST /oidc/reg - Dynamic client registration\n * - POST /oidc/introspect - Token introspection\n * - POST /oidc/revoke - Token revocation\n *\n * Custom interaction endpoints:\n * - GET /oidc/interaction/:uid - GitHub authentication initiation\n * - GET /oidc/callback - GitHub OAuth callback\n */\n\n/**\n * Migration from legacy McpAuthModule:\n *\n * 1. Replace imports:\n * - Change: import { McpAuthModule } from '@rekog/mcp-nest/authz'\n * - To: import { McpOidcProviderModule } from '@rekog/mcp-nest/authz'\n *\n * 2. Update module registration:\n * - Change: McpAuthModule.forRoot(...)\n * - To: McpOidcProviderModule.forRoot(...)\n *\n * 3. Update endpoints (if you have hardcoded URLs):\n * - Authorization: /authorize → /oidc/auth\n * - Token: /token → /oidc/token\n * - Callback: /callback → /oidc/callback\n * - Well-known: /.well-known/oauth-authorization-server (unchanged)\n *\n * 4. Benefits of the new implementation:\n * - Standards-compliant OIDC provider\n * - Better security with proper OIDC flows\n * - Automatic JWKS endpoint\n * - Built-in token introspection\n * - Support for ID tokens\n * - Better integration with OIDC-compliant clients\n */\n"]}
|
|
@@ -23,7 +23,7 @@ let McpAuthJwtGuard = class McpAuthJwtGuard {
|
|
|
23
23
|
throw new common_1.UnauthorizedException('Access token required');
|
|
24
24
|
}
|
|
25
25
|
const payload = this.jwtTokenService.validateToken(token);
|
|
26
|
-
if (!payload
|
|
26
|
+
if (!payload) {
|
|
27
27
|
throw new common_1.UnauthorizedException('Invalid or expired access token');
|
|
28
28
|
}
|
|
29
29
|
request.user = payload;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AAExB,qEAA4E;AAOrE,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAEjE,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAwB,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC,uBAAuB,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAE1D,IAAI,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AAExB,qEAA4E;AAOrE,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,YAA6B,eAAgC;QAAhC,oBAAe,GAAf,eAAe,CAAiB;IAAG,CAAC;IAEjE,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAwB,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC,uBAAuB,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAE1D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,8BAAqB,CAAC,iCAAiC,CAAC,CAAC;QACrE,CAAC;QAED,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,sBAAsB,CAAC,OAAgB;QAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,CAAC;CACF,CAAA;AA9BY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEmC,mCAAe;GADlD,eAAe,CA8B3B","sourcesContent":["import {\n Injectable,\n CanActivate,\n ExecutionContext,\n UnauthorizedException,\n} from '@nestjs/common';\nimport { Request } from 'express';\nimport { JwtPayload, JwtTokenService } from '../services/jwt-token.service';\n\nexport interface AuthenticatedRequest extends Request {\n user: JwtPayload;\n}\n\n@Injectable()\nexport class McpAuthJwtGuard implements CanActivate {\n constructor(private readonly jwtTokenService: JwtTokenService) {}\n\n canActivate(context: ExecutionContext): boolean {\n const request = context.switchToHttp().getRequest<AuthenticatedRequest>();\n const token = this.extractTokenFromHeader(request);\n\n if (!token) {\n throw new UnauthorizedException('Access token required');\n }\n\n const payload = this.jwtTokenService.validateToken(token);\n\n if (!payload) {\n throw new UnauthorizedException('Invalid or expired access token');\n }\n\n request.user = payload;\n return true;\n }\n\n private extractTokenFromHeader(request: Request): string | undefined {\n const authHeader = request.headers.authorization;\n if (!authHeader) {\n return undefined;\n }\n\n const [type, token] = authHeader.split(' ');\n return type === 'Bearer' ? token : undefined;\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import { AuthenticatedRequest } from './guards/jwt-auth.guard';
|
|
|
4
4
|
import { OAuthEndpointConfiguration, OAuthModuleOptions, OAuthUserProfile } from './providers/oauth-provider.interface';
|
|
5
5
|
import { ClientService } from './services/client.service';
|
|
6
6
|
import { JwtTokenService, TokenPair } from './services/jwt-token.service';
|
|
7
|
-
import {
|
|
7
|
+
import { IOAuthStore } from './stores/oauth-store.interface';
|
|
8
8
|
interface OAuthCallbackRequest extends ExpressRequest {
|
|
9
9
|
user?: {
|
|
10
10
|
profile: OAuthUserProfile;
|
|
@@ -30,23 +30,32 @@ export declare function createMcpOAuthController(endpoints?: OAuthEndpointConfig
|
|
|
30
30
|
response_modes_supported: string[];
|
|
31
31
|
grant_types_supported: string[];
|
|
32
32
|
token_endpoint_auth_methods_supported: string[];
|
|
33
|
+
scopes_supported: string[];
|
|
33
34
|
revocation_endpoint: string;
|
|
34
35
|
code_challenge_methods_supported: string[];
|
|
35
36
|
};
|
|
36
|
-
registerClient(registrationDto:
|
|
37
|
+
registerClient(registrationDto: any): Promise<import("./stores/oauth-store.interface").OAuthClient>;
|
|
37
38
|
authorize(query: any, req: any, res: Response, next: NextFunction): Promise<void>;
|
|
38
39
|
handleProviderCallback(req: OAuthCallbackRequest, res: Response, next: NextFunction): void;
|
|
39
40
|
processAuthenticationSuccess(req: OAuthCallbackRequest, res: Response): Promise<void>;
|
|
40
|
-
exchangeToken(body: any): Promise<TokenPair>;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
valid: boolean;
|
|
45
|
-
user_id: string;
|
|
46
|
-
client_id: string | undefined;
|
|
47
|
-
scope: string | undefined;
|
|
48
|
-
expires_at: number;
|
|
41
|
+
exchangeToken(body: any, req: any): Promise<TokenPair>;
|
|
42
|
+
extractClientCredentials(req: any, body: any): {
|
|
43
|
+
client_id: string;
|
|
44
|
+
client_secret?: string;
|
|
49
45
|
};
|
|
46
|
+
validateClientAuthentication(client: any, clientCredentials: {
|
|
47
|
+
client_id: string;
|
|
48
|
+
client_secret?: string;
|
|
49
|
+
}): void;
|
|
50
|
+
handleAuthorizationCodeGrant(code: string, code_verifier: string, _redirect_uri: string, clientCredentials: {
|
|
51
|
+
client_id: string;
|
|
52
|
+
client_secret?: string;
|
|
53
|
+
}): Promise<TokenPair>;
|
|
54
|
+
handleRefreshTokenGrant(refresh_token: string, clientCredentials: {
|
|
55
|
+
client_id: string;
|
|
56
|
+
client_secret?: string;
|
|
57
|
+
}): Promise<TokenPair>;
|
|
58
|
+
validateToken(req: AuthenticatedRequest): any;
|
|
50
59
|
validatePKCE(code_verifier: string, code_challenge: string, method: string): boolean;
|
|
51
60
|
};
|
|
52
61
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-oauth.controller.d.ts","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,MAAM,EAOP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAmB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,
|
|
1
|
+
{"version":3,"file":"mcp-oauth.controller.d.ts","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,MAAM,EAOP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAmB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAEL,WAAW,EACZ,MAAM,gCAAgC,CAAC;AAGxC,UAAU,oBAAqB,SAAQ,cAAc;IACnD,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,gBAAgB,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,0BAA+B;kBASG,kBAAkB,SACpB,WAAW,mBACxB,eAAe,iBACjB,aAAa;;4BAPnB,MAAM;+BACH,OAAO;0BACZ,kBAAkB;wBAGK,WAAW;kCACxB,eAAe;gCACjB,aAAa;;;;;;;;;;;;;;wCAsCO,GAAG;yBAM/B,GAAG,OAEd,GAAG,OACI,QAAQ,QACN,YAAY;oCA2Ed,oBAAoB,OACpB,QAAQ,QACN,YAAY;0CA2BrB,oBAAoB,OACpB,QAAQ;4BAyEC,GAAG,OACL,GAAG,GACd,OAAO,CAAC,SAAS,CAAC;sCAuCd,GAAG,QACF,GAAG,GACR;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE;6CA4BtC,GAAG,qBACQ;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,IAAI;2CAqCC,MAAM,iBACG,MAAM,iBACN,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;+CAyEJ,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;2BA0CK,oBAAoB;oCAiB7B,MAAM,kBACL,MAAM,UACd,MAAM,GACb,OAAO;;EAcb"}
|
|
@@ -50,6 +50,7 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
50
50
|
'client_secret_post',
|
|
51
51
|
'none',
|
|
52
52
|
],
|
|
53
|
+
scopes_supported: ['offline_access'],
|
|
53
54
|
revocation_endpoint: (0, normalize_endpoint_1.normalizeEndpoint)(`${this.serverUrl}/${endpoints?.revoke}`),
|
|
54
55
|
code_challenge_methods_supported: ['plain', 'S256'],
|
|
55
56
|
};
|
|
@@ -58,7 +59,7 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
58
59
|
return await this.clientService.registerClient(registrationDto);
|
|
59
60
|
}
|
|
60
61
|
async authorize(query, req, res, next) {
|
|
61
|
-
const { response_type, client_id, redirect_uri, code_challenge, code_challenge_method, state, } = query;
|
|
62
|
+
const { response_type, client_id, redirect_uri, code_challenge, code_challenge_method, state, scope, } = query;
|
|
62
63
|
const resource = this.options.resource;
|
|
63
64
|
if (response_type !== 'code') {
|
|
64
65
|
throw new common_1.BadRequestException('Only response_type=code is supported');
|
|
@@ -84,7 +85,7 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
84
85
|
codeChallenge: code_challenge,
|
|
85
86
|
codeChallengeMethod: code_challenge_method || 'plain',
|
|
86
87
|
oauthState: state,
|
|
87
|
-
scope:
|
|
88
|
+
scope: scope,
|
|
88
89
|
resource,
|
|
89
90
|
expiresAt: Date.now() + this.options.oauthSessionExpiresIn,
|
|
90
91
|
};
|
|
@@ -167,22 +168,71 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
167
168
|
await this.store.removeOAuthSession(sessionId);
|
|
168
169
|
res.redirect(redirectUrl.toString());
|
|
169
170
|
}
|
|
170
|
-
async exchangeToken(body) {
|
|
171
|
-
const { grant_type, code, code_verifier, redirect_uri, client_id, refresh_token, } = body;
|
|
171
|
+
async exchangeToken(body, req) {
|
|
172
|
+
const { grant_type, code, code_verifier, redirect_uri, client_id, client_secret, refresh_token, } = body;
|
|
172
173
|
if (grant_type === 'authorization_code') {
|
|
173
|
-
|
|
174
|
+
const clientCredentials = this.extractClientCredentials(req, body);
|
|
175
|
+
return this.handleAuthorizationCodeGrant(code, code_verifier, redirect_uri, clientCredentials);
|
|
174
176
|
}
|
|
175
177
|
else if (grant_type === 'refresh_token') {
|
|
176
|
-
|
|
178
|
+
let clientCredentials;
|
|
179
|
+
try {
|
|
180
|
+
clientCredentials = this.extractClientCredentials(req, body);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
clientCredentials = { client_id: '' };
|
|
184
|
+
}
|
|
185
|
+
return this.handleRefreshTokenGrant(refresh_token, clientCredentials);
|
|
177
186
|
}
|
|
178
187
|
else {
|
|
179
188
|
throw new common_1.BadRequestException('Unsupported grant_type');
|
|
180
189
|
}
|
|
181
190
|
}
|
|
182
|
-
|
|
191
|
+
extractClientCredentials(req, body) {
|
|
192
|
+
const authHeader = req.headers?.authorization;
|
|
193
|
+
if (authHeader && authHeader.startsWith('Basic ')) {
|
|
194
|
+
const credentials = Buffer.from(authHeader.slice(6), 'base64').toString('utf-8');
|
|
195
|
+
const [client_id, client_secret] = credentials.split(':', 2);
|
|
196
|
+
if (client_id) {
|
|
197
|
+
return { client_id, client_secret };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (body.client_id) {
|
|
201
|
+
return {
|
|
202
|
+
client_id: body.client_id,
|
|
203
|
+
client_secret: body.client_secret,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
throw new common_1.BadRequestException('Missing client credentials');
|
|
207
|
+
}
|
|
208
|
+
validateClientAuthentication(client, clientCredentials) {
|
|
209
|
+
if (!client) {
|
|
210
|
+
throw new common_1.BadRequestException('Invalid client_id');
|
|
211
|
+
}
|
|
212
|
+
const { token_endpoint_auth_method } = client;
|
|
213
|
+
switch (token_endpoint_auth_method) {
|
|
214
|
+
case 'client_secret_basic':
|
|
215
|
+
case 'client_secret_post':
|
|
216
|
+
if (!clientCredentials.client_secret) {
|
|
217
|
+
throw new common_1.BadRequestException('Client secret required for this authentication method');
|
|
218
|
+
}
|
|
219
|
+
if (client.client_secret !== clientCredentials.client_secret) {
|
|
220
|
+
throw new common_1.BadRequestException('Invalid client credentials');
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
case 'none':
|
|
224
|
+
if (clientCredentials.client_secret) {
|
|
225
|
+
throw new common_1.BadRequestException('Client secret not allowed for public clients');
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
throw new common_1.BadRequestException(`Unsupported authentication method: ${token_endpoint_auth_method}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async handleAuthorizationCodeGrant(code, code_verifier, _redirect_uri, clientCredentials) {
|
|
183
233
|
this.logger.debug('handleAuthorizationCodeGrant - Params:', {
|
|
184
234
|
code,
|
|
185
|
-
client_id,
|
|
235
|
+
client_id: clientCredentials.client_id,
|
|
186
236
|
});
|
|
187
237
|
const authCode = await this.store.getAuthCode(code);
|
|
188
238
|
if (!authCode) {
|
|
@@ -194,10 +244,12 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
194
244
|
this.logger.error('handleAuthorizationCodeGrant - Authorization code expired:', code);
|
|
195
245
|
throw new common_1.BadRequestException('Authorization code has expired');
|
|
196
246
|
}
|
|
197
|
-
if (authCode.client_id !== client_id) {
|
|
198
|
-
this.logger.error('handleAuthorizationCodeGrant - Client ID mismatch:', { expected: authCode.client_id, got: client_id });
|
|
247
|
+
if (authCode.client_id !== clientCredentials.client_id) {
|
|
248
|
+
this.logger.error('handleAuthorizationCodeGrant - Client ID mismatch:', { expected: authCode.client_id, got: clientCredentials.client_id });
|
|
199
249
|
throw new common_1.BadRequestException('Client ID mismatch');
|
|
200
250
|
}
|
|
251
|
+
const client = await this.clientService.getClient(clientCredentials.client_id);
|
|
252
|
+
this.validateClientAuthentication(client, clientCredentials);
|
|
201
253
|
if (authCode.code_challenge) {
|
|
202
254
|
const isValid = this.validatePKCE(code_verifier, authCode.code_challenge, authCode.code_challenge_method);
|
|
203
255
|
if (!isValid) {
|
|
@@ -209,12 +261,30 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
209
261
|
this.logger.error('handleAuthorizationCodeGrant - No resource associated with code');
|
|
210
262
|
throw new common_1.BadRequestException('Authorization code is not associated with a resource');
|
|
211
263
|
}
|
|
212
|
-
const tokens = this.jwtTokenService.generateTokenPair(authCode.user_id, client_id, authCode.scope, authCode.resource);
|
|
264
|
+
const tokens = this.jwtTokenService.generateTokenPair(authCode.user_id, clientCredentials.client_id, authCode.scope, authCode.resource);
|
|
213
265
|
await this.store.removeAuthCode(code);
|
|
214
266
|
this.logger.log('handleAuthorizationCodeGrant - Token pair generated for user:', authCode.user_id);
|
|
215
267
|
return tokens;
|
|
216
268
|
}
|
|
217
|
-
handleRefreshTokenGrant(refresh_token) {
|
|
269
|
+
async handleRefreshTokenGrant(refresh_token, clientCredentials) {
|
|
270
|
+
const payload = this.jwtTokenService.validateToken(refresh_token);
|
|
271
|
+
if (!payload || payload.type !== 'refresh') {
|
|
272
|
+
throw new common_1.BadRequestException('Invalid or expired refresh token');
|
|
273
|
+
}
|
|
274
|
+
const clientId = clientCredentials.client_id || payload.client_id;
|
|
275
|
+
if (!clientId) {
|
|
276
|
+
throw new common_1.BadRequestException('Unable to determine client_id');
|
|
277
|
+
}
|
|
278
|
+
const client = await this.clientService.getClient(clientId);
|
|
279
|
+
if (client?.token_endpoint_auth_method !== 'none') {
|
|
280
|
+
this.validateClientAuthentication(client, {
|
|
281
|
+
...clientCredentials,
|
|
282
|
+
client_id: clientId,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
if (payload.client_id !== clientId) {
|
|
286
|
+
throw new common_1.BadRequestException('Invalid refresh token or token does not belong to this client');
|
|
287
|
+
}
|
|
218
288
|
const newTokens = this.jwtTokenService.refreshAccessToken(refresh_token);
|
|
219
289
|
if (!newTokens) {
|
|
220
290
|
throw new common_1.BadRequestException('Failed to refresh token');
|
|
@@ -222,13 +292,16 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
222
292
|
return newTokens;
|
|
223
293
|
}
|
|
224
294
|
validateToken(req) {
|
|
225
|
-
|
|
295
|
+
const response = {
|
|
226
296
|
valid: true,
|
|
227
297
|
user_id: req.user.sub,
|
|
228
|
-
client_id: req.user.client_id,
|
|
229
|
-
scope: req.user.scope,
|
|
298
|
+
client_id: req.user.azp || req.user.client_id,
|
|
230
299
|
expires_at: req.user.exp * 1000,
|
|
231
300
|
};
|
|
301
|
+
if (req.user.scope) {
|
|
302
|
+
response.scope = req.user.scope;
|
|
303
|
+
}
|
|
304
|
+
return response;
|
|
232
305
|
}
|
|
233
306
|
validatePKCE(code_verifier, code_challenge, method) {
|
|
234
307
|
if (method === 'plain') {
|
|
@@ -244,7 +317,7 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
244
317
|
}
|
|
245
318
|
};
|
|
246
319
|
__decorate([
|
|
247
|
-
(0, common_1.Get)(endpoints.
|
|
320
|
+
(0, common_1.Get)(endpoints.wellKnownAuthorizationServerMetadata),
|
|
248
321
|
__metadata("design:type", Function),
|
|
249
322
|
__metadata("design:paramtypes", []),
|
|
250
323
|
__metadata("design:returntype", void 0)
|
|
@@ -278,8 +351,9 @@ function createMcpOAuthController(endpoints = {}) {
|
|
|
278
351
|
__decorate([
|
|
279
352
|
(0, common_1.Post)(endpoints.token),
|
|
280
353
|
__param(0, (0, common_1.Body)()),
|
|
354
|
+
__param(1, (0, common_1.Req)()),
|
|
281
355
|
__metadata("design:type", Function),
|
|
282
|
-
__metadata("design:paramtypes", [Object]),
|
|
356
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
283
357
|
__metadata("design:returntype", Promise)
|
|
284
358
|
], McpOAuthController.prototype, "exchangeToken", null);
|
|
285
359
|
__decorate([
|