bsso
Version:
Bloomberg SSO js utilities
160 lines (159 loc) • 6.75 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BSSO = void 0;
const query_string_1 = __importDefault(require("query-string"));
const utils_1 = require("./utils");
class BSSO {
constructor({ clientId, redirectUri, codeVerifier = null, accessToken = null, refreshToken = null, expirationTimestamp = null, }) {
this._clientId = '';
this._redirectUri = '';
this._codeVerifier = '';
this._accessToken = null;
this._refreshToken = null;
this._expirationTimestamp = null;
this._clientId = clientId;
this._redirectUri = encodeURI(redirectUri);
// the verifier is very important, we will need to hold on to it
// after the redirect to verify our identity as per the PKCE workflow
this._codeVerifier = codeVerifier !== null && codeVerifier !== void 0 ? codeVerifier : (0, utils_1.generateRandomString)(43);
// if possible rehydrate
this._accessToken = accessToken;
this._refreshToken = refreshToken;
this._expirationTimestamp = expirationTimestamp;
}
get clientId() {
return this._clientId;
}
get redirectUri() {
return this._redirectUri;
}
get codeVerifier() {
return this._codeVerifier;
}
get accessToken() {
return this._accessToken;
}
get refreshToken() {
return this._refreshToken;
}
get expirationTimestamp() {
return this._expirationTimestamp;
}
createTokenFromRedirectCode(code) {
return __awaiter(this, void 0, void 0, function* () {
const body = new URLSearchParams();
body.append('grant_type', 'authorization_code');
body.append('code', code);
body.append('code_verifier', this.codeVerifier);
body.append('client_id', this.clientId);
body.append('redirect_uri', this.redirectUri);
const rawResponse = yield fetch('https://bsso.blpprofessional.com/as/token.oauth2', {
method: 'POST',
mode: 'cors',
body: body.toString(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return this.handleTokenResponse(rawResponse);
});
}
regenerateToken() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.refreshToken) {
throw new Error('Have to have a refresh token to regenerate the access token.');
}
const body = new URLSearchParams();
body.append('grant_type', 'refresh_token');
body.append('refresh_token', this.refreshToken);
body.append('client_id', this.clientId);
// because we are getting new tokens, clear the currently
// existing ones from memory
this._accessToken = null;
this._refreshToken = null;
const response = yield fetch('https://bsso.blpprofessional.com/as/token.oauth2', {
method: 'POST',
mode: 'cors',
body: body.toString(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return this.handleTokenResponse(response);
});
}
handleTokenResponse(fetchResponse) {
return __awaiter(this, void 0, void 0, function* () {
const regenerated = yield fetchResponse.json();
if (regenerated.error) {
throw new Error(regenerated.error);
}
this._accessToken = regenerated.access_token;
this._refreshToken = regenerated.refresh_token;
this._expirationTimestamp = Date.now() / 1000 + regenerated.expires_in;
return this.accessToken;
});
}
getToken(code) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.accessToken && !this.refreshToken && !code) {
throw new Error('Have to have a redirect code to get token the first time.');
}
if (this.accessToken && !this.isExpired()) {
return Promise.resolve(this.accessToken);
}
if (!this.accessToken && !this.refreshToken && code) {
return this.createTokenFromRedirectCode(code);
}
return this.regenerateToken();
});
}
isExpired() {
if (!this.expirationTimestamp)
return true;
const current = new Date().getTime() / 1000;
return current > this.expirationTimestamp;
}
getRedirectURL(token, scope = 'sapi blpapi-eps') {
return __awaiter(this, void 0, void 0, function* () {
// hash the verifier via SHA-256, the algorithm we specify below as our code_challenge_method
const challengeHash = (0, utils_1.base64UrlEncode)((0, utils_1.sha256)(this.codeVerifier));
return query_string_1.default.stringifyUrl({
url: 'https://bsso.blpprofessional.com/as/authorization.oauth2',
query: {
client_id: this.clientId,
code_challenge: challengeHash,
code_challenge_method: 'S256',
response_type: 'code',
redirect_uri: this.redirectUri,
scope,
adapter: 'token',
ssotoken: token,
},
});
});
}
toJSON() {
return {
clientId: this.clientId,
redirectUri: decodeURI(this.redirectUri),
codeVerifier: this.codeVerifier,
accessToken: this.accessToken,
refreshToken: this.refreshToken,
expirationTimestamp: this.expirationTimestamp,
};
}
}
exports.BSSO = BSSO;