@authress/sdk
Version:
Client SDK for Authress authorization as a service. Provides managed authorization api to secure service resources including user data.
138 lines (119 loc) • 4.01 kB
JavaScript
const axios = require('axios');
const packageInfo = require('../package.json');
const { sanitizeUrl } = require('./util');
const defaultHeaders = {
'Content-Type': 'application/json'
};
async function retryExecutor(func) {
let lastError = null;
for (let iteration = 0; iteration < 5; iteration++) {
try {
const result = await func();
return result;
} catch (error) {
lastError = error;
if (error.code === 'EPIPE' || error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET' || error.status >= 500) {
await new Promise(resolve => setTimeout(resolve, 10 * 2 ** iteration));
continue;
}
throw error;
}
}
throw lastError;
}
class HttpClient {
constructor(baseUrl, tokenProvider, userAgent) {
const sanitizedBaseUrl = sanitizeUrl(baseUrl);
this.baseUrl = new URL(sanitizedBaseUrl).toString().replace(/\/+$/, '');
this.tokenProvider = tokenProvider;
this.userAgentSuffix = userAgent || '';
const client = axios.create({ baseURL: this.baseUrl });
client.interceptors.request.use(async config => {
let token;
if (typeof this.tokenProvider === 'function') {
token = await this.tokenProvider(this.baseUrl);
} else if (typeof this.tokenProvider === 'object') {
this.tokenProvider.authressCustomDomain = this.tokenProvider.authressCustomDomain || this.baseUrl;
token = await this.tokenProvider.getToken();
}
config.headers = {
...config.headers,
Authorization: `Bearer ${token}`
};
// Avoid breaking SDK usages in UIs that depend on this library, since we aren't allowed to set User-Agent in a browser context
if (process && process.versions && process.versions.node) {
config.headers['User-Agent'] = `Authress SDK; Javascript; ${packageInfo.version}; ${this.userAgentSuffix}`;
}
return config;
}, error => {
let newError = error;
if (error) {
newError = error.message;
if (error.response) {
newError = {
data: error.response.data || {},
status: error.response.status,
headers: error.response.headers
};
} else if (error.message) {
newError = {
message: error.message,
code: error.code,
stack: error.stack
};
}
}
throw newError;
});
client.interceptors.response.use(response => {
return response;
}, error => {
const newError = error && error.response && {
url: error.config && error.config.url,
data: error.response.data || {},
status: error.response.status,
headers: error.response.headers
} || error.message && { message: error.message, code: error.code, stack: error.stack } || error;
throw newError;
});
this.client = client;
}
get(url, headers, type = 'json') {
return retryExecutor(() => {
return this.client.get(url.toString(), {
headers: Object.assign({}, defaultHeaders, headers),
responseType: type
});
});
}
delete(url, headers, type = 'json') {
return retryExecutor(() => {
return this.client.delete(url.toString(), {
headers: Object.assign({}, defaultHeaders, headers),
responseType: type
});
});
}
post(url, data, headers) {
return retryExecutor(() => {
return this.client.post(url.toString(), data, {
headers: Object.assign({}, defaultHeaders, headers)
});
});
}
put(url, data, headers) {
return retryExecutor(() => {
return this.client.put(url.toString(), data, {
headers: Object.assign({}, defaultHeaders, headers)
});
});
}
patch(url, data, headers) {
return retryExecutor(() => {
return this.client.patch(url.toString(), data, {
headers: Object.assign({}, defaultHeaders, headers)
});
});
}
}
module.exports = HttpClient;