UNPKG

2.49 kBJavaScriptView Raw
1/**
2 * Authorization Token
3 * @module auth_token
4 */
5
6const crypto = require('crypto');
7const smart_escape = require('./utils/encoding/smart_escape');
8
9const unsafe = /([ "#%&'/:;<=>?@[\]^`{|}~]+)/g;
10
11function digest(message, key) {
12 return crypto.createHmac("sha256", Buffer.from(key, "hex")).update(message).digest('hex');
13}
14
15/**
16 * Escape url using lowercase hex code
17 * @param {string} url a url string
18 * @return {string} escaped url
19 */
20function escapeToLower(url) {
21 const safeUrl = smart_escape(url, unsafe);
22 return safeUrl.replace(/%../g, function (match) {
23 return match.toLowerCase();
24 });
25}
26
27/**
28 * Auth token options
29 * @typedef {object} authTokenOptions
30 * @property {string} [token_name="__cld_token__"] The name of the token.
31 * @property {string} key The secret key required to sign the token.
32 * @property {string} ip The IP address of the client.
33 * @property {number} start_time=now The start time of the token in seconds from epoch.
34 * @property {string} expiration The expiration time of the token in seconds from epoch.
35 * @property {string} duration The duration of the token (from start_time).
36 * @property {string} acl The ACL for the token.
37 * @property {string} url The URL to authentication in case of a URL token.
38 *
39 */
40
41/**
42 * Generate an authorization token
43 * @param {authTokenOptions} options
44 * @returns {string} the authorization token
45 */
46module.exports = function (options) {
47 const tokenName = options.token_name ? options.token_name : "__cld_token__";
48 const tokenSeparator = "~";
49 if (options.expiration == null) {
50 if (options.duration != null) {
51 let start = options.start_time != null ? options.start_time : Math.round(Date.now() / 1000);
52 options.expiration = start + options.duration;
53 } else {
54 throw new Error("Must provide either expiration or duration");
55 }
56 }
57 let tokenParts = [];
58 if (options.ip != null) {
59 tokenParts.push(`ip=${options.ip}`);
60 }
61 if (options.start_time != null) {
62 tokenParts.push(`st=${options.start_time}`);
63 }
64 tokenParts.push(`exp=${options.expiration}`);
65 if (options.acl != null) {
66 tokenParts.push(`acl=${escapeToLower(options.acl)}`);
67 }
68 let toSign = [...tokenParts];
69 if (options.url != null && options.acl == null) {
70 let url = escapeToLower(options.url);
71 toSign.push(`url=${url}`);
72 }
73 let auth = digest(toSign.join(tokenSeparator), options.key);
74 tokenParts.push(`hmac=${auth}`);
75 return `${tokenName}=${tokenParts.join(tokenSeparator)}`;
76};