twilio 5.6.0 → 5.7.0

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.
Files changed (51) hide show
  1. package/README.md +5 -0
  2. package/lib/base/BaseTwilio.d.ts +3 -0
  3. package/lib/base/BaseTwilio.js +4 -1
  4. package/lib/base/RequestClient.d.ts +36 -1
  5. package/lib/base/RequestClient.js +40 -0
  6. package/lib/base/ValidationClient.d.ts +10 -0
  7. package/lib/base/ValidationClient.js +2 -0
  8. package/lib/http/bearer_token/ApiTokenManager.d.ts +1 -1
  9. package/lib/http/bearer_token/ApiTokenManager.js +4 -4
  10. package/lib/http/bearer_token/OrgsTokenManager.d.ts +1 -1
  11. package/lib/http/bearer_token/OrgsTokenManager.js +4 -4
  12. package/lib/index.d.ts +3 -0
  13. package/lib/index.js +2 -0
  14. package/lib/jwt/validation/RequestCanonicalizer.d.ts +20 -0
  15. package/lib/jwt/validation/RequestCanonicalizer.js +97 -0
  16. package/lib/jwt/validation/ValidationToken.d.ts +45 -0
  17. package/lib/jwt/validation/ValidationToken.js +121 -0
  18. package/lib/rest/accounts/v1/bulkConsents.d.ts +1 -1
  19. package/lib/rest/api/v2010/account/address/dependentPhoneNumber.d.ts +10 -12
  20. package/lib/rest/api/v2010/account/address/dependentPhoneNumber.js +2 -2
  21. package/lib/rest/api/v2010/account/call/transcription.d.ts +1 -1
  22. package/lib/rest/api/v2010/account/usage/record/allTime.d.ts +1 -1
  23. package/lib/rest/api/v2010/account/usage/record/daily.d.ts +1 -1
  24. package/lib/rest/api/v2010/account/usage/record/lastMonth.d.ts +1 -1
  25. package/lib/rest/api/v2010/account/usage/record/monthly.d.ts +1 -1
  26. package/lib/rest/api/v2010/account/usage/record/thisMonth.d.ts +1 -1
  27. package/lib/rest/api/v2010/account/usage/record/today.d.ts +1 -1
  28. package/lib/rest/api/v2010/account/usage/record/yearly.d.ts +1 -1
  29. package/lib/rest/api/v2010/account/usage/record/yesterday.d.ts +1 -1
  30. package/lib/rest/api/v2010/account/usage/record.d.ts +1 -1
  31. package/lib/rest/api/v2010/account/usage/trigger.d.ts +1 -1
  32. package/lib/rest/api/v2010/account/usage.d.ts +1 -1
  33. package/lib/rest/content/v1/content/approvalFetch.d.ts +3 -3
  34. package/lib/rest/content/v1/content.d.ts +9 -6
  35. package/lib/rest/content/v1/contentAndApprovals.d.ts +9 -9
  36. package/lib/rest/content/v1/legacyContent.d.ts +6 -6
  37. package/lib/rest/events/v1/subscription.d.ts +0 -10
  38. package/lib/rest/events/v1/subscription.js +0 -6
  39. package/lib/rest/messaging/v1/brandRegistration/brandVetting.d.ts +0 -6
  40. package/lib/rest/messaging/v1/brandRegistration/brandVetting.js +0 -2
  41. package/lib/rest/messaging/v2/channelsSender.d.ts +2 -22
  42. package/lib/rest/messaging/v2/channelsSender.js +1 -7
  43. package/lib/rest/numbers/V1.d.ts +5 -5
  44. package/lib/rest/numbers/V1.js +6 -8
  45. package/lib/rest/numbers/v1/portingPortIn.d.ts +3 -3
  46. package/lib/rest/numbers/v1/{portingWebhookConfigurationFetch.d.ts → webhook.d.ts} +10 -10
  47. package/lib/rest/numbers/v1/{portingWebhookConfigurationFetch.js → webhook.js} +6 -6
  48. package/lib/rest/verify/v2/service.d.ts +1 -1
  49. package/lib/rest/verify/v2/verificationAttemptsSummary.d.ts +3 -3
  50. package/lib/twiml/VoiceResponse.d.ts +36 -16
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -274,6 +274,11 @@ If you are using callbacks, error information will be included in the `error` pa
274
274
 
