sms-verification-api 0.9.1 → 0.9.7
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/CHANGELOG.md +71 -0
- package/examples/{client.js → client.ts} +105 -119
- package/examples/{libphonenumber-example.js → libphonenumber-example.ts} +1 -1
- package/package.json +16 -14
- package/src/identity-verification-server.ts +385 -261
- package/src/{index.js → index.ts} +1 -1
- package/src/sns.ts +265 -0
- package/src/{verify-phone-server.js → verify-phone-server.ts} +206 -151
- package/src/verify-phone.ts +48 -22
- package/test/{api.test.js → api.test.ts} +20 -16
- package/test/{integration.test.js → integration.test.ts} +10 -10
- package/test/{server.test.js → server.test.ts} +3 -3
- package/test/{verify.test.js → verify.test.ts} +20 -23
- package/test/{voip.test.js → voip.test.ts} +13 -12
- package/tsconfig.json +24 -0
- package/{vitest.config.js → vitest.config.ts} +1 -1
- package/wrangler.toml +1 -4
- package/src/sns.js +0 -236
- /package/test/{metadata-test.js → metadata-test.ts} +0 -0
- /package/test/{setup.js → setup.ts} +0 -0
- /package/test/{utils.test.js → utils.test.ts} +0 -0
package/src/sns.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AWS SNS HTTP API Client for Cloudflare Workers
|
|
3
|
-
* @class SNSClient
|
|
4
|
-
*/
|
|
5
|
-
class SNSClient {
|
|
6
|
-
/**
|
|
7
|
-
* Create a new SNS client instance
|
|
8
|
-
* @param {Object} options - Configuration options
|
|
9
|
-
* @param {string} options.accessKeyId - AWS access key ID
|
|
10
|
-
* @param {string} options.secretAccessKey - AWS secret access key
|
|
11
|
-
* @param {string} [options.region='us-east-1'] - AWS region
|
|
12
|
-
*/
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
this.accessKeyId = options.accessKeyId;
|
|
15
|
-
this.secretAccessKey = options.secretAccessKey;
|
|
16
|
-
this.region = options.region || 'us-east-1';
|
|
17
|
-
this.endpoint = `https://sns.${this.region}.amazonaws.com`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Convert string to Uint8Array
|
|
21
|
-
stringToUint8Array(str) {
|
|
22
|
-
return new TextEncoder().encode(str);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Convert Uint8Array to hex string
|
|
26
|
-
arrayBufferToHex(buffer) {
|
|
27
|
-
return Array.from(new Uint8Array(buffer))
|
|
28
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
29
|
-
.join('');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// AWS Signature Version 4 signing using Web Crypto API
|
|
33
|
-
async sign(method, url, headers, payload) {
|
|
34
|
-
const now = new Date();
|
|
35
|
-
const amzDate = now.toISOString().replace(/[:\-]|\.\d{3}/g, '');
|
|
36
|
-
const dateStamp = amzDate.substr(0, 8);
|
|
37
|
-
|
|
38
|
-
// Create canonical request
|
|
39
|
-
const canonicalUri = '/';
|
|
40
|
-
const canonicalQuerystring = url.split('?')[1] || '';
|
|
41
|
-
|
|
42
|
-
// Add required headers
|
|
43
|
-
headers['host'] = `sns.${this.region}.amazonaws.com`;
|
|
44
|
-
headers['x-amz-date'] = amzDate;
|
|
45
|
-
|
|
46
|
-
// Calculate payload hash
|
|
47
|
-
const payloadHash = await crypto.subtle.digest('SHA-256', this.stringToUint8Array(payload))
|
|
48
|
-
.then(buffer => this.arrayBufferToHex(buffer));
|
|
49
|
-
headers['x-amz-content-sha256'] = payloadHash;
|
|
50
|
-
|
|
51
|
-
// Create canonical headers
|
|
52
|
-
const sortedHeaders = Object.keys(headers).sort().map(key =>
|
|
53
|
-
`${key.toLowerCase()}:${headers[key]}`
|
|
54
|
-
).join('\n');
|
|
55
|
-
|
|
56
|
-
const signedHeaders = Object.keys(headers).sort().map(key =>
|
|
57
|
-
key.toLowerCase()
|
|
58
|
-
).join(';');
|
|
59
|
-
|
|
60
|
-
const canonicalRequest = [
|
|
61
|
-
method,
|
|
62
|
-
canonicalUri,
|
|
63
|
-
canonicalQuerystring,
|
|
64
|
-
sortedHeaders,
|
|
65
|
-
'',
|
|
66
|
-
signedHeaders,
|
|
67
|
-
payloadHash
|
|
68
|
-
].join('\n');
|
|
69
|
-
|
|
70
|
-
// Create string to sign
|
|
71
|
-
const algorithm = 'AWS4-HMAC-SHA256';
|
|
72
|
-
const credentialScope = `${dateStamp}/${this.region}/sns/aws4_request`;
|
|
73
|
-
|
|
74
|
-
const canonicalRequestHash = await crypto.subtle.digest('SHA-256', this.stringToUint8Array(canonicalRequest))
|
|
75
|
-
.then(buffer => this.arrayBufferToHex(buffer));
|
|
76
|
-
|
|
77
|
-
const stringToSign = [
|
|
78
|
-
algorithm,
|
|
79
|
-
amzDate,
|
|
80
|
-
credentialScope,
|
|
81
|
-
canonicalRequestHash
|
|
82
|
-
].join('\n');
|
|
83
|
-
|
|
84
|
-
// Calculate signature using Web Crypto API
|
|
85
|
-
const kDate = await crypto.subtle.importKey(
|
|
86
|
-
'raw',
|
|
87
|
-
this.stringToUint8Array(`AWS4${this.secretAccessKey}`),
|
|
88
|
-
{ name: 'HMAC', hash: 'SHA-256' },
|
|
89
|
-
false,
|
|
90
|
-
['sign']
|
|
91
|
-
).then(key => crypto.subtle.sign('HMAC', key, this.stringToUint8Array(dateStamp)));
|
|
92
|
-
|
|
93
|
-
const kRegion = await crypto.subtle.importKey(
|
|
94
|
-
'raw',
|
|
95
|
-
kDate,
|
|
96
|
-
{ name: 'HMAC', hash: 'SHA-256' },
|
|
97
|
-
false,
|
|
98
|
-
['sign']
|
|
99
|
-
).then(key => crypto.subtle.sign('HMAC', key, this.stringToUint8Array(this.region)));
|
|
100
|
-
|
|
101
|
-
const kService = await crypto.subtle.importKey(
|
|
102
|
-
'raw',
|
|
103
|
-
kRegion,
|
|
104
|
-
{ name: 'HMAC', hash: 'SHA-256' },
|
|
105
|
-
false,
|
|
106
|
-
['sign']
|
|
107
|
-
).then(key => crypto.subtle.sign('HMAC', key, this.stringToUint8Array('sns')));
|
|
108
|
-
|
|
109
|
-
const kSigning = await crypto.subtle.importKey(
|
|
110
|
-
'raw',
|
|
111
|
-
kService,
|
|
112
|
-
{ name: 'HMAC', hash: 'SHA-256' },
|
|
113
|
-
false,
|
|
114
|
-
['sign']
|
|
115
|
-
).then(key => crypto.subtle.sign('HMAC', key, this.stringToUint8Array('aws4_request')));
|
|
116
|
-
|
|
117
|
-
const signature = await crypto.subtle.importKey(
|
|
118
|
-
'raw',
|
|
119
|
-
kSigning,
|
|
120
|
-
{ name: 'HMAC', hash: 'SHA-256' },
|
|
121
|
-
false,
|
|
122
|
-
['sign']
|
|
123
|
-
).then(key => crypto.subtle.sign('HMAC', key, this.stringToUint8Array(stringToSign)))
|
|
124
|
-
.then(buffer => this.arrayBufferToHex(buffer));
|
|
125
|
-
|
|
126
|
-
// Create authorization header
|
|
127
|
-
headers['authorization'] = `${algorithm} Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
128
|
-
|
|
129
|
-
return headers;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Make HTTP request to SNS
|
|
133
|
-
async makeRequest(action, params = {}) {
|
|
134
|
-
const queryParams = new URLSearchParams({
|
|
135
|
-
Action: action,
|
|
136
|
-
Version: '2010-03-31',
|
|
137
|
-
...params
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const url = `${this.endpoint}/?${queryParams.toString()}`;
|
|
141
|
-
const payload = '';
|
|
142
|
-
const headers = {
|
|
143
|
-
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// Sign the request
|
|
147
|
-
const signedHeaders = await this.sign('GET', url, headers, payload);
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
const response = await fetch(url, {
|
|
151
|
-
method: 'GET',
|
|
152
|
-
headers: signedHeaders
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const text = await response.text();
|
|
156
|
-
|
|
157
|
-
if (!response.ok) {
|
|
158
|
-
throw new Error(`SNS API Error: ${response.status} - ${text}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return this.parseXMLResponse(text);
|
|
162
|
-
} catch (error) {
|
|
163
|
-
throw new Error(`SNS Request failed: ${error.message}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Parse XML response (simplified for Cloudflare Workers)
|
|
168
|
-
parseXMLResponse(xmlText) {
|
|
169
|
-
// Simple XML parsing for common SNS responses
|
|
170
|
-
// Note: DOMParser is not available in Cloudflare Workers, so we'll use regex parsing
|
|
171
|
-
// This is a simplified parser for SNS responses
|
|
172
|
-
|
|
173
|
-
// Handle common SNS responses using regex parsing
|
|
174
|
-
const messageIdMatch = xmlText.match(/<MessageId>([^<]+)<\/MessageId>/);
|
|
175
|
-
const topicArnMatch = xmlText.match(/<TopicArn>([^<]+)<\/TopicArn>/);
|
|
176
|
-
const subscriptionArnMatch = xmlText.match(/<SubscriptionArn>([^<]+)<\/SubscriptionArn>/);
|
|
177
|
-
|
|
178
|
-
if (messageIdMatch) {
|
|
179
|
-
return { MessageId: messageIdMatch[1] };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (topicArnMatch) {
|
|
183
|
-
return { TopicArn: topicArnMatch[1] };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (subscriptionArnMatch) {
|
|
187
|
-
return { SubscriptionArn: subscriptionArnMatch[1] };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Fallback: return raw text for unknown responses
|
|
191
|
-
return { raw: xmlText };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Global SNS client instance
|
|
196
|
-
let defaultClient = null;
|
|
197
|
-
|
|
198
|
-
// Create SNS client
|
|
199
|
-
export function createClient(options = {}) {
|
|
200
|
-
defaultClient = new SNSClient(options);
|
|
201
|
-
return defaultClient;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Send SMS function
|
|
205
|
-
export function sendSMS(textmessage, phone, senderid, SMSType, callback, client = null) {
|
|
206
|
-
const snsClient = client || defaultClient;
|
|
207
|
-
|
|
208
|
-
if (!snsClient) {
|
|
209
|
-
return callback({ err: new Error('SNS client not initialized. Call createClient() first.') });
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const params = {
|
|
213
|
-
Message: textmessage,
|
|
214
|
-
PhoneNumber: phone,
|
|
215
|
-
'MessageAttributes.entry.1.Name': 'AWS.SNS.SMS.SenderID',
|
|
216
|
-
'MessageAttributes.entry.1.Value.DataType': 'String',
|
|
217
|
-
'MessageAttributes.entry.1.Value.StringValue': senderid,
|
|
218
|
-
'MessageAttributes.entry.2.Name': 'AWS.SNS.SMS.SMSType',
|
|
219
|
-
'MessageAttributes.entry.2.Value.DataType': 'String',
|
|
220
|
-
'MessageAttributes.entry.2.Value.StringValue': SMSType
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
snsClient.makeRequest('Publish', params)
|
|
224
|
-
.then(response => {
|
|
225
|
-
callback(undefined, response.MessageId);
|
|
226
|
-
})
|
|
227
|
-
.catch(error => {
|
|
228
|
-
callback({ err: error, 'err.stack': error.stack });
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Export for compatibility
|
|
233
|
-
export default {
|
|
234
|
-
createClient,
|
|
235
|
-
sendSMS
|
|
236
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|