browser-sdk
Version:
javascript SDK for the lightelligence-platform
199 lines (165 loc) • 5.59 kB
JavaScript
import * as OIDC from 'oidc-client';
/**
* @typedef jsonService
* @property {Function} postForm
* @property {XMLHttpRequest} _XMLHttpRequest
* @property {Array<string>} _contentTypes
*/
/**
* @typedef jsonServicePostFormPayload
* @property {Object<string>} headers
*/
/**
* @typedef tokenClient
* @property {jsonService} _jsonService
* @property {Function} exchangeRefreshToken
*/
/**
* User Manager
*
* Just a wrapper around OIDC.UserManager
* https://github.com/IdentityModel/oidc-client-js/blob/dev/src/UserManager.js
*/
export default class UserManager extends OIDC.UserManager {
constructor(settings) {
super(settings);
/**
* We access the tokenClient which is part of the UserManager
*
* https://github.com/IdentityModel/oidc-client-js/blob/dev/src/UserManager.js#L45
*
* @type tokenClient
*/
const tokenClient = this._tokenClient; // eslint-disable-line
/**
* We need to access the JSONService instance in order to modify the
* XMLHTTPRequest to accept headers.
*
* https://github.com/IdentityModel/oidc-client-js/blob/dev/src/JsonService.js#L99
*/
const jsonService = tokenClient._jsonService; // eslint-disable-line
/** @type Array<string> */
const allowedContentTypes = jsonService._contentTypes; // eslint-disable-line
/**
* Checks if a content type is one of the allowed ones
*
* @param contentType
* @returns {string}
*/
const isAllowedContentType = contentType =>
allowedContentTypes.find(item => contentType.startsWith(item));
/**
* Replaces postForm method of the jsonService.
* https://github.com/IdentityModel/oidc-client-js/blob/dev/src/JsonService.js#L99
*
* @param {string} url
* @param {jsonServicePostFormPayload} payload
*/
jsonService.postForm = (url, payload) => {
/** @type XMLHttpRequest */
const XMLHttpRequest = jsonService._XMLHttpRequest; // eslint-disable-line
if (!url) {
OIDC.Log.error('JsonService.postForm: No url passed');
throw new Error('url');
}
OIDC.Log.debug('JsonService.postForm, url: ', url);
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('POST', url);
req.onload = () => {
OIDC.Log.debug(
'JsonService.postForm: HTTP response received, status',
req.status
);
if (req.status !== 200 && req.status !== 400) {
return reject(Error(`${req.statusText} ( ${req.status} )`));
}
const contentType = req.getResponseHeader('Content-Type');
if (!contentType || !isAllowedContentType(contentType)) {
if (req.status === 200) {
return reject(
Error(
`Invalid response Content-Type: ${contentType}, from URL: ${url}`
)
);
}
return reject(Error(`${req.statusText} ( ${req.status} )`));
}
try {
const responsePayload = JSON.parse(req.responseText);
if (req.status === 200) {
return resolve(responsePayload);
}
if (responsePayload && responsePayload.error) {
OIDC.Log.error(
'JsonService.postForm: Error from server: ',
responsePayload.error
);
return reject(new Error(responsePayload.error));
}
} catch (error) {
OIDC.Log.error(
'JsonService.postForm: Error parsing JSON response',
error.message
);
return reject(error);
}
return reject(Error(`${req.statusText} ( ${req.status} )`));
};
req.onerror = () => {
OIDC.Log.error('JsonService.postForm: network error');
reject(Error('Network Error'));
};
const body = Object.entries(payload)
.filter(([key]) => key !== 'headers')
.map(pair => pair.map(encodeURIComponent))
.map(([key, value]) => `${key}=${value}`)
.join('&');
req.setRequestHeader(
'Content-Type',
'application/x-www-form-urlencoded'
);
Object.entries(payload.headers || {}).forEach(([name, value]) => {
req.setRequestHeader(name, value);
});
req.send(body);
});
};
}
/**
* Change the tenant id
*
* @param {string} tenantId Changes the users's tenant
* @returns {Promise<User>}
*/
async changeTenant(tenantId) {
/** @type {tokenClient} */
const tokenClient = this._tokenClient; // eslint-disable-line
const user = await this.getUser();
if (!user || !user.refresh_token) {
throw new Error('changeTenant: No refresh token');
}
if (!user.profile) {
throw new Error('changeTenant: No profile loaded for the user');
}
const result = await tokenClient.exchangeRefreshToken({
refresh_token: user.refresh_token,
headers: { 'X-Requested-With': tenantId },
});
if (
!result ||
!result.access_token ||
!result.refresh_token ||
!result.expires_in
) {
throw new Error('changeTenant: Error while changing tenant');
}
user.access_token = result.access_token;
user.refresh_token = result.refresh_token;
user.expires_in = result.expires_in;
user.profile.tenant = tenantId;
await this.storeUser(user);
this.events.load(user);
return user;
}
}