agora-token 2.0.4 → 2.0.6
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/.devcontainer/devcontainer.json +22 -0
- package/.prettierrc +12 -0
- package/index.d.ts +260 -26
- package/index.js +4 -1
- package/package.json +1 -1
- package/sample/AccessToken2Sample.js +29 -19
- package/sample/ConvoAITokenBuilderSample.js +36 -0
- package/sample/RtcTokenBuilder2Sample.js +16 -0
- package/sample/SttTokenBuilderSample.js +36 -0
- package/src/AccessToken2.js +366 -40
- package/src/ApaasTokenBuilder.js +9 -3
- package/src/ChatTokenBuilder.js +6 -2
- package/src/ConvoAITokenBuilder.js +68 -0
- package/src/EducationTokenBuilder.js +9 -3
- package/src/FpaTokenBuilder.js +1 -1
- package/src/RtcTokenBuilder2.js +124 -16
- package/src/RtmTokenBuilder2.js +25 -1
- package/src/SttTokenBuilder.js +68 -0
- package/test/AccessToken2Test.js +331 -4
- package/test/AccessTokenTest.js +62 -1
- package/test/ApaasTokenBuilderTest.js +8 -4
- package/test/ChatTokenBuilderTest.js +6 -4
- package/test/ConvoAITokenBuilderTest.js +58 -0
- package/test/DynamicKeyTest.js +4 -0
- package/test/EducationTokenBuilderTest.js +8 -4
- package/test/FpaTokenBuilderTest.js +2 -1
- package/test/RtcTokenBuilder2Test.js +74 -30
- package/test/RtmTokenBuilder2Test.js +53 -4
- package/test/RtmTokenBuilderTest.js +1 -0
- package/test/SttTokenBuilderTest.js +58 -0
- package/sample.js +0 -46
package/src/AccessToken2.js
CHANGED
|
@@ -1,42 +1,55 @@
|
|
|
1
1
|
var crypto = require('crypto')
|
|
2
2
|
const zlib = require('zlib')
|
|
3
3
|
const VERSION_LENGTH = 3
|
|
4
|
-
const APP_ID_LENGTH = 32
|
|
5
4
|
|
|
5
|
+
// Returns the Token007 version prefix.
|
|
6
6
|
const getVersion = () => {
|
|
7
7
|
return '007'
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// Returns whether a value is a 32-character hexadecimal identifier.
|
|
11
|
+
const isUuid = value => {
|
|
12
|
+
return value !== null && typeof value !== 'undefined' && /^[0-9a-fA-F]{32}$/.test(value.toString())
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Represents the common service type and privilege payload.
|
|
10
16
|
class Service {
|
|
17
|
+
// Creates a service with the specified numeric type.
|
|
11
18
|
constructor(service_type) {
|
|
12
19
|
this.__type = service_type
|
|
13
20
|
this.__privileges = {}
|
|
14
21
|
}
|
|
15
22
|
|
|
23
|
+
// Serializes the numeric service type.
|
|
16
24
|
__pack_type() {
|
|
17
25
|
let buf = new ByteBuf()
|
|
18
26
|
buf.putUint16(this.__type)
|
|
19
27
|
return buf.pack()
|
|
20
28
|
}
|
|
21
29
|
|
|
30
|
+
// Serializes the service privilege map.
|
|
22
31
|
__pack_privileges() {
|
|
23
32
|
let buf = new ByteBuf()
|
|
24
33
|
buf.putTreeMapUInt32(this.__privileges)
|
|
25
34
|
return buf.pack()
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
// Returns the numeric service type.
|
|
28
38
|
service_type() {
|
|
29
39
|
return this.__type
|
|
30
40
|
}
|
|
31
41
|
|
|
42
|
+
// Adds or updates a service privilege expiration timestamp.
|
|
32
43
|
add_privilege(privilege, expire) {
|
|
33
44
|
this.__privileges[privilege] = expire
|
|
34
45
|
}
|
|
35
46
|
|
|
47
|
+
// Serializes the service type and privileges.
|
|
36
48
|
pack() {
|
|
37
49
|
return Buffer.concat([this.__pack_type(), this.__pack_privileges()])
|
|
38
50
|
}
|
|
39
51
|
|
|
52
|
+
// Deserializes service privileges after the type has been consumed.
|
|
40
53
|
unpack(buffer) {
|
|
41
54
|
let bufReader = new ReadByteBuf(buffer)
|
|
42
55
|
this.__privileges = bufReader.getTreeMapUInt32()
|
|
@@ -46,19 +59,23 @@ class Service {
|
|
|
46
59
|
|
|
47
60
|
const kRtcServiceType = 1
|
|
48
61
|
|
|
62
|
+
// Represents an RTC service payload.
|
|
49
63
|
class ServiceRtc extends Service {
|
|
64
|
+
// Creates an RTC service for a channel and user ID.
|
|
50
65
|
constructor(channel_name, uid) {
|
|
51
66
|
super(kRtcServiceType)
|
|
52
67
|
this.__channel_name = channel_name
|
|
53
68
|
this.__uid = uid === 0 ? '' : `${uid}`
|
|
54
69
|
}
|
|
55
70
|
|
|
71
|
+
// Serializes the RTC service payload.
|
|
56
72
|
pack() {
|
|
57
73
|
let buffer = new ByteBuf()
|
|
58
74
|
buffer.putString(this.__channel_name).putString(this.__uid)
|
|
59
75
|
return Buffer.concat([super.pack(), buffer.pack()])
|
|
60
76
|
}
|
|
61
77
|
|
|
78
|
+
// Deserializes the RTC service payload.
|
|
62
79
|
unpack(buffer) {
|
|
63
80
|
let bufReader = super.unpack(buffer)
|
|
64
81
|
this.__channel_name = bufReader.getString()
|
|
@@ -74,18 +91,22 @@ ServiceRtc.kPrivilegePublishDataStream = 4
|
|
|
74
91
|
|
|
75
92
|
const kRtmServiceType = 2
|
|
76
93
|
|
|
94
|
+
// Represents an RTM service payload.
|
|
77
95
|
class ServiceRtm extends Service {
|
|
96
|
+
// Creates an RTM service for a user ID.
|
|
78
97
|
constructor(user_id) {
|
|
79
98
|
super(kRtmServiceType)
|
|
80
99
|
this.__user_id = user_id || ''
|
|
81
100
|
}
|
|
82
101
|
|
|
102
|
+
// Serializes the RTM service payload.
|
|
83
103
|
pack() {
|
|
84
104
|
let buffer = new ByteBuf()
|
|
85
105
|
buffer.putString(this.__user_id)
|
|
86
106
|
return Buffer.concat([super.pack(), buffer.pack()])
|
|
87
107
|
}
|
|
88
108
|
|
|
109
|
+
// Deserializes the RTM service payload.
|
|
89
110
|
unpack(buffer) {
|
|
90
111
|
let bufReader = super.unpack(buffer)
|
|
91
112
|
this.__user_id = bufReader.getString()
|
|
@@ -95,17 +116,50 @@ class ServiceRtm extends Service {
|
|
|
95
116
|
|
|
96
117
|
ServiceRtm.kPrivilegeLogin = 1
|
|
97
118
|
|
|
119
|
+
const kStreamingServiceType = 3
|
|
120
|
+
|
|
121
|
+
// Represents a Streaming service payload.
|
|
122
|
+
class ServiceStreaming extends Service {
|
|
123
|
+
// Creates a Streaming service for a channel and numeric user ID or string account.
|
|
124
|
+
constructor(channelName = '', account = '') {
|
|
125
|
+
super(kStreamingServiceType)
|
|
126
|
+
this.__channel_name = channelName
|
|
127
|
+
this.__account = account === 0 ? '' : `${account}`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Serializes the Streaming service payload.
|
|
131
|
+
pack() {
|
|
132
|
+
const buffer = new ByteBuf().putString(this.__channel_name).putString(this.__account)
|
|
133
|
+
return Buffer.concat([super.pack(), buffer.pack()])
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Deserializes the Streaming service payload.
|
|
137
|
+
unpack(buffer) {
|
|
138
|
+
const reader = super.unpack(buffer)
|
|
139
|
+
this.__channel_name = reader.getString()
|
|
140
|
+
this.__account = reader.getString()
|
|
141
|
+
return reader
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
ServiceStreaming.kPrivilegePublishMixStream = 1
|
|
146
|
+
ServiceStreaming.kPrivilegePublishRawStream = 2
|
|
147
|
+
|
|
98
148
|
const kFpaServiceType = 4
|
|
99
149
|
|
|
150
|
+
// Represents an FPA service payload.
|
|
100
151
|
class ServiceFpa extends Service {
|
|
152
|
+
// Creates an FPA service.
|
|
101
153
|
constructor() {
|
|
102
154
|
super(kFpaServiceType)
|
|
103
155
|
}
|
|
104
156
|
|
|
157
|
+
// Serializes the FPA service payload.
|
|
105
158
|
pack() {
|
|
106
159
|
return super.pack()
|
|
107
160
|
}
|
|
108
161
|
|
|
162
|
+
// Deserializes the FPA service payload.
|
|
109
163
|
unpack(buffer) {
|
|
110
164
|
let bufReader = super.unpack(buffer)
|
|
111
165
|
return bufReader
|
|
@@ -114,20 +168,64 @@ class ServiceFpa extends Service {
|
|
|
114
168
|
|
|
115
169
|
ServiceFpa.kPrivilegeLogin = 1
|
|
116
170
|
|
|
171
|
+
const kConvoAIServiceType = 9
|
|
172
|
+
|
|
173
|
+
// Represents a ConvoAI service payload.
|
|
174
|
+
class ServiceConvoAI extends Service {
|
|
175
|
+
// Creates a ConvoAI service.
|
|
176
|
+
constructor() {
|
|
177
|
+
super(kConvoAIServiceType)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Serializes the ConvoAI service payload.
|
|
181
|
+
pack() {
|
|
182
|
+
return super.pack()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Deserializes the ConvoAI service payload.
|
|
186
|
+
unpack(buffer) {
|
|
187
|
+
return super.unpack(buffer)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const kSttServiceType = 10
|
|
192
|
+
|
|
193
|
+
// Represents an STT service payload.
|
|
194
|
+
class ServiceStt extends Service {
|
|
195
|
+
// Creates an STT service.
|
|
196
|
+
constructor() {
|
|
197
|
+
super(kSttServiceType)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Serializes the STT service payload.
|
|
201
|
+
pack() {
|
|
202
|
+
return super.pack()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Deserializes the STT service payload.
|
|
206
|
+
unpack(buffer) {
|
|
207
|
+
return super.unpack(buffer)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
117
211
|
const kChatServiceType = 5
|
|
118
212
|
|
|
213
|
+
// Represents a Chat service payload.
|
|
119
214
|
class ServiceChat extends Service {
|
|
215
|
+
// Creates a Chat service for a user ID.
|
|
120
216
|
constructor(user_id) {
|
|
121
217
|
super(kChatServiceType)
|
|
122
218
|
this.__user_id = user_id || ''
|
|
123
219
|
}
|
|
124
220
|
|
|
221
|
+
// Serializes the Chat service payload.
|
|
125
222
|
pack() {
|
|
126
223
|
let buffer = new ByteBuf()
|
|
127
224
|
buffer.putString(this.__user_id)
|
|
128
225
|
return Buffer.concat([super.pack(), buffer.pack()])
|
|
129
226
|
}
|
|
130
227
|
|
|
228
|
+
// Deserializes the Chat service payload.
|
|
131
229
|
unpack(buffer) {
|
|
132
230
|
let bufReader = super.unpack(buffer)
|
|
133
231
|
this.__user_id = bufReader.getString()
|
|
@@ -138,9 +236,40 @@ class ServiceChat extends Service {
|
|
|
138
236
|
ServiceChat.kPrivilegeUser = 1
|
|
139
237
|
ServiceChat.kPrivilegeApp = 2
|
|
140
238
|
|
|
239
|
+
const kFCdnServiceType = 6
|
|
240
|
+
|
|
241
|
+
// Represents an FCDN service payload.
|
|
242
|
+
class ServiceFCdn extends Service {
|
|
243
|
+
// Creates an FCDN service for a channel and numeric user ID or string account.
|
|
244
|
+
constructor(channelName = '', account = '') {
|
|
245
|
+
super(kFCdnServiceType)
|
|
246
|
+
this.__channel_name = channelName
|
|
247
|
+
this.__account = account === 0 ? '' : `${account}`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Serializes the FCDN service payload.
|
|
251
|
+
pack() {
|
|
252
|
+
const buffer = new ByteBuf().putString(this.__channel_name).putString(this.__account)
|
|
253
|
+
return Buffer.concat([super.pack(), buffer.pack()])
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Deserializes the FCDN service payload.
|
|
257
|
+
unpack(buffer) {
|
|
258
|
+
const reader = super.unpack(buffer)
|
|
259
|
+
this.__channel_name = reader.getString()
|
|
260
|
+
this.__account = reader.getString()
|
|
261
|
+
return reader
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
ServiceFCdn.kPrivilegePublish = 1
|
|
266
|
+
ServiceFCdn.kPrivilegePlay = 2
|
|
267
|
+
|
|
141
268
|
const kApaasServiceType = 7
|
|
142
269
|
|
|
270
|
+
// Represents an APaaS service payload.
|
|
143
271
|
class ServiceApaas extends Service {
|
|
272
|
+
// Creates an APaaS service for a room, user, and role.
|
|
144
273
|
constructor(roomUuid, userUuid, role) {
|
|
145
274
|
super(kApaasServiceType)
|
|
146
275
|
this.__room_uuid = roomUuid || ''
|
|
@@ -148,6 +277,7 @@ class ServiceApaas extends Service {
|
|
|
148
277
|
this.__role = role || -1
|
|
149
278
|
}
|
|
150
279
|
|
|
280
|
+
// Serializes the APaaS service payload.
|
|
151
281
|
pack() {
|
|
152
282
|
let buffer = new ByteBuf()
|
|
153
283
|
buffer.putString(this.__room_uuid)
|
|
@@ -156,6 +286,7 @@ class ServiceApaas extends Service {
|
|
|
156
286
|
return Buffer.concat([super.pack(), buffer.pack()])
|
|
157
287
|
}
|
|
158
288
|
|
|
289
|
+
// Deserializes the APaaS service payload.
|
|
159
290
|
unpack(buffer) {
|
|
160
291
|
let bufReader = super.unpack(buffer)
|
|
161
292
|
this.__room_uuid = bufReader.getString()
|
|
@@ -169,7 +300,87 @@ ServiceApaas.PRIVILEGE_ROOM_USER = 1
|
|
|
169
300
|
ServiceApaas.PRIVILEGE_USER = 2
|
|
170
301
|
ServiceApaas.PRIVILEGE_APP = 3
|
|
171
302
|
|
|
303
|
+
// Stores RTM2 resource-level permissions.
|
|
304
|
+
class Rtm2Permissions {
|
|
305
|
+
// Creates an empty RTM2 permission set.
|
|
306
|
+
constructor() {
|
|
307
|
+
this.details = {}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Adds or replaces resources for a resource and permission type.
|
|
311
|
+
add(resourceType, permissionType, resources) {
|
|
312
|
+
if (!this.details[resourceType]) {
|
|
313
|
+
this.details[resourceType] = {}
|
|
314
|
+
}
|
|
315
|
+
this.details[resourceType][permissionType] = Array.from(resources)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
Rtm2Permissions.kMessageChannels = 0
|
|
320
|
+
Rtm2Permissions.kStreamChannels = 1
|
|
321
|
+
Rtm2Permissions.kGroupChannels = 2
|
|
322
|
+
Rtm2Permissions.kServerGroups = 3
|
|
323
|
+
Rtm2Permissions.kUsers = 4
|
|
324
|
+
Rtm2Permissions.kRead = 0
|
|
325
|
+
Rtm2Permissions.kWrite = 1
|
|
326
|
+
|
|
327
|
+
const kRtm2ServiceType = 8
|
|
328
|
+
|
|
329
|
+
// Represents an RTM2 service payload.
|
|
330
|
+
class ServiceRtm2 extends Service {
|
|
331
|
+
// Creates an RTM2 service for a user and permission set.
|
|
332
|
+
constructor(userId, permissions) {
|
|
333
|
+
super(kRtm2ServiceType)
|
|
334
|
+
this.__user_id = userId === 0 || userId == null ? '' : String(userId)
|
|
335
|
+
this.__permissions = permissions || new Rtm2Permissions()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Serializes the RTM2 service payload.
|
|
339
|
+
pack() {
|
|
340
|
+
const buffer = new ByteBuf().putString(this.__user_id)
|
|
341
|
+
const resourceTypes = Object.keys(this.__permissions.details).map(Number).sort((left, right) => left - right)
|
|
342
|
+
buffer.putUint16(resourceTypes.length)
|
|
343
|
+
resourceTypes.forEach(resourceType => {
|
|
344
|
+
const permissionMap = this.__permissions.details[resourceType]
|
|
345
|
+
const permissionTypes = Object.keys(permissionMap).map(Number).sort((left, right) => left - right)
|
|
346
|
+
buffer.putUint16(resourceType).putUint16(permissionTypes.length)
|
|
347
|
+
permissionTypes.forEach(permissionType => {
|
|
348
|
+
const resources = permissionMap[permissionType]
|
|
349
|
+
buffer.putUint16(permissionType).putUint16(resources.length)
|
|
350
|
+
resources.forEach(resource => buffer.putString(resource))
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
return Buffer.concat([super.pack(), buffer.pack()])
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Deserializes the RTM2 service payload.
|
|
357
|
+
unpack(buffer) {
|
|
358
|
+
const reader = super.unpack(buffer)
|
|
359
|
+
this.__user_id = reader.getString()
|
|
360
|
+
this.__permissions = new Rtm2Permissions()
|
|
361
|
+
const resourceTypeCount = reader.getUint16()
|
|
362
|
+
for (let i = 0; i < resourceTypeCount; i++) {
|
|
363
|
+
const resourceType = reader.getUint16()
|
|
364
|
+
const permissionCount = reader.getUint16()
|
|
365
|
+
for (let j = 0; j < permissionCount; j++) {
|
|
366
|
+
const permissionType = reader.getUint16()
|
|
367
|
+
const resourceCount = reader.getUint16()
|
|
368
|
+
const resources = []
|
|
369
|
+
for (let k = 0; k < resourceCount; k++) {
|
|
370
|
+
resources.push(reader.getString().toString())
|
|
371
|
+
}
|
|
372
|
+
this.__permissions.add(resourceType, permissionType, resources)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return reader
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
ServiceRtm2.kPrivilegeLogin = 1
|
|
380
|
+
|
|
381
|
+
// Builds, parses, and verifies Token007 tokens containing one or more services.
|
|
172
382
|
class AccessToken2 {
|
|
383
|
+
// Creates a token builder or an empty token parser.
|
|
173
384
|
constructor(appId, appCertificate, issueTs, expire) {
|
|
174
385
|
this.appId = appId
|
|
175
386
|
this.appCertificate = appCertificate
|
|
@@ -177,53 +388,69 @@ class AccessToken2 {
|
|
|
177
388
|
this.expire = expire
|
|
178
389
|
// salt ranges in (1, 99999999)
|
|
179
390
|
this.salt = Math.floor(Math.random() * 99999999) + 1
|
|
180
|
-
this.services =
|
|
391
|
+
this.services = []
|
|
392
|
+
this.__signature = Buffer.alloc(0)
|
|
393
|
+
this.__signing_info = Buffer.alloc(0)
|
|
394
|
+
this.__parsed = false
|
|
181
395
|
}
|
|
182
396
|
|
|
183
|
-
|
|
184
|
-
|
|
397
|
+
// Derives the signing key with the stored or supplied App Certificate.
|
|
398
|
+
__signing(appCertificate = this.appCertificate) {
|
|
399
|
+
let signing = encodeHMac(new ByteBuf().putUint32(this.issueTs).pack(), appCertificate)
|
|
185
400
|
signing = encodeHMac(new ByteBuf().putUint32(this.salt).pack(), signing)
|
|
186
401
|
return signing
|
|
187
402
|
}
|
|
188
403
|
|
|
404
|
+
// Validates the fields required to build a token.
|
|
189
405
|
__build_check() {
|
|
190
|
-
let is_uuid = data => {
|
|
191
|
-
if (data.length !== APP_ID_LENGTH) {
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
let buf = Buffer.from(data, 'hex')
|
|
195
|
-
return !!buf
|
|
196
|
-
}
|
|
197
|
-
|
|
198
406
|
const { appId, appCertificate, services } = this
|
|
199
|
-
if (!
|
|
407
|
+
if (!isUuid(appId) || !isUuid(appCertificate)) {
|
|
200
408
|
return false
|
|
201
409
|
}
|
|
202
410
|
|
|
203
|
-
if (
|
|
411
|
+
if (services.length === 0) {
|
|
204
412
|
return false
|
|
205
413
|
}
|
|
206
414
|
return true
|
|
207
415
|
}
|
|
208
416
|
|
|
417
|
+
// Returns services in stable type order while preserving duplicate insertion order.
|
|
418
|
+
__services_for_packing() {
|
|
419
|
+
return this.services
|
|
420
|
+
.map((service, index) => ({ service, index }))
|
|
421
|
+
.sort((left, right) => {
|
|
422
|
+
const typeDifference = left.service.service_type() - right.service.service_type()
|
|
423
|
+
return typeDifference || left.index - right.index
|
|
424
|
+
})
|
|
425
|
+
.map(entry => entry.service)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Adds a service without replacing services of the same type.
|
|
209
429
|
add_service(service) {
|
|
210
|
-
this.services
|
|
430
|
+
this.services.push(service)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Returns all services of the requested type in insertion or token order.
|
|
434
|
+
getServices(serviceType) {
|
|
435
|
+
return this.services.filter(service => service.service_type() === serviceType)
|
|
211
436
|
}
|
|
212
437
|
|
|
438
|
+
// Builds a Token007 token containing all added services.
|
|
213
439
|
build() {
|
|
214
440
|
if (!this.__build_check()) {
|
|
215
441
|
return ''
|
|
216
442
|
}
|
|
217
443
|
|
|
218
444
|
let signing = this.__signing()
|
|
445
|
+
const services = this.__services_for_packing()
|
|
219
446
|
let signing_info = new ByteBuf()
|
|
220
447
|
.putString(this.appId)
|
|
221
448
|
.putUint32(this.issueTs)
|
|
222
449
|
.putUint32(this.expire)
|
|
223
450
|
.putUint32(this.salt)
|
|
224
|
-
.putUint16(
|
|
451
|
+
.putUint16(services.length)
|
|
225
452
|
.pack()
|
|
226
|
-
|
|
453
|
+
services.forEach(service => {
|
|
227
454
|
signing_info = Buffer.concat([signing_info, service.pack()])
|
|
228
455
|
})
|
|
229
456
|
|
|
@@ -233,38 +460,82 @@ class AccessToken2 {
|
|
|
233
460
|
return `${getVersion()}${Buffer.from(compressed).toString('base64')}`
|
|
234
461
|
}
|
|
235
462
|
|
|
463
|
+
// Parses known services and retains the original bytes for signature verification.
|
|
236
464
|
from_string(origin_token) {
|
|
237
|
-
|
|
465
|
+
// Clear the previous token state so a failed parse cannot reuse its signature or services.
|
|
466
|
+
this.appId = ''
|
|
467
|
+
this.issueTs = 0
|
|
468
|
+
this.expire = 0
|
|
469
|
+
this.salt = 0
|
|
470
|
+
this.services = []
|
|
471
|
+
this.__signature = Buffer.alloc(0)
|
|
472
|
+
this.__signing_info = Buffer.alloc(0)
|
|
473
|
+
this.__parsed = false
|
|
474
|
+
|
|
475
|
+
if (typeof origin_token !== 'string' || origin_token.length < VERSION_LENGTH) {
|
|
476
|
+
return false
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const origin_version = origin_token.substring(0, VERSION_LENGTH)
|
|
238
480
|
if (origin_version !== getVersion()) {
|
|
239
481
|
return false
|
|
240
482
|
}
|
|
241
483
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
let
|
|
257
|
-
let
|
|
258
|
-
|
|
259
|
-
|
|
484
|
+
try {
|
|
485
|
+
const origin_content = origin_token.substring(VERSION_LENGTH)
|
|
486
|
+
const buffer = zlib.inflateSync(Buffer.from(origin_content, 'base64'))
|
|
487
|
+
const bufferReader = new ReadByteBuf(buffer)
|
|
488
|
+
|
|
489
|
+
this.__signature = Buffer.from(bufferReader.getString())
|
|
490
|
+
this.__signing_info = Buffer.from(bufferReader.pack())
|
|
491
|
+
this.services = []
|
|
492
|
+
this.appId = bufferReader.getString()
|
|
493
|
+
this.issueTs = bufferReader.getUint32()
|
|
494
|
+
this.expire = bufferReader.getUint32()
|
|
495
|
+
this.salt = bufferReader.getUint32()
|
|
496
|
+
const service_count = bufferReader.getUint16()
|
|
497
|
+
|
|
498
|
+
let remainBuf = bufferReader.pack()
|
|
499
|
+
for (let i = 0; i < service_count; i++) {
|
|
500
|
+
const bufferReaderService = new ReadByteBuf(remainBuf)
|
|
501
|
+
const service_type = bufferReaderService.getUint16()
|
|
502
|
+
const ServiceClass = AccessToken2.kServices[service_type]
|
|
503
|
+
if (!ServiceClass) {
|
|
504
|
+
this.__parsed = true
|
|
505
|
+
return true
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const service = new ServiceClass()
|
|
509
|
+
remainBuf = service.unpack(bufferReaderService.pack()).pack()
|
|
510
|
+
this.add_service(service)
|
|
511
|
+
}
|
|
512
|
+
} catch (error) {
|
|
513
|
+
return false
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
this.__parsed = true
|
|
517
|
+
return true
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Verifies the signature of a successfully parsed token.
|
|
521
|
+
verifySignature(appCertificate) {
|
|
522
|
+
if (!this.__parsed || this.__signature.length === 0 || this.__signing_info.length === 0
|
|
523
|
+
|| !isUuid(this.appId) || !isUuid(appCertificate)) {
|
|
524
|
+
return false
|
|
260
525
|
}
|
|
526
|
+
|
|
527
|
+
const signature = encodeHMac(this.__signing(appCertificate), this.__signing_info)
|
|
528
|
+
return this.__signature.length === signature.length
|
|
529
|
+
&& crypto.timingSafeEqual(this.__signature, signature)
|
|
261
530
|
}
|
|
262
531
|
}
|
|
263
532
|
|
|
533
|
+
// Returns a SHA-256 HMAC digest.
|
|
264
534
|
var encodeHMac = function (key, message) {
|
|
265
535
|
return crypto.createHmac('sha256', key).update(message).digest()
|
|
266
536
|
}
|
|
267
537
|
|
|
538
|
+
// Creates a little-endian token buffer writer.
|
|
268
539
|
var ByteBuf = function () {
|
|
269
540
|
var that = {
|
|
270
541
|
buffer: Buffer.alloc(1024),
|
|
@@ -273,46 +544,76 @@ var ByteBuf = function () {
|
|
|
273
544
|
|
|
274
545
|
that.buffer.fill(0)
|
|
275
546
|
|
|
547
|
+
// Grows the backing buffer when the next value does not fit.
|
|
548
|
+
var ensureCapacity = function (additionalLength) {
|
|
549
|
+
const requiredLength = that.position + additionalLength
|
|
550
|
+
if (requiredLength <= that.buffer.length) {
|
|
551
|
+
return
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
let capacity = that.buffer.length
|
|
555
|
+
while (capacity < requiredLength) {
|
|
556
|
+
capacity *= 2
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const expanded = Buffer.alloc(capacity)
|
|
560
|
+
that.buffer.copy(expanded, 0, 0, that.position)
|
|
561
|
+
that.buffer = expanded
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Returns the bytes written to the buffer.
|
|
276
565
|
that.pack = function () {
|
|
277
566
|
var out = Buffer.alloc(that.position)
|
|
278
567
|
that.buffer.copy(out, 0, 0, out.length)
|
|
279
568
|
return out
|
|
280
569
|
}
|
|
281
570
|
|
|
571
|
+
// Appends an unsigned 16-bit integer.
|
|
282
572
|
that.putUint16 = function (v) {
|
|
573
|
+
ensureCapacity(2)
|
|
283
574
|
that.buffer.writeUInt16LE(v, that.position)
|
|
284
575
|
that.position += 2
|
|
285
576
|
return that
|
|
286
577
|
}
|
|
287
578
|
|
|
579
|
+
// Appends an unsigned 32-bit integer.
|
|
288
580
|
that.putUint32 = function (v) {
|
|
581
|
+
ensureCapacity(4)
|
|
289
582
|
that.buffer.writeUInt32LE(v, that.position)
|
|
290
583
|
that.position += 4
|
|
291
584
|
return that
|
|
292
585
|
}
|
|
586
|
+
// Appends a signed 32-bit integer.
|
|
293
587
|
that.putInt32 = function (v) {
|
|
588
|
+
ensureCapacity(4)
|
|
294
589
|
that.buffer.writeInt32LE(v, that.position)
|
|
295
590
|
that.position += 4
|
|
296
591
|
return that
|
|
297
592
|
}
|
|
298
593
|
|
|
594
|
+
// Appends a signed 16-bit integer.
|
|
299
595
|
that.putInt16 = function (v) {
|
|
596
|
+
ensureCapacity(2)
|
|
300
597
|
that.buffer.writeInt16LE(v, that.position)
|
|
301
598
|
that.position += 2
|
|
302
599
|
return that
|
|
303
600
|
}
|
|
304
601
|
|
|
602
|
+
// Appends length-prefixed bytes.
|
|
305
603
|
that.putBytes = function (bytes) {
|
|
306
604
|
that.putUint16(bytes.length)
|
|
605
|
+
ensureCapacity(bytes.length)
|
|
307
606
|
bytes.copy(that.buffer, that.position)
|
|
308
607
|
that.position += bytes.length
|
|
309
608
|
return that
|
|
310
609
|
}
|
|
311
610
|
|
|
611
|
+
// Appends a length-prefixed string.
|
|
312
612
|
that.putString = function (str) {
|
|
313
613
|
return that.putBytes(Buffer.from(str))
|
|
314
614
|
}
|
|
315
615
|
|
|
616
|
+
// Appends a string map in numeric key order.
|
|
316
617
|
that.putTreeMap = function (map) {
|
|
317
618
|
if (!map) {
|
|
318
619
|
that.putUint16(0)
|
|
@@ -328,6 +629,7 @@ var ByteBuf = function () {
|
|
|
328
629
|
return that
|
|
329
630
|
}
|
|
330
631
|
|
|
632
|
+
// Appends an unsigned 32-bit value map in numeric key order.
|
|
331
633
|
that.putTreeMapUInt32 = function (map) {
|
|
332
634
|
if (!map) {
|
|
333
635
|
that.putUint16(0)
|
|
@@ -346,30 +648,35 @@ var ByteBuf = function () {
|
|
|
346
648
|
return that
|
|
347
649
|
}
|
|
348
650
|
|
|
651
|
+
// Creates a little-endian token buffer reader.
|
|
349
652
|
var ReadByteBuf = function (bytes) {
|
|
350
653
|
var that = {
|
|
351
654
|
buffer: bytes,
|
|
352
655
|
position: 0
|
|
353
656
|
}
|
|
354
657
|
|
|
658
|
+
// Reads an unsigned 16-bit integer.
|
|
355
659
|
that.getUint16 = function () {
|
|
356
660
|
var ret = that.buffer.readUInt16LE(that.position)
|
|
357
661
|
that.position += 2
|
|
358
662
|
return ret
|
|
359
663
|
}
|
|
360
664
|
|
|
665
|
+
// Reads an unsigned 32-bit integer.
|
|
361
666
|
that.getUint32 = function () {
|
|
362
667
|
var ret = that.buffer.readUInt32LE(that.position)
|
|
363
668
|
that.position += 4
|
|
364
669
|
return ret
|
|
365
670
|
}
|
|
366
671
|
|
|
672
|
+
// Reads a signed 16-bit integer.
|
|
367
673
|
that.getInt16 = function () {
|
|
368
674
|
var ret = that.buffer.readUInt16LE(that.position)
|
|
369
675
|
that.position += 2
|
|
370
676
|
return ret
|
|
371
677
|
}
|
|
372
678
|
|
|
679
|
+
// Reads length-prefixed bytes.
|
|
373
680
|
that.getString = function () {
|
|
374
681
|
var len = that.getUint16()
|
|
375
682
|
|
|
@@ -379,6 +686,7 @@ var ReadByteBuf = function (bytes) {
|
|
|
379
686
|
return out
|
|
380
687
|
}
|
|
381
688
|
|
|
689
|
+
// Reads an unsigned 32-bit value map.
|
|
382
690
|
that.getTreeMapUInt32 = function () {
|
|
383
691
|
var map = {}
|
|
384
692
|
var len = that.getUint16()
|
|
@@ -390,10 +698,11 @@ var ReadByteBuf = function (bytes) {
|
|
|
390
698
|
return map
|
|
391
699
|
}
|
|
392
700
|
|
|
701
|
+
// Returns unread bytes from the buffer.
|
|
393
702
|
that.pack = function () {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
that.buffer.copy(out, 0, that.position
|
|
703
|
+
const length = that.buffer.length - that.position
|
|
704
|
+
const out = Buffer.alloc(length)
|
|
705
|
+
that.buffer.copy(out, 0, that.position)
|
|
397
706
|
return out
|
|
398
707
|
}
|
|
399
708
|
|
|
@@ -403,20 +712,37 @@ var ReadByteBuf = function (bytes) {
|
|
|
403
712
|
AccessToken2.kServices = {}
|
|
404
713
|
AccessToken2.kServices[kApaasServiceType] = ServiceApaas
|
|
405
714
|
AccessToken2.kServices[kChatServiceType] = ServiceChat
|
|
715
|
+
AccessToken2.kServices[kFCdnServiceType] = ServiceFCdn
|
|
406
716
|
AccessToken2.kServices[kFpaServiceType] = ServiceFpa
|
|
407
717
|
AccessToken2.kServices[kRtcServiceType] = ServiceRtc
|
|
408
718
|
AccessToken2.kServices[kRtmServiceType] = ServiceRtm
|
|
719
|
+
AccessToken2.kServices[kRtm2ServiceType] = ServiceRtm2
|
|
720
|
+
AccessToken2.kServices[kStreamingServiceType] = ServiceStreaming
|
|
721
|
+
AccessToken2.kServices[kConvoAIServiceType] = ServiceConvoAI
|
|
722
|
+
AccessToken2.kServices[kSttServiceType] = ServiceStt
|
|
409
723
|
|
|
410
724
|
module.exports = {
|
|
411
725
|
AccessToken2,
|
|
412
726
|
kApaasServiceType,
|
|
413
727
|
kChatServiceType,
|
|
728
|
+
kConvoAIServiceType,
|
|
729
|
+
kFCdnServiceType,
|
|
414
730
|
kFpaServiceType,
|
|
415
731
|
kRtcServiceType,
|
|
416
732
|
kRtmServiceType,
|
|
733
|
+
kRtm2ServiceType,
|
|
734
|
+
kSttServiceType,
|
|
735
|
+
kStreamingServiceType,
|
|
736
|
+
Rtm2Permissions,
|
|
737
|
+
Service,
|
|
417
738
|
ServiceApaas,
|
|
418
739
|
ServiceChat,
|
|
740
|
+
ServiceConvoAI,
|
|
741
|
+
ServiceFCdn,
|
|
419
742
|
ServiceFpa,
|
|
420
743
|
ServiceRtc,
|
|
421
|
-
ServiceRtm
|
|
744
|
+
ServiceRtm,
|
|
745
|
+
ServiceRtm2,
|
|
746
|
+
ServiceStt,
|
|
747
|
+
ServiceStreaming
|
|
422
748
|
}
|