275
275
  400-level errors are [normal during API operation](https://www.twilio.com/docs/api/rest/request#get-responses) ("Invalid number", "Cannot deliver SMS to that number", for example) and should be handled appropriately.
276
276
 
277
+ ### Use a Client with PKCV Authentication
278
+
279
+ twilio-node now supports Public Key Client Validation authentication for Twilio APIs. To use this feature, refer to the [example file](https://github.com/twilio/twilio-node/blob/main/examples/pkcv.js).
280
+ Additional documentation can be found on [Public Key Client Validation Quickstart](https://twilio.com/docs/iam/pkcv/quickstart).
281
+
277
282
  ### Use a custom HTTP Client
278
283
 
279
284
  To use a custom HTTP client with this helper library, please see the [advanced example of how to do so](./advanced-examples/custom-http-client.md).
@@ -2,6 +2,7 @@
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
4
  import RequestClient from "./RequestClient";
5
+ import { ValidationClientOptions } from "./ValidationClient";
5
6
  import { HttpMethod } from "../interfaces";
6
7
  import { Headers } from "../http/request";
7
8
  import AuthStrategy from "../auth_strategy/AuthStrategy";
@@ -19,6 +20,7 @@ declare namespace Twilio {
19
20
  autoRetry?: boolean;
20
21
  maxRetryDelay?: number;
21
22
  maxRetries?: number;
23
+ validationClient?: ValidationClientOptions;
22
24
  /**
23
25
  https.Agent options
24
26
  */
@@ -60,6 +62,7 @@ declare namespace Twilio {
60
62
  autoRetry?: boolean;
61
63
  maxRetryDelay?: number;
62
64
  maxRetries?: number;
65
+ validationClient?: ValidationClientOptions;
63
66
  /**
64
67
  https.Agent options
65
68
  */
@@ -67,6 +67,7 @@ var Twilio;
67
67
  this.autoRetry = this.opts.autoRetry || false;
68
68
  this.maxRetryDelay = this.opts.maxRetryDelay;
69
69
  this.maxRetries = this.opts.maxRetries;
70
+ this.validationClient = this.opts.validationClient;
70
71
  this.userAgentExtensions = this.opts.userAgentExtensions || [];
71
72
  this._httpClient = this.opts.httpClient;
72
73
  if (this.opts.lazyLoading === false) {
@@ -108,6 +109,7 @@ var Twilio;
108
109
  autoRetry: this.autoRetry,
109
110
  maxRetryDelay: this.maxRetryDelay,
110
111
  maxRetries: this.maxRetries,
112
+ validationClient: this.validationClient,
111
113
  });
112
114
  }
113
115
  return this._httpClient;
@@ -173,7 +175,8 @@ var Twilio;
173
175
  data: opts.data,
174
176
  timeout: opts.timeout,
175
177
  allowRedirects: opts.allowRedirects,
176
- logLevel: opts.logLevel,
178
+ // use the Twilio client's log-level if the httpClient's log-level is unspecified
179
+ logLevel: opts.logLevel || this.opts?.logLevel,
177
180
  });
178
181
  }
179
182
  /* jshint ignore:start */
@@ -1,10 +1,11 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  import { HttpMethod } from "../interfaces";
4
- import { AxiosInstance } from "axios";
4
+ import { AxiosInstance, InternalAxiosRequestConfig } from "axios";
5
5
  import Response from "../http/response";
6
6
  import Request, { Headers } from "../http/request";
7
7
  import AuthStrategy from "../auth_strategy/AuthStrategy";
