@authress/login 2.2.196

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts ADDED
@@ -0,0 +1,174 @@
1
+ interface Settings {
2
+ /** Your Authress custom domain - see https://authress.io/app/#/setup?focus=domain */
3
+ authressLoginHostUrl: string;
4
+ /** The Authress applicationId for this app - see https://authress.io/app/#/manage?focus=applications */
5
+ applicationId: string;
6
+ }
7
+
8
+ interface AuthenticationParameters {
9
+ /** Specify which provider connection that user would like to use to log in - see https://authress.io/app/#/manage?focus=connections */
10
+ connectionId?: string;
11
+ /** Instead of connectionId, specify the tenant lookup identifier to log the user with the mapped tenant - see https://authress.io/app/#/manage?focus=tenants */
12
+ tenantLookupIdentifier?: string;
13
+ /** Store the credentials response in the specified location. Options are either 'cookie' or 'query'. (Default: **cookie**) */
14
+ responseLocation?: string;
15
+ /** The type of credentials returned in the response. The list of options is any of 'code token id_token' separated by a space. Select token to receive an access_token, id_token to return the user identity in an JWT, and code for the authorization_code grant_type flow. (Default: **token id_token**) */
16
+ flowType?: string;
17
+ /** Specify where the provider should redirect the user to in your application. If not specified, will be the current location href. Must be a valid redirect url matching what is defined in the application in the Authress Management portal. (Default: **window.location.href**) */
18
+ redirectUrl?: string;
19
+ /** Connection specific properties to pass to the identity provider. Can be used to override default scopes for example. */
20
+ connectionProperties?: Record<string, string>;
21
+ /** Force getting new credentials. (Default: **false** - only get new credentials if none exist.) */
22
+ force?: boolean;
23
+ /** Enable multi-account login. The user will be prompted to login with their other account, if they are not logged in already. (Default: **false** - the current session is validated and no login is displayed) */
24
+ multiAccount?: boolean;
25
+ /** Remove all cookies, LocalStorage, and SessionStorage related data before logging in. In most cases, this helps prevent corrupted browser state from affecting your user's experience. (Default: **true**) */
26
+ clearUserDataBeforeLogin?: boolean;
27
+ }
28
+
29
+ interface LinkIdentityParameters {
30
+ /** Specify which provider connection that user would like to use to log in - see https://authress.io/app/#/manage?focus=connections */
31
+ connectionId?: string;
32
+ /** Instead of connectionId, specify the tenant lookup identifier to log the user with the mapped tenant - see https://authress.io/app/#/manage?focus=tenants */
33
+ tenantLookupIdentifier?: string;
34
+ /** Specify where the provider should redirect the user to in your application. If not specified, will be the current location href. Must be a valid redirect url matching what is defined in the application in the Authress Management portal. (Default: **window.location.href**) */
35
+ redirectUrl?: string;
36
+ /** Connection specific properties to pass to the identity provider. Can be used to override default scopes for example. */
37
+ connectionProperties?: Record<string, string>;
38
+ }
39
+
40
+ interface ExtensionAuthenticationParameters {
41
+ /** The redirect to your login screen will contain two query parameters `state`. Pass the state into this method. (Default: **window.location.query.state**) */
42
+ state?: string;
43
+ /** Specify which provider connection that user would like to use to log in - see https://authress.io/app/#/manage?focus=connections */
44
+ connectionId?: string;
45
+ /** Instead of connectionId, specify the tenant lookup identifier to log the user with the mapped tenant - see https://authress.io/app/#/manage?focus=tenants */
46
+ tenantLookupIdentifier?: string;
47
+ /** Connection specific properties to pass to the identity provider. Can be used to override default scopes for example. */
48
+ connectionProperties?: Record<string, string>;
49
+ }
50
+
51
+ /** Options for getting a token including timeout configuration. */
52
+ interface TokenParameters {
53
+ /** Timeout waiting for user token to populate. After this time an error will be thrown. (Default: **5000**) */
54
+ timeoutInMillis?: number;
55
+ }
56
+
57
+ interface UserCredentials {
58
+ /** User access token generated credentials for the connected provider used to log in */
59
+ accessToken: string;
60
+ }
61
+
62
+ export class LoginClient {
63
+ /**
64
+ * @constructor constructs the LoginClient with a given configuration
65
+ * @param {Settings} settings Authress LoginClient settings
66
+ * @param {Object} [logger] a configured logger object, optionally `console`, which can used to display debug and warning messages.
67
+ */
68
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
69
+ constructor(settings: Settings, logger?: unknown);
70
+
71
+ /**
72
+ * @description Gets the user's profile data and returns it if it exists. Should be called after {@link userSessionExists} or it will be empty.
73
+ * @return {Object} The user identity which contains a userData object.
74
+ */
75
+ getUserIdentity(): Record<string, unknown>;
76
+
77
+ /**
78
+ * @description Gets the user's credentials that were generated as part of the connection provider. These credentials work directly with that provider.
79
+ * @return {Promise<UserCredentials?>} The user's connection credentials.
80
+ */
81
+ getConnectionCredentials(): Promise<UserCredentials | null>;
82
+
83
+ /**
84
+ * @description Async wait for a user session to exist. Will block until {@link userSessionExists} or {@link authenticate} is called.
85
+ * @return {Promise<void>}
86
+ */
87
+ waitForUserSession(): Promise<void>;
88
+
89
+ /**
90
+ * @description Call this function on every route change. It will check if the user just logged in or is still logged in.
91
+ * @return {Promise<boolean>} Returns truthy if there a valid existing session, falsy otherwise.
92
+ */
93
+ userSessionExists(): Promise<boolean>;
94
+
95
+ /**
96
+ * @description When a platform extension attempts to log a user in, the Authress Login page will redirect to your Platform defaultAuthenticationUrl. At this point, show the user the login screen, and then pass the results of the login to this method.
97
+ * @param {ExtensionAuthenticationParameters} settings Parameters for controlling how and when users should be authenticated for the app.
98
+ * @return {Promise<boolean>} Is there a valid existing session.
99
+ */
100
+ updateExtensionAuthenticationRequest(settings: ExtensionAuthenticationParameters): Promise<void>;
101
+
102
+ /**
103
+ * @description Unlink an identity from the user's account.
104
+ * @param {String} identityId Specify the provider connection id or the user id of that connection that user would like to unlink - see https://authress.io/app/#/manage?focus=connections
105
+ * @return {Promise<void>} Throws an error if identity cannot be unlinked.
106
+ */
107
+ unlinkIdentity(identityId: string): Promise<void>;
108
+
109
+ /**
110
+ * @description Link a new identity to the currently logged in user. The user will be asked to authenticate to a new connection.
111
+ * @param {LinkIdentityParameters} settings Parameters for selecting which identity of a user should be linked.
112
+ * @return {Promise<void>}
113
+ */
114
+ linkIdentity(settings: LinkIdentityParameters): Promise<void>;
115
+
116
+ /**
117
+ * @description Logs a user in, if the user is not logged in, will redirect the user to their selected connection/provider and then redirect back to the {@link redirectUrl}. If neither the {@link connectionId} nor the {@link tenantLookupIdentifier} is specified the user will be directed to the Authress hosted login page to select their preferred login method.
118
+ * @param {AuthenticationParameters} [settings] Parameters for controlling how and when users should be authenticated for the app.
119
+ * @return {Promise<boolean>} Is there a valid existing session.
120
+ */
121
+ authenticate(settings?: AuthenticationParameters): Promise<boolean>;
122
+
123
+ /**
124
+ * @description Ensures the user's bearer token exists. To be used in the Authorization header as a Bearer token. This method blocks on a valid user session being created, and expects {@link authenticate} to have been called first. Additionally, if the application configuration specifies that tokens should be secured from javascript, the token will be a hidden cookie only visible to service APIs and will not be returned. If the token is expired and the session is still valid, then it will automatically generate a new token directly from Authress.
125
+ * @param {TokenParameters} [settings] Optional token parameters to constrain how the existing token is retreived.
126
+ * @return {Promise<string>} The Authorization Bearer token.
127
+ */
128
+ ensureToken(settings?: TokenParameters): Promise<string>;
129
+
130
+ /**
131
+ * @description Log the user out removing the current user's session. If the user is not logged in this has no effect. If the user is logged in via secure session, the the redirect url will be ignored. If the user is logged in without a secure session the user agent will be redirected to the hosted login and then redirected to the {@link redirectUrl}.
132
+ * @param {string} [redirectUrl='window.location.href'] Optional redirect location to return the user to after logout. Will only be used for cross domain sessions.
133
+ */
134
+ logout(redirectUri?: string): Promise<void>;
135
+ }
136
+
137
+ interface RequestTokenParameters {
138
+ /** The redirect to your login screen will contain two query parameters `state` and `flow`. Pass the state into this method. */
139
+ code?: string;
140
+ }
141
+
142
+ interface TokenResponse {
143
+ /** The user access token to be used with the platform */
144
+ accessToken: string;
145
+ }
146
+
147
+ export class ExtensionClient {
148
+ /**
149
+ * @constructor constructs an ExtensionClient to be embedded in your platform SDK to enable extension easy login
150
+ * @param {string} authressCustomDomain Your Authress custom domain - see https://authress.io/app/#/manage?focus=domain
151
+ * @param {string} extensionId The platform extensionId for this app - see https://authress.io/app/#/manage?focus=extensions
152
+ */
153
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
154
+ constructor(authressCustomDomain: string, extensionId: string);
155
+
156
+ /**
157
+ * @description Gets the user's profile data and returns it if it exists. Should be called after {@link userSessionExists} or it will be empty.
158
+ * @return {Promise<Record<string, unknown>>} The user identity which contains a userData object.
159
+ */
160
+ getUserIdentity(): Promise<Record<string, unknown>>;
161
+
162
+ /**
163
+ * @description When a platform extension attempts to log a user in, the Authress Login page will redirect to your Platform defaultAuthenticationUrl. At this point, show the user the login screen, and then pass the results of the login to this method.
164
+ * @param {String} [code] The redirect to your login screen will contain two query parameters `state` and `flow`. Pass the state into this method.
165
+ */
166
+ requestToken(options?: RequestTokenParameters): Promise<TokenResponse>;
167
+
168
+ /**
169
+ * @description Logs a user in, if the user is logged in, will return the token response, if the user is not logged in, will redirect the user to their selected connection/provider and then redirect back to the {@link redirectUrl}.
170
+ * @param {String} [redirectUrl=${window.location.href}] Specify where the provider should redirect to the user to in your application. If not specified, the default is the current location href. Must be a valid redirect url matching what is defined in the application in the Authress Management portal. Only used if the user is not logged in.
171
+ * @return {Promise<TokenResponse>} Returns the token if the user is logged in otherwise redirects the user
172
+ */
173
+ login(redirectUrl?: string): Promise<TokenResponse>;
174
+ }
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@authress/login",
3
+ "version": "2.2.196",
4
+ "description": "Universal login sdk for Authress authentication as a service. Provides managed authentication for user identity, authentication, and token verification.",
5
+ "main": "./src/index.js",
6
+ "types": "./index.d.ts",
7
+ "files": [
8
+ "index.d.ts",
9
+ "src",
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "node make.js build && NODE_ENV=production webpack --mode=production",
14
+ "lint": "eslint --ext .js,.ts src tests make.js index.d.ts",
15
+ "test": "check-dts index.d.ts && mocha tests/**/*.test.js -R spec"
16
+ },
17
+ "dependencies": {
18
+ "axios": "^0.21",
19
+ "cookie": "^0.4.1",
20
+ "lodash.take": "^4.1.1"
21
+ },
22
+ "devDependencies": {
23
+ "@babel/core": "^7.17.5",
24
+ "@babel/preset-env": "^7.16.11",
25
+ "@types/node": "^14.14.35",
26
+ "@typescript-eslint/eslint-plugin": "^3.1.0",
27
+ "@typescript-eslint/parser": "^3.1.0",
28
+ "babel-loader": "^8.2.3",
29
+ "chai": "^4.2.0",
30
+ "check-dts": "^0.4.4",
31
+ "ci-build-tools": "^1.0.13",
32
+ "commander": "^4.0.1",
33
+ "compression-webpack-plugin": "^9.2.0",
34
+ "eslint": "^7.12.1",
35
+ "eslint-config-cimpress-atsquad": "^1.0.67",
36
+ "eslint-loader": "^4.0.2",
37
+ "eslint-plugin-mocha": "^7.0.1",
38
+ "eslint-plugin-node": "^11.1.0",
39
+ "eslint-plugin-promise": "^6.1.1",
40
+ "fs-extra": "^8.1.0",
41
+ "glob": "^7.1.6",
42
+ "mocha": "^10.1.0",
43
+ "path-browserify": "^1.0.1",
44
+ "sinon": "^7.5.0",
45
+ "sinon-chai": "^3.3.0",
46
+ "terser-webpack-plugin": "^5.3.1",
47
+ "typescript": "^3.9.5",
48
+ "webpack": "^5.69.1",
49
+ "webpack-cli": "^4.9.2"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/Authress/authress-login.js"
54
+ },
55
+ "keywords": [
56
+ "authentication",
57
+ "authentication as a service",
58
+ "Login",
59
+ "Login Client",
60
+ "universal login",
61
+ "auth",
62
+ "federated login",
63
+ "secure login",
64
+ "application security",
65
+ "IDaaS",
66
+ "authentication",
67
+ "user authentication",
68
+ "user identity",
69
+ "Oauth2",
70
+ "Oauth2.1",
71
+ "Oauth3",
72
+ "platform",
73
+ "platform login",
74
+ "extension",
75
+ "Authress",
76
+ "Authress client",
77
+ "user security"
78
+ ],
79
+ "author": "Authress Developers <developers@authress.io> (https://authress.io)",
80
+ "license": "Apache-2.0",
81
+ "bugs": {
82
+ "url": "https://github.com/Authress/authress-login.js/issues"
83
+ },
84
+ "homepage": "https://authress.io",
85
+ "engines": {
86
+ "node": ">=14"
87
+ }
88
+ }
@@ -0,0 +1,32 @@
1
+ function percentToByte(p) {
2
+ return String.fromCharCode(parseInt(p.slice(1), 16));
3
+ }
4
+
5
+ function encodeBase64(str) {
6
+ return btoa(encodeURIComponent(str).replace(/%[0-9A-F]{2}/g, percentToByte));
7
+ }
8
+
9
+ function byteToPercent(b) {
10
+ return `%${`00${b.charCodeAt(0).toString(16)}`.slice(-2)}`;
11
+ }
12
+
13
+ function decodeBase64(str) {
14
+ return decodeURIComponent(Array.from(atob(str), byteToPercent).join(''));
15
+ }
16
+
17
+ module.exports.decode = function decode(str) {
18
+ return decodeBase64(str.replace(/-/g, '+').replace(/_/g, '/'));
19
+ };
20
+
21
+ module.exports.encode = function encode(str) {
22
+ if (str && typeof str === 'object') {
23
+ return btoa(String.fromCharCode(...new Uint8Array(str))).replace(/\//g, '_')
24
+ .replace(/\+/g, '-')
25
+ .replace(/=+$/, '');
26
+ }
27
+
28
+ return encodeBase64(str)
29
+ .replace(/\//g, '_')
30
+ .replace(/\+/g, '-')
31
+ .replace(/=+$/, '');
32
+ };
@@ -0,0 +1,153 @@
1
+ const base64url = require('./base64url');
2
+
3
+ const jwtManager = require('./jwtManager');
4
+
5
+ const AuthenticationRequestNonceKey = 'ExtensionRequestNonce';
6
+
7
+ let userSessionSequencePromise = null;
8
+
9
+ class ExtensionClient {
10
+ /**
11
+ * @constructor constructs an ExtensionClient to be embedded in your platform SDK to enable extension easy login
12
+ * @param {string} authressCustomDomain Your Authress custom domain - see https://authress.io/app/#/manage?focus=domain
13
+ * @param {string} extensionId The platform extensionId for this app - see https://authress.io/app/#/manage?focus=extensions
14
+ */
15
+ constructor(authressCustomDomain, extensionId) {
16
+ this.extensionId = extensionId;
17
+
18
+ if (!authressCustomDomain) {
19
+ throw Error('Missing required property "authressCustomDomain" in ExtensionClient constructor. The Custom Authress Domain Host is required.');
20
+ }
21
+
22
+ if (!extensionId) {
23
+ throw Error('Missing required property "extensionId" in ExtensionClient constructor. The extension is required for selecting the correct login method.');
24
+ }
25
+
26
+ this.authressCustomDomain = `https://${authressCustomDomain.replace(/^(https?:\/+)/, '')}`;
27
+ this.accessToken = null;
28
+
29
+ window.onload = async () => {
30
+ await this.requestToken({ silent: true });
31
+ };
32
+ }
33
+
34
+ /**
35
+ * @description Gets the user's profile data and returns it if it exists. Should be called after {@link userSessionExists} or it will be empty.
36
+ * @return {Promise<Record<string, unknown>>} The user data object.
37
+ */
38
+ async getUserIdentity() {
39
+ const userData = await this.accessToken && jwtManager.decode(this.accessToken);
40
+ if (!userData) {
41
+ return null;
42
+ }
43
+
44
+ if (userData.exp * 1000 < Date.now()) {
45
+ this.accessToken = null;
46
+ return null;
47
+ }
48
+
49
+ return userData;
50
+ }
51
+
52
+ async getTokenResponse() {
53
+ const isLoggedIn = await this.getUserIdentity();
54
+ if (!isLoggedIn) {
55
+ return null;
56
+ }
57
+
58
+ return { accessToken: this.accessToken };
59
+ }
60
+
61
+ /**
62
+ * @description When a platform extension attempts to log a user in, the Authress Login page will redirect to your Platform defaultAuthenticationUrl. At this point, show the user the login screen, and then pass the results of the login to this method.
63
+ * @param {String} [options.code] The redirect to your login screen will contain two query parameters `state` and `flow`. Pass the state into this method.
64
+ * @return {Promise<TokenResponse>} Returns the token if the user is logged in otherwise redirects the user
65
+ */
66
+ requestToken(options = { code: null, silent: false }) {
67
+ if (userSessionSequencePromise) {
68
+ return userSessionSequencePromise = userSessionSequencePromise
69
+ .catch(() => { /* ignore since we always want to continue even after a failure */ })
70
+ .then(() => this.requestTokenContinuation(options));
71
+ }
72
+ const nextContinuation = this.requestTokenContinuation(options);
73
+ nextContinuation.catch(() => { /* This prevents an uncaught promise rejection in the running process */ });
74
+ return userSessionSequencePromise = nextContinuation;
75
+ }
76
+
77
+ async requestTokenContinuation(options = { code: null, silent: false }) {
78
+ const code = options && options.code || new URLSearchParams(window.location.search).get('code');
79
+ if (!code) {
80
+ if (!options || !options.silent) {
81
+ const e = Error('OAuth Authorization code is required');
82
+ e.code = 'InvalidAuthorizationCode';
83
+ throw e;
84
+ }
85
+ return this.getTokenResponse();
86
+ }
87
+
88
+ const url = new URL(this.authressCustomDomain);
89
+ url.pathname = '/api/authentication/oauth/tokens';
90
+ const { codeVerifier, redirectUrl } = JSON.parse(localStorage.getItem(AuthenticationRequestNonceKey) || '{}');
91
+ const result = await fetch(url.toString(), {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ code_verifier: codeVerifier,
96
+ code,
97
+ grant_type: 'authorization_code',
98
+ client_id: this.extensionId,
99
+ redirect_uri: redirectUrl
100
+ })
101
+ });
102
+
103
+ const tokenResponse = await result.json();
104
+ this.accessToken = tokenResponse.access_token;
105
+
106
+ const newUrl = new URL(window.location);
107
+ newUrl.searchParams.delete('code');
108
+ newUrl.searchParams.delete('iss');
109
+ newUrl.searchParams.delete('nonce');
110
+ newUrl.searchParams.delete('expires_in');
111
+ newUrl.searchParams.delete('access_token');
112
+ newUrl.searchParams.delete('id_token');
113
+ history.replaceState({}, undefined, newUrl.toString());
114
+
115
+ return this.getTokenResponse();
116
+ }
117
+
118
+ /**
119
+ * @description Logs a user in, if the user is logged in, will return the token response, if the user is not logged in, will redirect the user to their selected connection/provider and then redirect back to the {@link redirectUrl}.
120
+ * @param {String} [redirectUrl=${window.location.href}] Specify where the provider should redirect to the user to in your application. If not specified, the default is the current location href. Must be a valid redirect url matching what is defined in the application in the Authress Management portal.
121
+ * @return {Promise<TokenResponse>} Returns the token if the user is logged in otherwise redirects the user
122
+ */
123
+ async login(redirectUrlOverride) {
124
+ const tokenResponse = await this.getTokenResponse();
125
+ if (tokenResponse) {
126
+ return tokenResponse;
127
+ }
128
+ const completeLoginResult = await this.requestToken({ silent: true });
129
+ if (completeLoginResult) {
130
+ return completeLoginResult;
131
+ }
132
+ const url = new URL(this.authressCustomDomain);
133
+
134
+ const codeVerifier = base64url.encode((window.crypto || window.msCrypto).getRandomValues(new Uint32Array(16)).toString());
135
+ // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
136
+ const hashBuffer = await (window.crypto || window.msCrypto).subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
137
+ const codeChallenge = base64url.encode(hashBuffer);
138
+
139
+ const redirectUrl = redirectUrlOverride || window.location.href;
140
+ localStorage.setItem(AuthenticationRequestNonceKey, JSON.stringify({ codeVerifier, redirectUrl }));
141
+ url.searchParams.set('client_id', this.extensionId);
142
+ url.searchParams.set('code_challenge', codeChallenge);
143
+ url.searchParams.set('code_challenge_method', 'S256');
144
+ url.searchParams.set('redirect_uri', redirectUrl);
145
+ window.location.assign(url.toString());
146
+
147
+ // Prevent the current UI from taking any action once we decided we need to log in.
148
+ await new Promise(resolve => setTimeout(resolve, 5000));
149
+ return null;
150
+ }
151
+ }
152
+
153
+ module.exports = ExtensionClient;
@@ -0,0 +1,166 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+ const axios = require('axios');
3
+
4
+ const defaultHeaders = {
5
+ 'Content-Type': 'application/json'
6
+ };
7
+
8
+ async function retryExecutor(func) {
9
+ let lastError = null;
10
+ for (let iteration = 0; iteration < 5; iteration++) {
11
+ try {
12
+ const result = await func();
13
+ return result;
14
+ } catch (error) {
15
+ lastError = error;
16
+ if (error.code === 'EPIPE' || error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET' || error.status >= 500) {
17
+ await new Promise(resolve => setTimeout(resolve, 10 * 2 ** iteration));
18
+ continue;
19
+ }
20
+ throw error;
21
+ }
22
+ }
23
+ throw lastError;
24
+ }
25
+
26
+ class HttpClient {
27
+ constructor(authressLoginCustomDomain, overrideLogger) {
28
+ if (!authressLoginCustomDomain) {
29
+ throw Error('Custom Authress Domain Host is required');
30
+ }
31
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
32
+ const logger = overrideLogger || { debug() {}, warn() {}, critical() {} };
33
+
34
+ const loginHostFullUrl = new URL(`https://${authressLoginCustomDomain.replace(/^(https?:\/+)/, '')}`);
35
+ const loginUrl = `${loginHostFullUrl.origin}/api`;
36
+ const client = axios.create({ baseURL: loginUrl });
37
+
38
+ client.interceptors.request.use(config => {
39
+ logger.debug({ title: 'HttpClient Request', online: navigator.onLine, requestId: config.requestId, method: config.method, url: config.url });
40
+
41
+ return config;
42
+ }, error => {
43
+ let notFound = false;
44
+ let newError = error;
45
+ let url;
46
+ let requestId;
47
+
48
+ if (error) {
49
+ newError = error.message;
50
+
51
+ if (error.response) {
52
+ newError = {
53
+ data: error.response.data,
54
+ status: error.response.status,
55
+ headers: error.response.headers
56
+ };
57
+ notFound = error.response.status === 404;
58
+ } else if (error.message) {
59
+ newError = {
60
+ message: error.message,
61
+ code: error.code,
62
+ stack: error.stack
63
+ };
64
+ }
65
+
66
+ if (error.config) {
67
+ url = error.config.url;
68
+ requestId = error.config.requestId;
69
+ } else {
70
+ requestId = error.request && error.request.config && error.request.config.requestId;
71
+ }
72
+ }
73
+
74
+ const logObject = { title: 'HttpClient Request Error', url, online: navigator.onLine, requestId, exception: newError };
75
+
76
+ if (notFound) {
77
+ logger.debug(logObject);
78
+ } else {
79
+ logger.warn(logObject);
80
+ }
81
+
82
+ throw newError;
83
+ });
84
+
85
+ client.interceptors.response.use(response => response, error => {
86
+ // Rewritten error object for easy consumption
87
+ if (error.re) {
88
+ throw error;
89
+ }
90
+
91
+ const newError = error && error.response && {
92
+ url: error.config && error.config.url,
93
+ data: error.response.data,
94
+ status: error.response.status,
95
+ headers: error.response.headers
96
+ } || error.message && { message: error.message, code: error.code, stack: error.stack } || error;
97
+ newError.re = true;
98
+ const requestId = error && (error.config && error.config.requestId || error.request && error.request.config && error.request.config.requestId);
99
+
100
+ let message = 'HttpClient Response Error';
101
+ let logMethod = 'warn';
102
+
103
+ if (!error) {
104
+ message = 'HttpClient Response Error - Unknown error occurred';
105
+ } else if (error.response && error.response.status === 404) {
106
+ logMethod = 'debug';
107
+ } else if (error.response && error.response.status === 401) {
108
+ message = 'HttpClient Response Error due to invalid token';
109
+ }
110
+
111
+ logger[logMethod]({ title: message, online: navigator.onLine, requestId, exception: newError, url: error && error.config && error.config.url });
112
+ throw newError;
113
+ });
114
+
115
+ this.client = client;
116
+ }
117
+
118
+ get(url, withCredentials, headers, type = 'json') {
119
+ return retryExecutor(() => {
120
+ return this.client.get(url.toString(), {
121
+ withCredentials: window.location.hostname !== 'localhost' && !!withCredentials,
122
+ headers: Object.assign({}, defaultHeaders, headers),
123
+ responseType: type
124
+ });
125
+ });
126
+ }
127
+
128
+ delete(url, withCredentials, headers, type = 'json') {
129
+ return retryExecutor(() => {
130
+ return this.client.delete(url.toString(), {
131
+ withCredentials: window.location.hostname !== 'localhost' && !!withCredentials,
132
+ headers: Object.assign({}, defaultHeaders, headers),
133
+ responseType: type
134
+ });
135
+ });
136
+ }
137
+
138
+ post(url, withCredentials, data, headers) {
139
+ return retryExecutor(() => {
140
+ return this.client.post(url.toString(), data, {
141
+ withCredentials: window.location.hostname !== 'localhost' && !!withCredentials,
142
+ headers: Object.assign({}, defaultHeaders, headers)
143
+ });
144
+ });
145
+ }
146
+
147
+ put(url, withCredentials, data, headers) {
148
+ return retryExecutor(() => {
149
+ return this.client.put(url.toString(), data, {
150
+ withCredentials: window.location.hostname !== 'localhost' && !!withCredentials,
151
+ headers: Object.assign({}, defaultHeaders, headers)
152
+ });
153
+ });
154
+ }
155
+
156
+ patch(url, withCredentials, data, headers) {
157
+ return retryExecutor(() => {
158
+ return this.client.patch(url.toString(), data, {
159
+ withCredentials: window.location.hostname !== 'localhost' && !!withCredentials,
160
+ headers: Object.assign({}, defaultHeaders, headers)
161
+ });
162
+ });
163
+ }
164
+ }
165
+
166
+ module.exports = HttpClient;