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.
- package/README.md +5 -0
- package/lib/base/BaseTwilio.d.ts +3 -0
- package/lib/base/BaseTwilio.js +4 -1
- package/lib/base/RequestClient.d.ts +36 -1
- package/lib/base/RequestClient.js +40 -0
- package/lib/base/ValidationClient.d.ts +10 -0
- package/lib/base/ValidationClient.js +2 -0
- package/lib/http/bearer_token/ApiTokenManager.d.ts +1 -1
- package/lib/http/bearer_token/ApiTokenManager.js +4 -4
- package/lib/http/bearer_token/OrgsTokenManager.d.ts +1 -1
- package/lib/http/bearer_token/OrgsTokenManager.js +4 -4
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/jwt/validation/RequestCanonicalizer.d.ts +20 -0
- package/lib/jwt/validation/RequestCanonicalizer.js +97 -0
- package/lib/jwt/validation/ValidationToken.d.ts +45 -0
- package/lib/jwt/validation/ValidationToken.js +121 -0
- package/lib/rest/accounts/v1/bulkConsents.d.ts +1 -1
- package/lib/rest/api/v2010/account/address/dependentPhoneNumber.d.ts +10 -12
- package/lib/rest/api/v2010/account/address/dependentPhoneNumber.js +2 -2
- package/lib/rest/api/v2010/account/call/transcription.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/allTime.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/daily.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/lastMonth.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/monthly.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/thisMonth.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/today.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/yearly.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record/yesterday.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/record.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage/trigger.d.ts +1 -1
- package/lib/rest/api/v2010/account/usage.d.ts +1 -1
- package/lib/rest/content/v1/content/approvalFetch.d.ts +3 -3
- package/lib/rest/content/v1/content.d.ts +9 -6
- package/lib/rest/content/v1/contentAndApprovals.d.ts +9 -9
- package/lib/rest/content/v1/legacyContent.d.ts +6 -6
- package/lib/rest/events/v1/subscription.d.ts +0 -10
- package/lib/rest/events/v1/subscription.js +0 -6
- package/lib/rest/messaging/v1/brandRegistration/brandVetting.d.ts +0 -6
- package/lib/rest/messaging/v1/brandRegistration/brandVetting.js +0 -2
- package/lib/rest/messaging/v2/channelsSender.d.ts +2 -22
- package/lib/rest/messaging/v2/channelsSender.js +1 -7
- package/lib/rest/numbers/V1.d.ts +5 -5
- package/lib/rest/numbers/V1.js +6 -8
- package/lib/rest/numbers/v1/portingPortIn.d.ts +3 -3
- package/lib/rest/numbers/v1/{portingWebhookConfigurationFetch.d.ts → webhook.d.ts} +10 -10
- package/lib/rest/numbers/v1/{portingWebhookConfigurationFetch.js → webhook.js} +6 -6
- package/lib/rest/verify/v2/service.d.ts +1 -1
- package/lib/rest/verify/v2/verificationAttemptsSummary.d.ts +3 -3
- package/lib/twiml/VoiceResponse.d.ts +36 -16
- 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).
|
package/lib/base/BaseTwilio.d.ts
CHANGED
|
@@ -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
|
*/
|
package/lib/base/BaseTwilio.js
CHANGED
|
@@ -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
|
-
|
|
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}`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import TokenManager from "./TokenManager";
|
|
2
|
-
import { TokenListInstanceCreateOptions } from "../../rest/
|
|
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/
|
|
7
|
-
const
|
|
8
|
-
const V1_1 = __importDefault(require("../../rest/
|
|
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
|
|
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/
|
|
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/
|
|
7
|
-
const
|
|
8
|
-
const V1_1 = __importDefault(require("../../rest/
|
|
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
|
|
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`];
|
|
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 {
|