8
+ import { ValidationClientOptions } from "./ValidationClient";
8
9
  declare class RequestClient {
9
10
  defaultTimeout: number;
10
11
  axios: AxiosInstance;
@@ -27,6 +28,7 @@ declare class RequestClient {
27
28
  * @param opts.autoRetry - Enable auto-retry requests with exponential backoff on 429 responses. Defaults to false.
28
29
  * @param opts.maxRetryDelay - Max retry delay in milliseconds for 429 Too Many Request response retries. Defaults to 3000.
29
30
  * @param opts.maxRetries - Max number of request retries for 429 Too Many Request responses. Defaults to 3.
31
+ * @param opts.validationClient - Validation client for PKCV
30
32
  */
31
33
  constructor(opts?: RequestClient.RequestClientOptions);
32
34
  /**
@@ -47,6 +49,28 @@ declare class RequestClient {
47
49
  */
48
50
  request<TData>(opts: RequestClient.RequestOptions<TData>): Promise<Response<TData>>;
49
51
  filterLoggingHeaders(headers: Headers): string[];
52
+ /**
53
+ * ValidationInterceptor adds the Twilio-Client-Validation header to the request
54
+ * @param validationClient - The validation client for PKCV
55
+ * <p>Usage Example:</p>
56
+ * ```javascript
57
+ * import axios from "axios";
58
+ * // Initialize validation client with credentials
59
+ * const validationClient = {
60
+ * accountSid: "ACXXXXXXXXXXXXXXXX",
61
+ * credentialSid: "CRXXXXXXXXXXXXXXXX",
62
+ * signingKey: "SKXXXXXXXXXXXXXXXX",
63
+ * privateKey: "private key",
64
+ * algorithm: "PS256",
65
+ * }
66
+ * // construct an axios instance
67
+ * const instance = axios.create();
68
+ * instance.interceptors.request.use(
69
+ * ValidationInterceptor(opts.validationClient)
70
+ * );
71
+ * ```
72
+ */
73
+ validationInterceptor(validationClient: ValidationClientOptions): (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig<any>;
50
74
  private logRequest;
51
75
  }
52
76
  declare namespace RequestClient {
@@ -148,6 +172,17 @@ declare namespace RequestClient {
148
172
  * Maximum number of request retries for 429 Error responses. Defaults to 3.
149
173
  */
150
174
  maxRetries?: number;
175
+ /**
176
+ * Validation client for Public Key Client Validation
177
+ * On setting this with your credentials, Twilio validates:
178
+ <ul>
179
+ <li>The request comes from a sender who is in control of the private key.</li>
180
+ <li>The message has not been modified in transit.</li>
181
+ </ul>
182
+ * That the message has not been modified in transit.
183
+ * Refer our doc for details - https://www.twilio.com/docs/iam/pkcv
184
+ */
185
+ validationClient?: ValidationClientOptions;
151
186
  }
152
187
  }
153
188
  export = RequestClient;
@@ -32,6 +32,7 @@ const qs_1 = __importDefault(require("qs"));
32
32
  const https = __importStar(require("https"));
33
33
  const response_1 = __importDefault(require("../http/response"));
34
34
  const request_1 = __importDefault(require("../http/request"));
35
+ const ValidationToken_1 = __importDefault(require("../jwt/validation/ValidationToken"));
35
36
  const DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded";
36
37
  const DEFAULT_TIMEOUT = 30000;
37
38
  const DEFAULT_INITIAL_RETRY_INTERVAL_MILLIS = 100;
@@ -74,6 +75,7 @@ class RequestClient {
74
75
  * @param opts.autoRetry - Enable auto-retry requests with exponential backoff on 429 responses. Defaults to false.
75
76
  * @param opts.maxRetryDelay - Max retry delay in milliseconds for 429 Too Many Request response retries. Defaults to 3000.
76
77
  * @param opts.maxRetries - Max number of request retries for 429 Too Many Request responses. Defaults to 3.
78
+ * @param opts.validationClient - Validation client for PKCV
77
79
  */
78
80
  constructor(opts) {
79
81
  opts = opts || {};
@@ -118,6 +120,10 @@ class RequestClient {
118
120
  maxRetries: this.maxRetries,
119
121
  }));
120
122
  }
123
+ // if validation client is set, intercept the request using ValidationInterceptor
124
+ if (opts.validationClient) {
125
+ this.axios.interceptors.request.use(this.validationInterceptor(opts.validationClient));
126
+ }
121
127
  }
