UNPKG

@harishreddym/baqend

Version:

Baqend JavaScript SDK

228 lines (202 loc) 6.4 kB
'use strict'; const hmac = require('./hmac').hmac; const deprectated = require('./deprecated'); /** * @interface util.TokenStorageFactory */ /** * Creates a new tokenStorage which persist tokens for the given origin * @param {string} origin The origin where the token contains to * @return {Promise<TokenStorage>} The initialized token storage * @name create * @memberOf util.TokenStorageFactory.prototype * @method */ /** * @alias util.TokenStorage */ class TokenStorage { /** * Parse a token string in its components * @param {string} token The token string to parse, time values are returned as timestamps * @return {{data: string, createdAt: int, expireAt: int, sig: string}} */ static parse(token) { return { val: token, createdAt: parseInt(token.substring(0, 8), 16) * 1000, expireAt: parseInt(token.substring(8, 16), 16) * 1000, sig: token.substring(token.length - 40), data: token.substring(0, token.length - 40), }; } /** * Get the stored token * @return {string} The token or undefined, if no token is available */ get token() { return this.tokenData ? this.tokenData.val : null; } static create(origin) { return Promise.resolve(new TokenStorage(origin)); } /** * @param {string} origin The origin where the token belongs to * @param {string} token The initial token * @param {boolean=} temporary If the token should be saved temporary or permanently */ constructor(origin, token, temporary) { /** * The actual stored token */ this.tokenData = token ? TokenStorage.parse(token) : null; this.origin = origin; /** * Indicates if the token should keep temporary only or should be persisted for later sessions * @type boolean */ this.temporary = temporary; } /** * Use the underlying storage implementation to save the token * @param {string} origin The origin where the token belongs to * @param {string} token The initial token * @param {boolean} temporary If the token should be saved temporary or permanently * @return {void} * @protected * @abstract */ saveToken(origin, token, temporary) { // eslint-disable-next-line no-underscore-dangle if (this._saveToken !== TokenStorage.prototype._saveToken) { // eslint-disable-next-line no-console console.log('Using deprecated TokenStorage._saveToken implementation.'); // eslint-disable-next-line no-underscore-dangle this._saveToken(origin, token, temporary); } } /** * Use the underlying storage implementation to save the token * @param {string} origin The origin where the token belongs to * @param {string} token The initial token * @param {boolean} temporary If the token should be saved temporary or permanently * @return {void} * @deprecated Use TokenStorage#saveToken instead * @protected * @abstract */ _saveToken(origin, token, temporary) {} // eslint-disable-line no-unused-vars /** * Update the token for the givin origin, the operation may be asynchronous * @param {String} token The token to store or <code>null</code> to remove the token * @return {void} */ update(token) { const t = token ? TokenStorage.parse(token) : null; if (this.tokenData && t && this.tokenData.expireAt > t.expireAt) { // an older token was fetched from the cache, so ignore it return; } this.tokenData = t; this.saveToken(this.origin, token, this.temporary); } /** * Derives a resource token from the stored origin token and signs the resource with the generated resource token * * @param {string} resource The resource which will be accessible with the returned token * @return {string} A resource token which can only be used to access the specified resource */ signPath(resource) { if (this.tokenData) { const path = resource.split('/').map(encodeURIComponent).join('/'); return path + '?BAT=' + (this.tokenData.data + hmac(path + this.tokenData.data, this.tokenData.sig)); } return resource; } } deprectated(TokenStorage.prototype, '_token', 'tokenData'); deprectated(TokenStorage.prototype, '_origin', 'origin'); const tokens = {}; /** * @ignore */ class GlobalStorage extends TokenStorage { /** * Creates a global token storage instance for the given origin * A global token storage use a global variable to store the actual origin tokens * @param origin * @return {Promise.<GlobalStorage>} */ static create(origin) { return Promise.resolve(new GlobalStorage(origin, tokens[origin])); } /** * @inheritDoc */ saveToken(origin, token, temporary) { if (!temporary) { if (token) { tokens[origin] = token; } else { delete tokens[origin]; } } } } /** * @alias util.TokenStorage.GLOBAL * @type {util.TokenStorageFactory} */ TokenStorage.GLOBAL = GlobalStorage; /** * @ignore */ class WebStorage extends TokenStorage { static isAvailable() { try { // firefox throws an exception if cookies are disabled if (typeof localStorage === 'undefined') { return false; } localStorage.setItem('bq_webstorage_test', 'bq'); localStorage.removeItem('bq_webstorage_test'); return true; } catch (e) { return false; } } /** * Creates a global web storage instance for the given origin * A web token storage use the localStorage or sessionStorage to store the origin tokens * @param origin * @return {Promise.<WebStorage>} */ static create(origin) { let temporary = false; let token = localStorage.getItem('BAT:' + origin); if (!token && typeof sessionStorage !== 'undefined') { token = sessionStorage.getItem('BAT:' + origin); temporary = !!token; } return Promise.resolve(new WebStorage(origin, token, temporary)); } /** * @inheritDoc */ saveToken(origin, token, temporary) { const webStorage = temporary ? sessionStorage : localStorage; if (token) { webStorage.setItem('BAT:' + origin, token); } else { webStorage.removeItem('BAT:' + origin); } } } if (WebStorage.isAvailable()) { /** * @alias util.TokenStorage.WEB_STORAGE * @type {util.TokenStorageFactory} */ TokenStorage.WEB_STORAGE = WebStorage; } module.exports = TokenStorage;