doubaoserver/AccessToken.js

241 lines
6.3 KiB
JavaScript

var crypto = require('crypto');
var randomInt = Math.floor(Math.random() * 0xFFFFFFFF);
const VERSION = "001";
const VERSION_LENGTH = 3;
const APP_ID_LENGTH = 24;
privileges = {
PrivPublishStream: 0,
// not exported, do not use directly
privPublishAudioStream: 1,
privPublishVideoStream: 2,
privPublishDataStream: 3,
PrivSubscribeStream: 4,
};
module.exports.privileges = privileges;
// Initializes token struct by required parameters.
var AccessToken = function (appID, appKey, roomID, userID) {
let token = this;
this.appID = appID;
this.appKey = appKey;
this.roomID = roomID;
this.userID = userID;
this.issuedAt = Math.floor(new Date() / 1000);
this.nonce = randomInt;
this.expireAt = 0;
this.privileges = {};
// AddPrivilege adds permission for token with an expiration.
this.addPrivilege = function (privilege, expireTimestamp) {
if (token.privileges === undefined) {
token.privileges = {}
}
token.privileges[privilege] = expireTimestamp;
if (privilege === privileges.PrivPublishStream) {
token.privileges[privileges.privPublishVideoStream] = expireTimestamp;
token.privileges[privileges.privPublishAudioStream] = expireTimestamp;
token.privileges[privileges.privPublishDataStream] = expireTimestamp;
}
};
// ExpireTime sets token expire time, won't expire by default.
// The token will be invalid after expireTime no matter what privilege's expireTime is.
this.expireTime = function (expireTimestamp) {
token.expireAt = expireTimestamp;
};
this.packMsg = function () {
var bufM = new ByteBuf();
bufM.putUint32(token.nonce);
bufM.putUint32(token.issuedAt);
bufM.putUint32(token.expireAt);
bufM.putString(token.roomID);
bufM.putString(token.userID);
bufM.putTreeMapUInt32(token.privileges);
return bufM.pack()
};
// Serialize generates the token string
this.serialize = function () {
var bytesM = this.packMsg();
var signature = encodeHMac(token.appKey, bytesM);
var content = new ByteBuf().putBytes(bytesM).putBytes(signature).pack();
return (VERSION + token.appID + content.toString('base64'));
};
// Verify checks if this token valid, called by server side.
this.verify = function (key) {
if (token.expireAt > 0 && Math.floor(new Date() / 1000) > token.expireAt) {
return false
}
token.appKey = key;
return encodeHMac(token.appKey, this.packMsg()).toString() === token.signature;
}
};
// Parse retrieves token information from raw string
var Parse = function (raw) {
try {
if (raw.length <= VERSION_LENGTH + APP_ID_LENGTH) {
return
}
if (raw.substr(0, VERSION_LENGTH) !== VERSION) {
return
}
var token = new AccessToken("", "", "", "");
token.appID = raw.substr(VERSION_LENGTH, APP_ID_LENGTH);
var contentBuf = Buffer.from(raw.substr(VERSION_LENGTH + APP_ID_LENGTH), 'base64');
var readbuf = new ReadByteBuf(contentBuf);
var msg = readbuf.getString();
token.signature = readbuf.getString().toString();
// parse msg
var msgBuf = new ReadByteBuf(msg);
token.nonce = msgBuf.getUint32();
token.issuedAt = msgBuf.getUint32();
token.expireAt = msgBuf.getUint32();
token.roomID = msgBuf.getString().toString();
token.userID = msgBuf.getString().toString();
token.privileges = msgBuf.getTreeMapUInt32();
return token
} catch (err) {
console.log(err);
}
};
module.exports.version = VERSION;
module.exports.AccessToken = AccessToken;
module.exports.Parse = Parse;
var encodeHMac = function (key, message) {
return crypto.createHmac('sha256', key).update(message).digest();
};
var ByteBuf = function () {
var that = {
buffer: Buffer.alloc(1024)
, position: 0
};
// that.buffer.fill(0);
that.pack = function () {
var out = Buffer.alloc(that.position);
that.buffer.copy(out, 0, 0, out.length);
return out;
};
that.putUint16 = function (v) {
that.buffer.writeUInt16LE(v, that.position);
that.position += 2;
return that;
};
that.putUint32 = function (v) {
that.buffer.writeUInt32LE(v, that.position);
that.position += 4;
return that;
};
that.putBytes = function (bytes) {
that.putUint16(bytes.length);
bytes.copy(that.buffer, that.position);
that.position += bytes.length;
return that;
};
that.putString = function (str) {
return that.putBytes(Buffer.from(str));
};
that.putTreeMap = function (map) {
if (!map) {
that.putUint16(0);
return that;
}
that.putUint16(Object.keys(map).length);
for (var key in map) {
that.putUint16(key);
that.putString(map[key]);
}
return that;
};
that.putTreeMapUInt32 = function (map) {
if (!map) {
that.putUint16(0);
return that;
}
that.putUint16(Object.keys(map).length);
for (var key in map) {
that.putUint16(key);
that.putUint32(map[key]);
}
return that;
};
return that;
};
var ReadByteBuf = function (bytes) {
var that = {
buffer: bytes
, position: 0
};
that.getUint16 = function () {
var ret = that.buffer.readUInt16LE(that.position);
that.position += 2;
return ret;
};
that.getUint32 = function () {
var ret = that.buffer.readUInt32LE(that.position);
that.position += 4;
return ret;
};
that.getString = function () {
var len = that.getUint16();
var out = Buffer.alloc(len);
that.buffer.copy(out, 0, that.position, (that.position + len));
that.position += len;
return out;
};
that.getTreeMapUInt32 = function () {
var map = {};
var len = that.getUint16();
for (var i = 0; i < len; i++) {
var key = that.getUint16();
var value = that.getUint32();
map[key] = value;
}
return map;
};
return that;
};