auth-extensions.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. "use strict";
  2. /**
  3. * OAuth provider extensions for specialized authentication flows.
  4. *
  5. * This module provides ready-to-use OAuthClientProvider implementations
  6. * for common machine-to-machine authentication scenarios.
  7. */
  8. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  9. if (k2 === undefined) k2 = k;
  10. var desc = Object.getOwnPropertyDescriptor(m, k);
  11. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  12. desc = { enumerable: true, get: function() { return m[k]; } };
  13. }
  14. Object.defineProperty(o, k2, desc);
  15. }) : (function(o, m, k, k2) {
  16. if (k2 === undefined) k2 = k;
  17. o[k2] = m[k];
  18. }));
  19. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  20. Object.defineProperty(o, "default", { enumerable: true, value: v });
  21. }) : function(o, v) {
  22. o["default"] = v;
  23. });
  24. var __importStar = (this && this.__importStar) || function (mod) {
  25. if (mod && mod.__esModule) return mod;
  26. var result = {};
  27. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  28. __setModuleDefault(result, mod);
  29. return result;
  30. };
  31. Object.defineProperty(exports, "__esModule", { value: true });
  32. exports.StaticPrivateKeyJwtProvider = exports.PrivateKeyJwtProvider = exports.ClientCredentialsProvider = void 0;
  33. exports.createPrivateKeyJwtAuth = createPrivateKeyJwtAuth;
  34. /**
  35. * Helper to produce a private_key_jwt client authentication function.
  36. *
  37. * Usage:
  38. * const addClientAuth = createPrivateKeyJwtAuth({ issuer, subject, privateKey, alg, audience? });
  39. * // pass addClientAuth as provider.addClientAuthentication implementation
  40. */
  41. function createPrivateKeyJwtAuth(options) {
  42. return async (_headers, params, url, metadata) => {
  43. // Lazy import to avoid heavy dependency unless used
  44. if (typeof globalThis.crypto === 'undefined') {
  45. throw new TypeError('crypto is not available, please ensure you add have Web Crypto API support for older Node.js versions (see https://github.com/modelcontextprotocol/typescript-sdk#nodejs-web-crypto-globalthiscrypto-compatibility)');
  46. }
  47. const jose = await Promise.resolve().then(() => __importStar(require('jose')));
  48. const audience = String(options.audience ?? metadata?.issuer ?? url);
  49. const lifetimeSeconds = options.lifetimeSeconds ?? 300;
  50. const now = Math.floor(Date.now() / 1000);
  51. const jti = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
  52. const baseClaims = {
  53. iss: options.issuer,
  54. sub: options.subject,
  55. aud: audience,
  56. exp: now + lifetimeSeconds,
  57. iat: now,
  58. jti
  59. };
  60. const claims = options.claims ? { ...baseClaims, ...options.claims } : baseClaims;
  61. // Import key for the requested algorithm
  62. const alg = options.alg;
  63. let key;
  64. if (typeof options.privateKey === 'string') {
  65. if (alg.startsWith('RS') || alg.startsWith('ES') || alg.startsWith('PS')) {
  66. key = await jose.importPKCS8(options.privateKey, alg);
  67. }
  68. else if (alg.startsWith('HS')) {
  69. key = new TextEncoder().encode(options.privateKey);
  70. }
  71. else {
  72. throw new Error(`Unsupported algorithm ${alg}`);
  73. }
  74. }
  75. else if (options.privateKey instanceof Uint8Array) {
  76. if (alg.startsWith('HS')) {
  77. key = options.privateKey;
  78. }
  79. else {
  80. // Assume PKCS#8 DER in Uint8Array for asymmetric algorithms
  81. key = await jose.importPKCS8(new TextDecoder().decode(options.privateKey), alg);
  82. }
  83. }
  84. else {
  85. // Treat as JWK
  86. key = await jose.importJWK(options.privateKey, alg);
  87. }
  88. // Sign JWT
  89. const assertion = await new jose.SignJWT(claims)
  90. .setProtectedHeader({ alg, typ: 'JWT' })
  91. .setIssuer(options.issuer)
  92. .setSubject(options.subject)
  93. .setAudience(audience)
  94. .setIssuedAt(now)
  95. .setExpirationTime(now + lifetimeSeconds)
  96. .setJti(jti)
  97. .sign(key);
  98. params.set('client_assertion', assertion);
  99. params.set('client_assertion_type', 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer');
  100. };
  101. }
  102. /**
  103. * OAuth provider for client_credentials grant with client_secret_basic authentication.
  104. *
  105. * This provider is designed for machine-to-machine authentication where
  106. * the client authenticates using a client_id and client_secret.
  107. *
  108. * @example
  109. * const provider = new ClientCredentialsProvider({
  110. * clientId: 'my-client',
  111. * clientSecret: 'my-secret'
  112. * });
  113. *
  114. * const transport = new StreamableHTTPClientTransport(serverUrl, {
  115. * authProvider: provider
  116. * });
  117. */
  118. class ClientCredentialsProvider {
  119. constructor(options) {
  120. this._clientInfo = {
  121. client_id: options.clientId,
  122. client_secret: options.clientSecret
  123. };
  124. this._clientMetadata = {
  125. client_name: options.clientName ?? 'client-credentials-client',
  126. redirect_uris: [],
  127. grant_types: ['client_credentials'],
  128. token_endpoint_auth_method: 'client_secret_basic',
  129. scope: options.scope
  130. };
  131. }
  132. get redirectUrl() {
  133. return undefined;
  134. }
  135. get clientMetadata() {
  136. return this._clientMetadata;
  137. }
  138. clientInformation() {
  139. return this._clientInfo;
  140. }
  141. saveClientInformation(info) {
  142. this._clientInfo = info;
  143. }
  144. tokens() {
  145. return this._tokens;
  146. }
  147. saveTokens(tokens) {
  148. this._tokens = tokens;
  149. }
  150. redirectToAuthorization() {
  151. throw new Error('redirectToAuthorization is not used for client_credentials flow');
  152. }
  153. saveCodeVerifier() {
  154. // Not used for client_credentials
  155. }
  156. codeVerifier() {
  157. throw new Error('codeVerifier is not used for client_credentials flow');
  158. }
  159. prepareTokenRequest(scope) {
  160. const params = new URLSearchParams({ grant_type: 'client_credentials' });
  161. if (scope)
  162. params.set('scope', scope);
  163. return params;
  164. }
  165. }
  166. exports.ClientCredentialsProvider = ClientCredentialsProvider;
  167. /**
  168. * OAuth provider for client_credentials grant with private_key_jwt authentication.
  169. *
  170. * This provider is designed for machine-to-machine authentication where
  171. * the client authenticates using a signed JWT assertion (RFC 7523 Section 2.2).
  172. *
  173. * @example
  174. * const provider = new PrivateKeyJwtProvider({
  175. * clientId: 'my-client',
  176. * privateKey: pemEncodedPrivateKey,
  177. * algorithm: 'RS256'
  178. * });
  179. *
  180. * const transport = new StreamableHTTPClientTransport(serverUrl, {
  181. * authProvider: provider
  182. * });
  183. */
  184. class PrivateKeyJwtProvider {
  185. constructor(options) {
  186. this._clientInfo = {
  187. client_id: options.clientId
  188. };
  189. this._clientMetadata = {
  190. client_name: options.clientName ?? 'private-key-jwt-client',
  191. redirect_uris: [],
  192. grant_types: ['client_credentials'],
  193. token_endpoint_auth_method: 'private_key_jwt',
  194. scope: options.scope
  195. };
  196. this.addClientAuthentication = createPrivateKeyJwtAuth({
  197. issuer: options.clientId,
  198. subject: options.clientId,
  199. privateKey: options.privateKey,
  200. alg: options.algorithm,
  201. lifetimeSeconds: options.jwtLifetimeSeconds
  202. });
  203. }
  204. get redirectUrl() {
  205. return undefined;
  206. }
  207. get clientMetadata() {
  208. return this._clientMetadata;
  209. }
  210. clientInformation() {
  211. return this._clientInfo;
  212. }
  213. saveClientInformation(info) {
  214. this._clientInfo = info;
  215. }
  216. tokens() {
  217. return this._tokens;
  218. }
  219. saveTokens(tokens) {
  220. this._tokens = tokens;
  221. }
  222. redirectToAuthorization() {
  223. throw new Error('redirectToAuthorization is not used for client_credentials flow');
  224. }
  225. saveCodeVerifier() {
  226. // Not used for client_credentials
  227. }
  228. codeVerifier() {
  229. throw new Error('codeVerifier is not used for client_credentials flow');
  230. }
  231. prepareTokenRequest(scope) {
  232. const params = new URLSearchParams({ grant_type: 'client_credentials' });
  233. if (scope)
  234. params.set('scope', scope);
  235. return params;
  236. }
  237. }
  238. exports.PrivateKeyJwtProvider = PrivateKeyJwtProvider;
  239. /**
  240. * OAuth provider for client_credentials grant with a static private_key_jwt assertion.
  241. *
  242. * This provider mirrors {@link PrivateKeyJwtProvider} but instead of constructing and
  243. * signing a JWT on each request, it accepts a pre-built JWT assertion string and
  244. * uses it directly for authentication.
  245. */
  246. class StaticPrivateKeyJwtProvider {
  247. constructor(options) {
  248. this._clientInfo = {
  249. client_id: options.clientId
  250. };
  251. this._clientMetadata = {
  252. client_name: options.clientName ?? 'static-private-key-jwt-client',
  253. redirect_uris: [],
  254. grant_types: ['client_credentials'],
  255. token_endpoint_auth_method: 'private_key_jwt',
  256. scope: options.scope
  257. };
  258. const assertion = options.jwtBearerAssertion;
  259. this.addClientAuthentication = async (_headers, params) => {
  260. params.set('client_assertion', assertion);
  261. params.set('client_assertion_type', 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer');
  262. };
  263. }
  264. get redirectUrl() {
  265. return undefined;
  266. }
  267. get clientMetadata() {
  268. return this._clientMetadata;
  269. }
  270. clientInformation() {
  271. return this._clientInfo;
  272. }
  273. saveClientInformation(info) {
  274. this._clientInfo = info;
  275. }
  276. tokens() {
  277. return this._tokens;
  278. }
  279. saveTokens(tokens) {
  280. this._tokens = tokens;
  281. }
  282. redirectToAuthorization() {
  283. throw new Error('redirectToAuthorization is not used for client_credentials flow');
  284. }
  285. saveCodeVerifier() {
  286. // Not used for client_credentials
  287. }
  288. codeVerifier() {
  289. throw new Error('codeVerifier is not used for client_credentials flow');
  290. }
  291. prepareTokenRequest(scope) {
  292. const params = new URLSearchParams({ grant_type: 'client_credentials' });
  293. if (scope)
  294. params.set('scope', scope);
  295. return params;
  296. }
  297. }
  298. exports.StaticPrivateKeyJwtProvider = StaticPrivateKeyJwtProvider;
  299. //# sourceMappingURL=auth-extensions.js.map