122
128
  /**
123
129
  * Make http request
@@ -213,6 +219,40 @@ class RequestClient {
213
219
  return !"authorization".includes(header.toLowerCase());
214
220
  });
215
221
  }
222
+ /**
223
+ * ValidationInterceptor adds the Twilio-Client-Validation header to the request
224
+ * @param validationClient - The validation client for PKCV
225
+ * <p>Usage Example:</p>
226
+ * ```javascript
227
+ * import axios from "axios";
228
+ * // Initialize validation client with credentials
229
+ * const validationClient = {
230
+ * accountSid: "ACXXXXXXXXXXXXXXXX",
231
+ * credentialSid: "CRXXXXXXXXXXXXXXXX",
232
+ * signingKey: "SKXXXXXXXXXXXXXXXX",
233
+ * privateKey: "private key",
234
+ * algorithm: "PS256",
235
+ * }
236
+ * // construct an axios instance
237
+ * const instance = axios.create();
238
+ * instance.interceptors.request.use(
239
+ * ValidationInterceptor(opts.validationClient)
240
+ * );
241
+ * ```
242
+ */
243
+ validationInterceptor(validationClient) {
244
+ return function (config) {
245
+ config.headers = config.headers || {};
246
+ try {
247
+ config.headers["Twilio-Client-Validation"] = new ValidationToken_1.default(validationClient).fromHttpRequest(config);
248
+ }
249
+ catch (err) {
250
+ console.log("Error creating Twilio-Client-Validation header:", err);
251
+ throw err;
252
+ }
253
+ return config;
254
+ };
255
+ }
216
256
  logRequest(options) {
217
257
  console.log("-- BEGIN Twilio API Request --");
218
258
  console.log(`${options.method} ${options.url}`);
@@ -0,0 +1,10 @@
1
+ declare namespace ValidationClient {
2
+ interface ValidationClientOptions {
3
+ accountSid: string;
4
+ credentialSid: string;
5
+ signingKey: string;
6
+ privateKey: string;
7
+ algorithm?: string;
8
+ }
9
+ }
10
+ export = ValidationClient;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +1,5 @@
1
1
  import TokenManager from "./TokenManager";
2
- import { TokenListInstanceCreateOptions } from "../../rest/previewIam/v1/token";
2
+ import { TokenListInstanceCreateOptions } from "../../rest/iam/v1/token";
3
3
  export default class ApiTokenManager implements TokenManager {
4
4
  private params;
5
5
  constructor(params: TokenListInstanceCreateOptions);
@@ -3,9 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const token_1 = require("../../rest/previewIam/v1/token");
7
- const PreviewIamBase_1 = __importDefault(require("../../rest/PreviewIamBase"));
8
- const V1_1 = __importDefault(require("../../rest/previewIam/V1"));
6
+ const token_1 = require("../../rest/iam/v1/token");
7
+ const IamBase_1 = __importDefault(require("../../rest/IamBase"));
8
+ const V1_1 = __importDefault(require("../../rest/iam/V1"));
9
9
  const NoAuthCredentialProvider_1 = __importDefault(require("../../credential_provider/NoAuthCredentialProvider"));
10
10
  const BaseTwilio_1 = require("../../base/BaseTwilio");
11
11
  class ApiTokenManager {
@@ -19,7 +19,7 @@ class ApiTokenManager {
19
19
  const noAuthCredentialProvider = new NoAuthCredentialProvider_1.default.NoAuthCredentialProvider();
20
20
  const client = new BaseTwilio_1.Client();
21
21
  client.setCredentialProvider(noAuthCredentialProvider);
22
- const tokenListInstance = (0, token_1.TokenListInstance)(new V1_1.default(new PreviewIamBase_1.default(client)));
22
+ const tokenListInstance = (0, token_1.TokenListInstance)(new V1_1.default(new IamBase_1.default(client)));
23
23
  return tokenListInstance
24
24
  .create(this.params)
25
25
  .then((token) => {
@@ -1,5 +1,5 @@
1
1
  import TokenManager from "./TokenManager";
2
- import { TokenListInstanceCreateOptions } from "../../rest/previewIam/v1/token";
2
+ import { TokenListInstanceCreateOptions } from "../../rest/iam/v1/token";
3
3
  export default class OrgsTokenManager implements TokenManager {
4
4
  private readonly params;
5
5
  constructor(params: TokenListInstanceCreateOptions);
@@ -3,9 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const token_1 = require("../../rest/previewIam/v1/token");
7
- const PreviewIamBase_1 = __importDefault(require("../../rest/PreviewIamBase"));
8
- const V1_1 = __importDefault(require("../../rest/previewIam/V1"));
6
+ const token_1 = require("../../rest/iam/v1/token");
7
+ const IamBase_1 = __importDefault(require("../../rest/IamBase"));
8
+ const V1_1 = __importDefault(require("../../rest/iam/V1"));
9
9
  const NoAuthCredentialProvider_1 = __importDefault(require("../../credential_provider/NoAuthCredentialProvider"));
10
10
  const BaseTwilio_1 = require("../../base/BaseTwilio");
11
11
  class OrgsTokenManager {
@@ -19,7 +19,7 @@ class OrgsTokenManager {
19
19
  const noAuthCredentialProvider = new NoAuthCredentialProvider_1.default.NoAuthCredentialProvider();
20
20
  const client = new BaseTwilio_1.Client();
21
21
  client.setCredentialProvider(noAuthCredentialProvider);
22
- const tokenListInstance = (0, token_1.TokenListInstance)(new V1_1.default(new PreviewIamBase_1.default(client)));
22
+ const tokenListInstance = (0, token_1.TokenListInstance)(new V1_1.default(new IamBase_1.default(client)));
23
23
  return tokenListInstance
24
24
  .create(this.params)
25
25
  .then((token) => {
package/lib/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import * as webhooks from "./webhooks/webhooks";
3
3
  import IRequestClient from "./base/RequestClient";
4
4
  import type { ClientOpts as IClientOpts } from "./base/BaseTwilio";
5
5
  import IAccessToken from "./jwt/AccessToken";
6
+ import IValidationToken from "./jwt/validation/ValidationToken";
6
7
  import IClientCapability from "./jwt/ClientCapability";
7
8
  import ITaskRouterCapability from "./jwt/taskrouter/TaskRouterCapability";
8
9
  import * as taskRouterUtil from "./jwt/taskrouter/util";
@@ -19,6 +20,8 @@ declare namespace TwilioSDK {
19
20
  namespace jwt {
20
21
  type AccessToken = IAccessToken;
21
22
  const AccessToken: typeof IAccessToken;
23
+ type ValidationToken = IValidationToken;
24
+ const ValidationToken: typeof IValidationToken;
22
25
  type ClientCapability = IClientCapability;
23
26
  const ClientCapability: typeof IClientCapability;
24
27
  namespace taskrouter {
package/lib/index.js CHANGED
@@ -29,6 +29,7 @@ const Twilio_1 = __importDefault(require("./rest/Twilio"));
29
29
  const webhooks = __importStar(require("./webhooks/webhooks"));
30
30
  const RequestClient_1 = __importDefault(require("./base/RequestClient"));
31
31
  const AccessToken_1 = __importDefault(require("./jwt/AccessToken"));
32
+ const ValidationToken_1 = __importDefault(require("./jwt/validation/ValidationToken"));
32
33
  const ClientCapability_1 = __importDefault(require("./jwt/ClientCapability"));
33
34
  const TaskRouterCapability_1 = __importDefault(require("./jwt/taskrouter/TaskRouterCapability"));
34
35
  const taskRouterUtil = __importStar(require("./jwt/taskrouter/util"));
@@ -47,6 +48,7 @@ function TwilioSDK(accountSid, authToken, opts) {
47
48
  let jwt;
48
49
  (function (jwt) {
49
50
  jwt.AccessToken = AccessToken_1.default;
51
+ jwt.ValidationToken = ValidationToken_1.default;
50
52
  jwt.ClientCapability = ClientCapability_1.default;
51
53
  let taskrouter;
52
54
  (function (taskrouter) {
@@ -0,0 +1,20 @@
1
+ declare class RequestCanonicalizer {
2
+ method: string;
3
+ uri: string;
4
+ queryParams: Record<string, string>;
5
+ requestBody: any;
6
+ headers: Record<string, string>;
7
+ constructor(method: string, uri: string, queryParams: Record<string, string>, requestBody: any, headers: Record<string, string>);
8
+ getCanonicalizedMethod(): string;
9
+ customEncode(str: string): string;
10
+ ASCIICompare(a: string, b: string): number;
11
+ getCanonicalizedPath(): string;
12
+ getCanonicalizedQueryParams(): string;
13
+ getCanonicalizedHeaders(): string;
14
+ getCanonicalizedHashedHeaders(): string;
15
+ getCanonicalizedRequestBody(): string;
16
+ sha256Hex(body: string): string;
17
+ getCanonicalizedRequestString(): string;
18
+ create(): string;
19
+ }
20
+ export default RequestCanonicalizer;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const crypto_1 = __importDefault(require("crypto"));
7
+ class RequestCanonicalizer {
8
+ constructor(method, uri, queryParams, requestBody, headers) {
9
+ this.method = method;
10
+ this.uri = uri;
11
+ this.queryParams = queryParams;
12
+ this.requestBody = requestBody;
13
+ this.headers = headers;
14
+ }
15
+ getCanonicalizedMethod() {
16
+ return this.method.toUpperCase();
17
+ }
18
+ customEncode(str) {
19
+ return encodeURIComponent(decodeURIComponent(str))
20
+ .replace(/\*/g, "%2A")
21
+ .replace(/%7E/g, "~");
22
+ }
23
+ ASCIICompare(a, b) {
24
+ if (a < b)
25
+ return -1;
26
+ return a > b ? 1 : 0;
27
+ }
28
+ getCanonicalizedPath() {
29
+ // Remove query string from path
30
+ const path = this.uri.split("?")[0];
31
+ // Normalize duplicate slashes (but preserve the leading one)
32
+ const normalizedPath = path.replace(/\/+/g, "/");
33
+ // We must preserve slashes (as path delimiters) but encode each segment
34
+ // Split and encode, but first decode each segment to avoid double-encoding
35
+ return normalizedPath
36
+ .split("/")
37
+ .map((segment) => this.customEncode(segment))
38
+ .join("/");
39
+ }
40
+ getCanonicalizedQueryParams() {
41
+ if (!this.queryParams) {
42
+ return "";
43
+ }
44
+ // sort query params on the basis of '{key}={value}'
45
+ const sortedQueryParams = Object.entries(this.queryParams)
46
+ .map(([key, value]) => {
47
+ return `${key}=${value}`;
48
+ })
49
+ .sort((a, b) => this.ASCIICompare(a, b)) // forces ASCII sorting using custom compare
50
+ .map((param) => {
51
+ const [key, value] = param.split("=");
52
+ return `${this.customEncode(key)}=${this.customEncode(value)}`; // encode and concatenate as `key=value`
53
+ });
54
+ return sortedQueryParams.join("&");
55
+ }
56
+ getCanonicalizedHeaders() {
57
+ // sort headers on the basis of '{key}:{value}'
58
+ const sortedHeaders = Object.keys(this.headers)
59
+ .map((key) => {
60
+ if (!this.headers[key]) {
61
+ return `${key.toLowerCase()}:`;
62
+ }
63
+ return `${key.toLowerCase()}:${this.headers[key].trim()}`;
64
+ })
65
+ .sort((a, b) => this.ASCIICompare(a, b)); // forces ASCII sorting using custom compare
66
+ return `${sortedHeaders.join("\n")}\n`;
67
+ }
68
+ getCanonicalizedHashedHeaders() {
69
+ const sortedHeaders = Object.keys(this.headers).sort((a, b) => this.ASCIICompare(a, b)); // forces ASCII sorting using custom compare
70
+ return sortedHeaders.join(";");
71
+ }
72
+ getCanonicalizedRequestBody() {
73
+ if (!this.requestBody) {
74
+ return "";
75
+ }
76
+ if (typeof this.requestBody === "string") {
77
+ return this.sha256Hex(this.requestBody);
78
+ }
79
+ else
80
+ return this.sha256Hex(JSON.stringify(this.requestBody));
81
+ }
82
+ sha256Hex(body) {
83
+ return crypto_1.default.createHash("sha256").update(body).digest("hex");
84
+ }
85
+ getCanonicalizedRequestString() {
86
+ return `${this.getCanonicalizedMethod()}
87
+ ${this.getCanonicalizedPath()}
88
+ ${this.getCanonicalizedQueryParams()}
89
+ ${this.getCanonicalizedHeaders()}
90
+ ${this.getCanonicalizedHashedHeaders()}
91
+ ${this.getCanonicalizedRequestBody()}`;
92
+ }
93
+ create() {
94
+ return this.sha256Hex(this.getCanonicalizedRequestString());
95
+ }
96
+ }
97
+ exports.default = RequestCanonicalizer;
@@ -0,0 +1,45 @@
1
+ import { ValidationClientOptions } from "../../base/ValidationClient";
2
+ import RequestCanonicalizer from "./RequestCanonicalizer";
3
+ import jwt, { Algorithm } from "jsonwebtoken";
4
+ declare class ValidationToken {
5
+ static readonly DEFAULT_ALGORITHM: "RS256";
6
+ static readonly ALGORITHMS: readonly [jwt.Algorithm, jwt.Algorithm];
7
+ private readonly _accountSid;
8
+ private readonly _credentialSid;
9
+ private readonly _signingKey;
10
+ private readonly _privateKey;
11
+ private readonly _algorithm;
12
+ ttl: number;
13
+ get accountSid(): string;
14
+ get credentialSid(): string;
15
+ get signingKey(): string;
16
+ get privateKey(): string;
17
+ get algorithm(): Algorithm;
18
+ /**
19
+ * @constructor
20
+ * @param opts - The Options used to configure the ValidationToken
21
+ * @param opts.accountSid - The account SID
22
+ * @param opts.credentialSid - The credential SID for public key submitted to Twilio
23
+ * @param opts.signingKey - The signing key
24
+ * @param opts.privateKey - The private key for signing the token
25
+ * @param opts.algorithm - The algorithm to use for signing the token
26
+ * @param opts.ttl - The time to live for the token in seconds
27
+ */
28
+ constructor(opts: ValidationClientOptions);
29
+ /**
30
+ * Generates a `RequestCanonicalizer` instance for the given HTTP request.
31
+ *
32
+ * @param request - The HTTP request object containing details such as headers, URL, method, query parameters, and body.
33
+ * @throws {Error} If the request URL or method is missing.
34
+ * @returns {RequestCanonicalizer} - An instance of `RequestCanonicalizer` initialized with the canonicalized request details.
35
+ */
36
+ getRequestCanonicalizer(request: any): RequestCanonicalizer;
37
+ /**
38
+ * Generate a JWT token to include in the request header for PKCV
39
+ * @param request - The request object
40
+ * @returns {string} - The JWT token
41
+ */
42
+ fromHttpRequest(request: any): string;
43
+ }
44
+ declare namespace ValidationToken { }
45
+ export = ValidationToken;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const RequestCanonicalizer_1 = __importDefault(require("./RequestCanonicalizer"));
6
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
7
+ class ValidationToken {
8
+ get accountSid() {
9
+ return this._accountSid;
10
+ }
11
+ get credentialSid() {
12
+ return this._credentialSid;
13
+ }
14
+ get signingKey() {
15
+ return this._signingKey;
16
+ }
17
+ get privateKey() {
18
+ return this._privateKey;
19
+ }
20
+ get algorithm() {
21
+ return this._algorithm;
22
+ }
23
+ /**
24
+ * @constructor
25
+ * @param opts - The Options used to configure the ValidationToken
26
+ * @param opts.accountSid - The account SID
27
+ * @param opts.credentialSid - The credential SID for public key submitted to Twilio
28
+ * @param opts.signingKey - The signing key
29
+ * @param opts.privateKey - The private key for signing the token
30
+ * @param opts.algorithm - The algorithm to use for signing the token
31
+ * @param opts.ttl - The time to live for the token in seconds
32
+ */
33
+ constructor(opts) {
34
+ if (!opts.accountSid) {
35
+ throw new Error("accountSid is required");
36
+ }
37
+ if (!opts.credentialSid) {
38
+ throw new Error("credentialSid is required");
39
+ }
40
+ if (!opts.signingKey) {
41
+ throw new Error("signingKey is required");
42
+ }
43
+ if (!opts.privateKey) {
44
+ throw new Error("privateKey is required");
45
+ }
46
+ const algorithm = opts.algorithm ?? ValidationToken.DEFAULT_ALGORITHM; // default to RS256;
47
+ if (!ValidationToken.ALGORITHMS.includes(algorithm)) {
48
+ throw new Error("Algorithm not supported. Allowed values are " +
49
+ ValidationToken.ALGORITHMS.join(", "));
50
+ }
51
+ this._accountSid = opts.accountSid;
52
+ this._credentialSid = opts.credentialSid;
53
+ this._signingKey = opts.signingKey;
54
+ this._privateKey = opts.privateKey;
55
+ this._algorithm = algorithm;
56
+ this.ttl = 300;
57
+ }
58
+ /**
59
+ * Generates a `RequestCanonicalizer` instance for the given HTTP request.
60
+ *
61
+ * @param request - The HTTP request object containing details such as headers, URL, method, query parameters, and body.
62
+ * @throws {Error} If the request URL or method is missing.
63
+ * @returns {RequestCanonicalizer} - An instance of `RequestCanonicalizer` initialized with the canonicalized request details.
64
+ */
65
+ getRequestCanonicalizer(request) {
66
+ const headers = request.headers ?? {};
67
+ const requestUrl = request.url;
68
+ const method = request.method;
69
+ const queryParams = request.params;
70
+ const requestBody = request.data;
71
+ if (!requestUrl) {
72
+ throw new Error("Url is required");
73
+ }
74
+ if (!method) {
75
+ throw new Error("Method is required");
76
+ }
77
+ const url = new URL(requestUrl);
78
+ let signedHeaders = {
79
+ host: url.host,
80
+ authorization: headers["Authorization"],
81
+ };
82
+ return new RequestCanonicalizer_1.default(method, url.pathname, queryParams, requestBody, signedHeaders);
83
+ }
84
+ /**
85
+ * Generate a JWT token to include in the request header for PKCV
86
+ * @param request - The request object
87
+ * @returns {string} - The JWT token
88
+ */
89
+ fromHttpRequest(request) {
90
+ try {
91
+ const requestCanonicalizer = this.getRequestCanonicalizer(request);
92
+ const canonicalizedRequest = requestCanonicalizer.create();
93
+ const header = {
94
+ cty: "twilio-pkrv;v=1",
95
+ typ: "JWT",
96
+ alg: this._algorithm,
97
+ kid: this._credentialSid,
98
+ };
99
+ const payload = {
100
+ iss: this._signingKey,
101
+ sub: this._accountSid,
102
+ hrh: requestCanonicalizer.getCanonicalizedHashedHeaders(),
103
+ rqh: canonicalizedRequest,
104
+ };
105
+ return jsonwebtoken_1.default.sign(payload, this._privateKey, {
106
+ header: header,
107
+ algorithm: this._algorithm,
108
+ expiresIn: this.ttl,
109
+ });
110
+ }
111
+ catch (err) {
112
+ throw new Error("Error generating JWT token " + err);
113
+ }
114
+ }
115
+ }
116
+ ValidationToken.DEFAULT_ALGORITHM = "RS256";
117
+ ValidationToken.ALGORITHMS = [
118
+ "RS256",
119
+ "PS256",
120
+ ];
121
+ module.exports = ValidationToken;
@@ -5,7 +5,7 @@ import V1 from "../V1";
5
5
  * Options to pass to create a BulkConsentsInstance
6
6
  */
7
7
  export interface BulkConsentsListInstanceCreateOptions {
8
- /** This is a list of objects that describes a contact\\\'s opt-in status. Each object contains the following fields: `contact_id`, which must be a string representing phone number in [E.164 format](https://www.twilio.com/docs/glossary/what-e164); `correlation_id`, a unique 32-character UUID used to uniquely map the request item with the response item; `sender_id`, which can be either a valid messaging service SID or a from phone number; `status`, a string representing the consent status. Can be one of [`opt-in`, `opt-out`]; and `source`, a string indicating the medium through which the consent was collected. Can be one of [`website`, `offline`, `opt-in-message`, `opt-out-message`, `others`]. */
8
+ /** This is a list of objects that describes a contact\\\'s opt-in status. Each object contains the following fields: `contact_id`, which must be a string representing phone number in [E.164 format](https://www.twilio.com/docs/glossary/what-e164); `correlation_id`, a unique 32-character UUID used to uniquely map the request item with the response item; `sender_id`, which can be either a valid messaging service SID or a from phone number; `status`, a string representing the consent status. Can be one of [`opt-in`, `opt-out`]; `source`, a string indicating the medium through which the consent was collected. Can be one of [`website`, `offline`, `opt-in-message`, `opt-out-message`, `others`]; `date_of_consent`, an optional datetime string field in ISO-8601 format that captures the exact date and time when the user gave or revoked consent. If not provided, it will be empty. */
9
9
  items: Array<object>;
10
10
  }
11
11
  export interface BulkConsentsSolution {