@schibsted/account-sdk-browser 6.0.0-alpha.2 → 6.0.0-alpha.3
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/README.md +39 -39
- package/dist/account-sdk.d.ts +4 -0
- package/dist/account.d.ts +29 -0
- package/dist/get-account-sdk.d.ts +3 -0
- package/dist/global-registry.d.ts +1 -1
- package/dist/globals.d.ts +2 -0
- package/dist/has-access-response.d.ts +2 -0
- package/dist/identity.d.ts +37 -237
- package/dist/index.d.ts +8 -3
- package/dist/index.js +859 -4
- package/dist/index.js.map +1 -0
- package/dist/monetization.d.ts +7 -43
- package/dist/resolve-browser-window.d.ts +1 -0
- package/dist/{RESTClient.d.ts → rest-client.d.ts} +22 -11
- package/dist/rest-clients.d.ts +10 -0
- package/dist/session-response.d.ts +30 -0
- package/dist/types/account.d.ts +13 -0
- package/dist/types/common.d.ts +22 -0
- package/dist/types/identity.d.ts +41 -0
- package/dist/types/login.d.ts +120 -0
- package/dist/types/monetization-guards.d.ts +2 -0
- package/dist/types/monetization.d.ts +21 -0
- package/dist/types/session-guards.d.ts +36 -0
- package/dist/types/session.d.ts +124 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -9
- package/src/account-sdk.ts +74 -0
- package/src/account.ts +149 -0
- package/src/cache.ts +1 -1
- package/src/get-account-sdk.ts +94 -0
- package/src/global-registry.ts +1 -1
- package/src/globals.ts +2 -0
- package/src/has-access-response.ts +35 -0
- package/src/identity.ts +269 -423
- package/src/index.ts +28 -3
- package/src/monetization.ts +31 -68
- package/src/object.ts +1 -1
- package/src/resolve-browser-window.ts +11 -0
- package/src/{RESTClient.ts → rest-client.ts} +55 -28
- package/src/rest-clients.ts +30 -0
- package/src/session-response.ts +85 -0
- package/src/types/account.ts +27 -0
- package/src/types/common.ts +26 -0
- package/src/types/identity.ts +55 -0
- package/src/types/login.ts +145 -0
- package/src/types/monetization-guards.ts +35 -0
- package/src/types/monetization.ts +24 -0
- package/src/types/session-guards.ts +89 -0
- package/src/types/session.ts +147 -0
- package/src/validate.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/identity-s4nofYmB.js +0 -370
- package/dist/identity-s4nofYmB.js.map +0 -1
- package/dist/identity.js +0 -2
- package/dist/monetization.js +0 -72
- package/dist/monetization.js.map +0 -1
- package/dist/version-spE-k97g.js +0 -289
- package/dist/version-spE-k97g.js.map +0 -1
- /package/dist/{SDKError.d.ts → sdk-error.d.ts} +0 -0
- /package/dist/{spidTalk.d.ts → spid-talk.d.ts} +0 -0
- /package/src/{SDKError.ts → sdk-error.ts} +0 -0
- /package/src/{spidTalk.ts → spid-talk.ts} +0 -0
package/src/identity.ts
CHANGED
|
@@ -4,202 +4,62 @@
|
|
|
4
4
|
|
|
5
5
|
import { TinyEmitter } from 'tiny-emitter';
|
|
6
6
|
import Cache from './cache.js';
|
|
7
|
-
import { ENDPOINTS
|
|
7
|
+
import { ENDPOINTS } from './config.js';
|
|
8
8
|
import { registerAndDispatchInGlobal } from './global-registry.js';
|
|
9
9
|
import { cloneDeep } from './object.js';
|
|
10
10
|
import * as popup from './popup.js';
|
|
11
|
-
import RESTClient from './
|
|
12
|
-
import
|
|
13
|
-
import
|
|
11
|
+
import type RESTClient from './rest-client.js';
|
|
12
|
+
import { buildClientSdrn, createAccountRestClient } from './rest-clients.js';
|
|
13
|
+
import SDKError from './sdk-error.js';
|
|
14
|
+
import {
|
|
15
|
+
isConnectedSessionResponse,
|
|
16
|
+
isConnectedSessionWithUserIdResponse,
|
|
17
|
+
isRedirectSessionPayload,
|
|
18
|
+
isSessionFailureResponse,
|
|
19
|
+
isSessionSuccessResponse,
|
|
20
|
+
normalizeSessionResponse,
|
|
21
|
+
} from './session-response.js';
|
|
22
|
+
import * as spidTalk from './spid-talk.js';
|
|
23
|
+
import type { LogFunction } from './types/common.js';
|
|
24
|
+
import type {
|
|
25
|
+
IdentityBeforeRedirectCallback,
|
|
26
|
+
IdentityOptions,
|
|
27
|
+
VarnishCookieOptions,
|
|
28
|
+
} from './types/identity.js';
|
|
29
|
+
import {
|
|
30
|
+
hasOpenSimplifiedLoginWidget,
|
|
31
|
+
type LoginOptions,
|
|
32
|
+
type OpenSimplifiedLoginWidget,
|
|
33
|
+
type SimplifiedLoginData,
|
|
34
|
+
type SimplifiedLoginWidgetInitialParams,
|
|
35
|
+
type SimplifiedLoginWidgetLoginOptions,
|
|
36
|
+
type SimplifiedLoginWidgetOptions,
|
|
37
|
+
} from './types/login.js';
|
|
38
|
+
import type {
|
|
39
|
+
ConnectedSessionResponse,
|
|
40
|
+
NormalizedSessionPayload,
|
|
41
|
+
SessionObjectResponse,
|
|
42
|
+
SessionResponse,
|
|
43
|
+
} from './types/session.js';
|
|
14
44
|
import { urlMapper } from './url.js';
|
|
15
45
|
import { assert, isNonEmptyString, isObject, isStr, isStrIn, isUrl } from './validate.js';
|
|
16
46
|
import version from './version.js';
|
|
17
47
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* Methods References) claim in ID token. Might also be used to ensure additional acr
|
|
34
|
-
* (sms, otp, eid) for already logged in users. Supported value is also 'otp-email' means one
|
|
35
|
-
* time password using email.
|
|
36
|
-
*/
|
|
37
|
-
acrValues?: string;
|
|
38
|
-
/**
|
|
39
|
-
* The OAuth scopes for the tokens. This is a list of scopes, separated by space. If the list
|
|
40
|
-
* of scopes contains `openid`, the generated tokens includes the id token which can be useful
|
|
41
|
-
* for getting information about the user. Omitting scope is allowed, while `invalid_scope` is
|
|
42
|
-
* returned when the client asks for a scope you aren't allowed to request.
|
|
43
|
-
* {@link https://tools.ietf.org/html/rfc6749#section-3.3}
|
|
44
|
-
* Defaults to `openid`.
|
|
45
|
-
*/
|
|
46
|
-
scope?: string;
|
|
47
|
-
/**
|
|
48
|
-
* Redirect uri that will receive the code. Must exactly match a redirectUri from your client
|
|
49
|
-
* in self-service
|
|
50
|
-
*/
|
|
51
|
-
redirectUri?: string;
|
|
52
|
-
/** Should we try to open a popup window? Defaults to `false`. */
|
|
53
|
-
preferPopup?: boolean;
|
|
54
|
-
/** user email or UUID hint */
|
|
55
|
-
loginHint?: string;
|
|
56
|
-
/** Pulse tag */
|
|
57
|
-
tag?: string;
|
|
58
|
-
/** Teaser slug. Teaser with given slug will be displayed in place of default teaser */
|
|
59
|
-
teaser?: string;
|
|
60
|
-
/**
|
|
61
|
-
* Specifies the allowable elapsed time in seconds since the last time the End-User was actively
|
|
62
|
-
* authenticated. If last authentication time is more than maxAge seconds in the past,
|
|
63
|
-
* re-authentication will be required. See the OpenID Connect spec section 3.1.2.1 for more
|
|
64
|
-
* information
|
|
65
|
-
*/
|
|
66
|
-
maxAge?: number | string;
|
|
67
|
-
/**
|
|
68
|
-
* Optional parameter to overwrite client locale setting.
|
|
69
|
-
* New flows supports nb_NO, fi_FI, sv_SE, en_US
|
|
70
|
-
*/
|
|
71
|
-
locale?: string;
|
|
72
|
-
/** display username and password on one screen. Defaults to `false`. */
|
|
73
|
-
oneStepLogin?: boolean;
|
|
74
|
-
/**
|
|
75
|
-
* String that specifies whether the Authorization Server prompts the End-User for
|
|
76
|
-
* reauthentication or confirm account screen. Supported values: `select_account` or `login`.
|
|
77
|
-
* Defaults to `select_account`.
|
|
78
|
-
*/
|
|
79
|
-
prompt?: string;
|
|
80
|
-
/** Identifier for cross-domain tracking in Pulse */
|
|
81
|
-
xDomainId?: string;
|
|
82
|
-
/** Environment for cross-domain tracking in Pulse */
|
|
83
|
-
xEnvironmentId?: string;
|
|
84
|
-
/** Campaign identifier for tracking in Pulse */
|
|
85
|
-
originCampaign?: string;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Identical to {@link LoginOptions} except that `state` may also be a (possibly async) function;
|
|
90
|
-
* all other fields reuse the `LoginOptions` documentation.
|
|
91
|
-
*/
|
|
92
|
-
export type SimplifiedLoginWidgetLoginOptions = Omit<LoginOptions, 'state'> & {
|
|
93
|
-
/**
|
|
94
|
-
* An opaque value used by the client to maintain state between the request and callback.
|
|
95
|
-
* It's also recommended to prevent CSRF {@link https://tools.ietf.org/html/rfc6749#section-10.12}
|
|
96
|
-
*/
|
|
97
|
-
state: string | (() => string | Promise<string>);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Successful response returned from {@link Identity#hasSession}.
|
|
102
|
-
*/
|
|
103
|
-
export type HasSessionSuccessResponse = {
|
|
104
|
-
/**
|
|
105
|
-
* Is the user connected to the merchant? (it means that the merchant id is in the list of
|
|
106
|
-
* merchants listed of this user in the database)? Example: false
|
|
107
|
-
*/
|
|
108
|
-
result: boolean;
|
|
109
|
-
/** Example: 'notConnected' or 'connected'. Deprecated, use `Identity.isConnected()` */
|
|
110
|
-
userStatus: string;
|
|
111
|
-
/** Example: 'localhost' */
|
|
112
|
-
baseDomain: string;
|
|
113
|
-
/** Example: '58eca10fdbb9f6df72c3368f'. Obsolete */
|
|
114
|
-
id: string;
|
|
115
|
-
/** Example: 37162 */
|
|
116
|
-
userId: number;
|
|
117
|
-
/** Example: 'b3b23aa7-34f2-5d02-a10e-5a3455c6ab2c' */
|
|
118
|
-
uuid: string;
|
|
119
|
-
/** Example: 'eyJjbGllbnRfaWQ...' */
|
|
120
|
-
sp_id: string;
|
|
121
|
-
/** Example: 30 * 60 * 1000 (for 30 minutes) */
|
|
122
|
-
expiresIn: number;
|
|
123
|
-
/** Example: 1506285759 */
|
|
124
|
-
serverTime: number;
|
|
125
|
-
/**
|
|
126
|
-
* Example: 'NCdzXaz4ZRb7...' The sig parameter is a concatenation of an HMAC SHA-256 signature
|
|
127
|
-
* string, a dot (.) and a base64url encoded JSON object (session).
|
|
128
|
-
* {@link http://techdocs.spid.no/sdks/js/response-signature-and-validation/}
|
|
129
|
-
*/
|
|
130
|
-
sig: string;
|
|
131
|
-
/** (Only for connected users) Example: 'batman' */
|
|
132
|
-
displayName: string;
|
|
133
|
-
/** (Only for connected users) Example: 'Bruce' */
|
|
134
|
-
givenName: string;
|
|
135
|
-
/** (Only for connected users) Example: 'Wayne' */
|
|
136
|
-
familyName: string;
|
|
137
|
-
/** (Only for connected users) Example: 'male', 'female', 'undisclosed' */
|
|
138
|
-
gender: string;
|
|
139
|
-
/** (Only for connected users) Example: 'http://www.srv.com/some/picture.jpg' */
|
|
140
|
-
photo: string;
|
|
141
|
-
/** (Only for connected users) */
|
|
142
|
-
tracking: boolean;
|
|
143
|
-
/** (Only for connected users) */
|
|
144
|
-
clientAgreementAccepted: boolean;
|
|
145
|
-
/** (Only for connected users) */
|
|
146
|
-
defaultAgreementAccepted: boolean;
|
|
147
|
-
pairId: string;
|
|
148
|
-
sdrn: string;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Failure response returned from {@link Identity#hasSession}.
|
|
153
|
-
*/
|
|
154
|
-
export type HasSessionFailureResponse = {
|
|
155
|
-
error: {
|
|
156
|
-
/** Typically an HTTP response code. Example: 401 */
|
|
157
|
-
code: number;
|
|
158
|
-
/** Example: "No session found!" */
|
|
159
|
-
description: string;
|
|
160
|
-
/** Example: "UserException" */
|
|
161
|
-
type: string;
|
|
162
|
-
};
|
|
163
|
-
response: {
|
|
164
|
-
/** Example: "localhost" */
|
|
165
|
-
baseDomain: string;
|
|
166
|
-
/** Time span in milliseconds. Example: 30 * 60 * 1000 (for 30 minutes) */
|
|
167
|
-
expiresIn: number;
|
|
168
|
-
result: boolean;
|
|
169
|
-
/** Server time in seconds since the Unix Epoch. Example: 1506287788 */
|
|
170
|
-
serverTime: number;
|
|
171
|
-
};
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Minimal user information used by the simplified login widget flow.
|
|
176
|
-
*/
|
|
177
|
-
export type SimplifiedLoginData = {
|
|
178
|
-
/** Deprecated: User UUID, to be be used as `loginHint` for {@link Identity#login} */
|
|
179
|
-
identifier: string;
|
|
180
|
-
/** Human-readable user identifier */
|
|
181
|
-
display_text: string;
|
|
182
|
-
/** Client name */
|
|
183
|
-
client_name: string;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Configuration options for {@link Identity#showSimplifiedLoginWidget}.
|
|
188
|
-
*/
|
|
189
|
-
export type SimplifiedLoginWidgetOptions = {
|
|
190
|
-
/** expected encoding of simplified login widget. Could be utf-8 (default), iso-8859-1 or iso-8859-15 */
|
|
191
|
-
encoding: string;
|
|
192
|
-
/**
|
|
193
|
-
* expected locale of simplified login widget. Should be provided in a short format like 'nb',
|
|
194
|
-
* 'sv'. If not set, a value from the env variable is used.
|
|
195
|
-
*/
|
|
196
|
-
locale?: 'nb' | 'sv' | 'fi' | 'da' | 'en';
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Emitted when an error happens (useful for debugging)
|
|
201
|
-
* @event Identity#error
|
|
202
|
-
*/
|
|
48
|
+
export { isConnectedSessionResponse, isSessionSuccessResponse } from './session-response.js';
|
|
49
|
+
export type {
|
|
50
|
+
ConnectedSessionResponse,
|
|
51
|
+
SessionFailureResponse,
|
|
52
|
+
SessionObjectResponse,
|
|
53
|
+
SessionResponse,
|
|
54
|
+
SessionSuccessResponse,
|
|
55
|
+
SessionUserId,
|
|
56
|
+
} from './types/session.js';
|
|
57
|
+
|
|
58
|
+
declare global {
|
|
59
|
+
interface Window {
|
|
60
|
+
openSimplifiedLoginWidget?: OpenSimplifiedLoginWidget;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
203
63
|
|
|
204
64
|
const HAS_SESSION_CACHE_KEY = 'hasSession-cache';
|
|
205
65
|
const SESSION_CALL_BLOCKED_CACHE_KEY = 'sessionCallBlocked-cache';
|
|
@@ -216,24 +76,28 @@ const globalWindow = () => window;
|
|
|
216
76
|
*/
|
|
217
77
|
export class Identity extends TinyEmitter {
|
|
218
78
|
_sessionInitiatedSent: boolean;
|
|
219
|
-
window:
|
|
79
|
+
window: Window;
|
|
220
80
|
clientId: string;
|
|
221
|
-
sessionStorageCache:
|
|
222
|
-
localStorageCache:
|
|
81
|
+
sessionStorageCache: Cache;
|
|
82
|
+
localStorageCache: Cache;
|
|
223
83
|
redirectUri: string;
|
|
224
84
|
env: string;
|
|
225
|
-
log?:
|
|
226
|
-
callbackBeforeRedirect:
|
|
85
|
+
log?: LogFunction;
|
|
86
|
+
callbackBeforeRedirect: IdentityBeforeRedirectCallback;
|
|
227
87
|
_sessionDomain: string;
|
|
228
88
|
_enableSessionCaching: boolean;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
89
|
+
/**
|
|
90
|
+
* @internal Starts as `{}` before the first `hasSession()` call, then stores the latest
|
|
91
|
+
* object response from hasSession().
|
|
92
|
+
*/
|
|
93
|
+
_session: SessionObjectResponse;
|
|
94
|
+
_spid: RESTClient;
|
|
95
|
+
_oauthService: RESTClient;
|
|
96
|
+
_bffService: RESTClient;
|
|
97
|
+
_sessionService: RESTClient;
|
|
98
|
+
_globalSessionService: RESTClient;
|
|
99
|
+
_usedSessionServiceGetSessionEndpoint: 'v2/session' | 'session';
|
|
100
|
+
_hasSessionInProgress?: false | Promise<SessionResponse>;
|
|
237
101
|
popup?: Window | null;
|
|
238
102
|
setVarnishCookie?: boolean;
|
|
239
103
|
varnishExpiresIn?: number;
|
|
@@ -259,15 +123,7 @@ export class Identity extends TinyEmitter {
|
|
|
259
123
|
log,
|
|
260
124
|
window = globalWindow(),
|
|
261
125
|
callbackBeforeRedirect = () => {},
|
|
262
|
-
}: {
|
|
263
|
-
clientId: string;
|
|
264
|
-
sessionDomain: string;
|
|
265
|
-
redirectUri: string;
|
|
266
|
-
env?: string;
|
|
267
|
-
log?: Function;
|
|
268
|
-
window?: Window;
|
|
269
|
-
callbackBeforeRedirect?: Function;
|
|
270
|
-
}) {
|
|
126
|
+
}: IdentityOptions) {
|
|
271
127
|
super();
|
|
272
128
|
assert(isNonEmptyString(clientId), 'clientId parameter is required');
|
|
273
129
|
assert(isObject(window), 'The reference to window is missing');
|
|
@@ -276,6 +132,7 @@ export class Identity extends TinyEmitter {
|
|
|
276
132
|
isNonEmptyString(sessionDomain) && isUrl(sessionDomain),
|
|
277
133
|
'sessionDomain parameter is not a valid URL',
|
|
278
134
|
);
|
|
135
|
+
assert(isStr(env), `env parameter is invalid: ${env}`);
|
|
279
136
|
|
|
280
137
|
spidTalk.emulate(window);
|
|
281
138
|
this._sessionInitiatedSent = false;
|
|
@@ -295,16 +152,43 @@ export class Identity extends TinyEmitter {
|
|
|
295
152
|
// Old session
|
|
296
153
|
this._session = {};
|
|
297
154
|
|
|
298
|
-
this.
|
|
155
|
+
const accountClientParams = { client_id: this.clientId, redirect_uri: this.redirectUri };
|
|
156
|
+
const clientSdrn = buildClientSdrn(this.env, this.clientId);
|
|
157
|
+
|
|
158
|
+
this._sessionService = createAccountRestClient({
|
|
159
|
+
serverUrl: sessionDomain,
|
|
160
|
+
log: this.log,
|
|
161
|
+
defaultParams: {
|
|
162
|
+
client_sdrn: clientSdrn,
|
|
163
|
+
redirect_uri: this.redirectUri,
|
|
164
|
+
sdk_version: version,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
299
167
|
this._usedSessionServiceGetSessionEndpoint =
|
|
300
168
|
this._sessionService.url.pathname && this._sessionService.url.pathname.length <= 1
|
|
301
169
|
? 'v2/session'
|
|
302
170
|
: 'session';
|
|
303
171
|
|
|
304
|
-
this.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
172
|
+
this._spid = createAccountRestClient({
|
|
173
|
+
serverUrl: urlMapper(env, ENDPOINTS.SPiD),
|
|
174
|
+
log: this.log,
|
|
175
|
+
defaultParams: accountClientParams,
|
|
176
|
+
});
|
|
177
|
+
this._bffService = createAccountRestClient({
|
|
178
|
+
serverUrl: urlMapper(env, ENDPOINTS.BFF),
|
|
179
|
+
log: this.log,
|
|
180
|
+
defaultParams: accountClientParams,
|
|
181
|
+
});
|
|
182
|
+
this._oauthService = createAccountRestClient({
|
|
183
|
+
serverUrl: urlMapper(env, ENDPOINTS.SPiD),
|
|
184
|
+
log: this.log,
|
|
185
|
+
defaultParams: accountClientParams,
|
|
186
|
+
});
|
|
187
|
+
this._globalSessionService = createAccountRestClient({
|
|
188
|
+
serverUrl: urlMapper(env, ENDPOINTS.SESSION_SERVICE),
|
|
189
|
+
log: this.log,
|
|
190
|
+
defaultParams: { client_sdrn: clientSdrn, sdk_version: version },
|
|
191
|
+
});
|
|
308
192
|
|
|
309
193
|
this._unblockSessionCall();
|
|
310
194
|
|
|
@@ -318,7 +202,7 @@ export class Identity extends TinyEmitter {
|
|
|
318
202
|
_getTabId(): number | undefined {
|
|
319
203
|
if (this._enableSessionCaching) {
|
|
320
204
|
const tabId = this.sessionStorageCache.get(TAB_ID_KEY);
|
|
321
|
-
if (
|
|
205
|
+
if (typeof tabId !== 'number') {
|
|
322
206
|
this.sessionStorageCache.set(TAB_ID_KEY, TAB_ID, TAB_ID_TTL);
|
|
323
207
|
return TAB_ID;
|
|
324
208
|
}
|
|
@@ -332,8 +216,8 @@ export class Identity extends TinyEmitter {
|
|
|
332
216
|
* @private
|
|
333
217
|
*
|
|
334
218
|
*/
|
|
335
|
-
_isSessionCallBlocked(): boolean
|
|
336
|
-
return this.localStorageCache.get(SESSION_CALL_BLOCKED_CACHE_KEY);
|
|
219
|
+
_isSessionCallBlocked(): boolean {
|
|
220
|
+
return Boolean(this.localStorageCache.get(SESSION_CALL_BLOCKED_CACHE_KEY));
|
|
337
221
|
}
|
|
338
222
|
|
|
339
223
|
/**
|
|
@@ -361,97 +245,34 @@ export class Identity extends TinyEmitter {
|
|
|
361
245
|
}
|
|
362
246
|
|
|
363
247
|
/**
|
|
364
|
-
*
|
|
365
|
-
* @private
|
|
366
|
-
* @param url - real URL or 'PRE' style key
|
|
367
|
-
*/
|
|
368
|
-
_setSpidServerUrl(url: string): void {
|
|
369
|
-
assert(isStr(url), `url parameter is invalid: ${url}`);
|
|
370
|
-
this._spid = new RESTClient({
|
|
371
|
-
serverUrl: urlMapper(url, ENDPOINTS.SPiD),
|
|
372
|
-
log: this.log,
|
|
373
|
-
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Set OAuth server URL
|
|
379
|
-
* @private
|
|
380
|
-
* @param url - real URL or 'PRE' style key
|
|
381
|
-
*/
|
|
382
|
-
_setOauthServerUrl(url: string): void {
|
|
383
|
-
assert(isStr(url), `url parameter is invalid: ${url}`);
|
|
384
|
-
this._oauthService = new RESTClient({
|
|
385
|
-
serverUrl: urlMapper(url, ENDPOINTS.SPiD),
|
|
386
|
-
log: this.log,
|
|
387
|
-
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Set BFF server URL
|
|
393
|
-
* @private
|
|
394
|
-
* @param url - real URL or 'PRE' style key
|
|
395
|
-
*/
|
|
396
|
-
_setBffServerUrl(url: string): void {
|
|
397
|
-
assert(isStr(url), `url parameter is invalid: ${url}`);
|
|
398
|
-
this._bffService = new RESTClient({
|
|
399
|
-
serverUrl: urlMapper(url, ENDPOINTS.BFF),
|
|
400
|
-
log: this.log,
|
|
401
|
-
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Set site-specific session-service domain
|
|
407
|
-
* @private
|
|
408
|
-
* @param domain - real URL — (**not** 'PRE' style env key)
|
|
409
|
-
*/
|
|
410
|
-
_setSessionServiceUrl(domain: string): void {
|
|
411
|
-
assert(isStr(domain), `domain parameter is invalid: ${domain}`);
|
|
412
|
-
const client_sdrn = `sdrn:${NAMESPACE[this.env as keyof typeof NAMESPACE]}:client:${this.clientId}`;
|
|
413
|
-
this._sessionService = new RESTClient({
|
|
414
|
-
serverUrl: domain,
|
|
415
|
-
log: this.log,
|
|
416
|
-
defaultParams: { client_sdrn, redirect_uri: this.redirectUri, sdk_version: version },
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Set global session-service server URL
|
|
422
|
-
* @private
|
|
423
|
-
* @param url - real URL or 'PRE' style key
|
|
424
|
-
*/
|
|
425
|
-
_setGlobalSessionServiceUrl(url: string): void {
|
|
426
|
-
assert(isStr(url), `url parameter is invalid: ${url}`);
|
|
427
|
-
const client_sdrn = `sdrn:${NAMESPACE[this.env as keyof typeof NAMESPACE]}:client:${this.clientId}`;
|
|
428
|
-
this._globalSessionService = new RESTClient({
|
|
429
|
-
serverUrl: urlMapper(url, ENDPOINTS.SESSION_SERVICE),
|
|
430
|
-
log: this.log,
|
|
431
|
-
defaultParams: { client_sdrn, sdk_version: version },
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Emits the relevant events based on the previous and new reply from hassession
|
|
248
|
+
* Emits the relevant events based on the previous and new reply from hasSession
|
|
437
249
|
* @private
|
|
438
250
|
* @param previous
|
|
439
251
|
* @param current
|
|
440
252
|
*/
|
|
441
|
-
_emitSessionEvent(previous:
|
|
253
|
+
_emitSessionEvent(previous: object, current: object): void {
|
|
254
|
+
const previousUserId = isSessionSuccessResponse(previous) ? previous.userId : undefined;
|
|
255
|
+
const currentUserId = isSessionSuccessResponse(current) ? current.userId : undefined;
|
|
256
|
+
const previousUserStatus = isSessionSuccessResponse(previous)
|
|
257
|
+
? previous.userStatus
|
|
258
|
+
: undefined;
|
|
259
|
+
const currentUserStatus = isSessionSuccessResponse(current)
|
|
260
|
+
? current.userStatus
|
|
261
|
+
: undefined;
|
|
262
|
+
|
|
442
263
|
/**
|
|
443
264
|
* Emitted when the user is logged in (This happens as a result of calling
|
|
444
265
|
* {@link Identity#hasSession}, so it is also emitted if the user was previously logged in)
|
|
445
266
|
* @event Identity#login
|
|
446
267
|
*/
|
|
447
|
-
if (
|
|
268
|
+
if (currentUserId) {
|
|
448
269
|
this.emit('login', current);
|
|
449
270
|
}
|
|
450
271
|
/**
|
|
451
272
|
* Emitted when the user logged out
|
|
452
273
|
* @event Identity#logout
|
|
453
274
|
*/
|
|
454
|
-
if (
|
|
275
|
+
if (previousUserId && !currentUserId) {
|
|
455
276
|
this.emit('logout', current);
|
|
456
277
|
}
|
|
457
278
|
/**
|
|
@@ -460,10 +281,10 @@ export class Identity extends TinyEmitter {
|
|
|
460
281
|
* this invocation, and the userId has now changed
|
|
461
282
|
* @event Identity#userChange
|
|
462
283
|
*/
|
|
463
|
-
if (
|
|
284
|
+
if (previousUserId && currentUserId && previousUserId !== currentUserId) {
|
|
464
285
|
this.emit('userChange', current);
|
|
465
286
|
}
|
|
466
|
-
if (
|
|
287
|
+
if (previousUserId || currentUserId) {
|
|
467
288
|
/**
|
|
468
289
|
* Emitted when the session is changed. More accurately, this event is emitted if there
|
|
469
290
|
* was a logged-in user either before or after {@link Identity#hasSession} was called.
|
|
@@ -483,7 +304,7 @@ export class Identity extends TinyEmitter {
|
|
|
483
304
|
* Emitted when the session is first created
|
|
484
305
|
* @event Identity#sessionInit
|
|
485
306
|
*/
|
|
486
|
-
if (
|
|
307
|
+
if (currentUserId && !this._sessionInitiatedSent) {
|
|
487
308
|
this._sessionInitiatedSent = true;
|
|
488
309
|
this.emit('sessionInit', current);
|
|
489
310
|
}
|
|
@@ -492,7 +313,7 @@ export class Identity extends TinyEmitter {
|
|
|
492
313
|
* {@link Identity#hasSession}
|
|
493
314
|
* @event Identity#statusChange
|
|
494
315
|
*/
|
|
495
|
-
if (
|
|
316
|
+
if (previousUserStatus !== currentUserStatus) {
|
|
496
317
|
this.emit('statusChange', current);
|
|
497
318
|
}
|
|
498
319
|
}
|
|
@@ -518,16 +339,13 @@ export class Identity extends TinyEmitter {
|
|
|
518
339
|
* cookie expires. The default is to use the same time that hasSession responses are cached for
|
|
519
340
|
* @param options.domain Override cookie domain. E.g. «vg.no» instead of «www.vg.no»
|
|
520
341
|
*/
|
|
521
|
-
enableVarnishCookie(options
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
if (Number.isInteger(options)) {
|
|
525
|
-
expiresIn = options as any;
|
|
526
|
-
} else if (typeof options == 'object') {
|
|
527
|
-
expiresIn = (options as any).expiresIn || expiresIn;
|
|
528
|
-
domain = (options as any).domain || domain;
|
|
342
|
+
enableVarnishCookie(options: VarnishCookieOptions): void {
|
|
343
|
+
if (typeof options !== 'object') {
|
|
344
|
+
throw new SDKError('VarnishCookieOptions must be an object');
|
|
529
345
|
}
|
|
530
346
|
|
|
347
|
+
const { expiresIn = 0, domain } = options;
|
|
348
|
+
|
|
531
349
|
assert(Number.isInteger(expiresIn), `'expiresIn' must be an integer`);
|
|
532
350
|
assert(expiresIn >= 0, `'expiresIn' cannot be negative`);
|
|
533
351
|
this.setVarnishCookie = true;
|
|
@@ -540,8 +358,8 @@ export class Identity extends TinyEmitter {
|
|
|
540
358
|
* @private
|
|
541
359
|
* @param sessionData
|
|
542
360
|
*/
|
|
543
|
-
_maybeSetVarnishCookie(sessionData:
|
|
544
|
-
if (!this.setVarnishCookie) {
|
|
361
|
+
_maybeSetVarnishCookie(sessionData: object): void {
|
|
362
|
+
if (!this.setVarnishCookie || !isSessionSuccessResponse(sessionData)) {
|
|
545
363
|
return;
|
|
546
364
|
}
|
|
547
365
|
const date = new Date();
|
|
@@ -549,7 +367,7 @@ export class Identity extends TinyEmitter {
|
|
|
549
367
|
this.varnishExpiresIn ||
|
|
550
368
|
(typeof sessionData.expiresIn === 'number' && sessionData.expiresIn > 0);
|
|
551
369
|
if (validExpires) {
|
|
552
|
-
const expires = this.varnishExpiresIn || sessionData.expiresIn;
|
|
370
|
+
const expires = this.varnishExpiresIn || sessionData.expiresIn || 0;
|
|
553
371
|
date.setTime(date.getTime() + expires * 1000);
|
|
554
372
|
} else {
|
|
555
373
|
date.setTime(0);
|
|
@@ -560,7 +378,7 @@ export class Identity extends TinyEmitter {
|
|
|
560
378
|
this.varnishCookieDomain ||
|
|
561
379
|
(typeof sessionData.baseDomain === 'string'
|
|
562
380
|
? sessionData.baseDomain
|
|
563
|
-
:
|
|
381
|
+
: this.window.location.hostname) ||
|
|
564
382
|
'';
|
|
565
383
|
|
|
566
384
|
const cookie = [
|
|
@@ -588,9 +406,11 @@ export class Identity extends TinyEmitter {
|
|
|
588
406
|
*/
|
|
589
407
|
_clearVarnishCookie(): void {
|
|
590
408
|
const baseDomain =
|
|
591
|
-
this._session &&
|
|
409
|
+
this._session &&
|
|
410
|
+
isSessionSuccessResponse(this._session) &&
|
|
411
|
+
typeof this._session.baseDomain === 'string'
|
|
592
412
|
? this._session.baseDomain
|
|
593
|
-
:
|
|
413
|
+
: this.window.location.hostname;
|
|
594
414
|
|
|
595
415
|
const domain = this.varnishCookieDomain || baseDomain || '';
|
|
596
416
|
|
|
@@ -634,21 +454,24 @@ export class Identity extends TinyEmitter {
|
|
|
634
454
|
* @fires Identity#statusChange
|
|
635
455
|
* @fires Identity#error
|
|
636
456
|
*/
|
|
637
|
-
hasSession(): Promise<
|
|
457
|
+
hasSession(): Promise<SessionResponse> {
|
|
638
458
|
const isSessionCallBlocked = this._isSessionCallBlocked();
|
|
459
|
+
|
|
639
460
|
if (isSessionCallBlocked) {
|
|
640
|
-
|
|
461
|
+
const sessionData = normalizeSessionResponse(this._session);
|
|
462
|
+
return Promise.resolve(isRedirectSessionPayload(sessionData) ? {} : sessionData);
|
|
641
463
|
}
|
|
642
464
|
|
|
643
465
|
if (this._hasSessionInProgress) {
|
|
644
|
-
return this._hasSessionInProgress
|
|
645
|
-
HasSessionSuccessResponse | HasSessionFailureResponse
|
|
646
|
-
>;
|
|
466
|
+
return this._hasSessionInProgress;
|
|
647
467
|
}
|
|
648
468
|
|
|
649
|
-
const _postProcess = (sessionData:
|
|
650
|
-
if (sessionData
|
|
651
|
-
|
|
469
|
+
const _postProcess = (sessionData: SessionObjectResponse): SessionObjectResponse => {
|
|
470
|
+
if (isSessionFailureResponse(sessionData)) {
|
|
471
|
+
const errorObject = isObject(sessionData.error)
|
|
472
|
+
? sessionData.error
|
|
473
|
+
: { description: String(sessionData.error) };
|
|
474
|
+
throw new SDKError('HasSession failed', errorObject);
|
|
652
475
|
}
|
|
653
476
|
this._maybeSetVarnishCookie(sessionData);
|
|
654
477
|
this._emitSessionEvent(this._session, sessionData);
|
|
@@ -656,31 +479,45 @@ export class Identity extends TinyEmitter {
|
|
|
656
479
|
return sessionData;
|
|
657
480
|
};
|
|
658
481
|
|
|
659
|
-
const
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
return sessionDataKeys.length === 1 && sessionDataKeys[0] === 'redirectURL';
|
|
482
|
+
const _getCachedSession = (): NormalizedSessionPayload | null => {
|
|
483
|
+
const cachedSession = this.sessionStorageCache.get(HAS_SESSION_CACHE_KEY);
|
|
484
|
+
return cachedSession ? normalizeSessionResponse(cachedSession) : null;
|
|
663
485
|
};
|
|
664
486
|
|
|
665
|
-
const _getSession = async () => {
|
|
487
|
+
const _getSession = async (): Promise<SessionResponse> => {
|
|
666
488
|
if (this._enableSessionCaching) {
|
|
667
489
|
// Try to resolve from cache (it has a TTL)
|
|
668
|
-
const cachedSession =
|
|
490
|
+
const cachedSession = _getCachedSession();
|
|
669
491
|
if (cachedSession) {
|
|
492
|
+
if (isRedirectSessionPayload(cachedSession)) {
|
|
493
|
+
return {};
|
|
494
|
+
}
|
|
670
495
|
return _postProcess(cachedSession);
|
|
671
496
|
}
|
|
672
497
|
}
|
|
673
|
-
let sessionData = null;
|
|
498
|
+
let sessionData: NormalizedSessionPayload | null = null;
|
|
674
499
|
try {
|
|
675
500
|
sessionData = await this._sessionService.get(
|
|
676
501
|
this._usedSessionServiceGetSessionEndpoint,
|
|
677
502
|
{ tabId: this._getTabId() },
|
|
503
|
+
normalizeSessionResponse,
|
|
678
504
|
);
|
|
679
|
-
} catch (err:
|
|
680
|
-
|
|
681
|
-
|
|
505
|
+
} catch (err: unknown) {
|
|
506
|
+
const cacheableError = isObject(err) ? err : undefined;
|
|
507
|
+
if (cacheableError?.code === 400 && this._enableSessionCaching) {
|
|
508
|
+
const errorExpiresIn =
|
|
509
|
+
typeof cacheableError.expiresIn === 'number'
|
|
510
|
+
? cacheableError.expiresIn
|
|
511
|
+
: 300;
|
|
512
|
+
const expiresIn = 1000 * errorExpiresIn;
|
|
682
513
|
this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, { error: err }, expiresIn);
|
|
683
|
-
} else if (
|
|
514
|
+
} else if (
|
|
515
|
+
cacheableError &&
|
|
516
|
+
typeof cacheableError.code === 'number' &&
|
|
517
|
+
cacheableError.code >= 500 &&
|
|
518
|
+
cacheableError.code < 600 &&
|
|
519
|
+
this._enableSessionCaching
|
|
520
|
+
) {
|
|
684
521
|
// Temporary fix: 30 seconds cache to limit number of calls when service is unavailable
|
|
685
522
|
this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, { error: err }, 30 * 1000);
|
|
686
523
|
}
|
|
@@ -689,33 +526,35 @@ export class Identity extends TinyEmitter {
|
|
|
689
526
|
|
|
690
527
|
if (sessionData) {
|
|
691
528
|
// for expiring session and safari browser do full page redirect to gain new session
|
|
692
|
-
if (
|
|
529
|
+
if (isRedirectSessionPayload(sessionData)) {
|
|
693
530
|
this._blockSessionCall();
|
|
694
531
|
|
|
695
532
|
await this.callbackBeforeRedirect();
|
|
696
533
|
|
|
697
|
-
|
|
534
|
+
const redirectUrl = this._sessionService.makeUrl(sessionData.redirectURL, {
|
|
698
535
|
tabId: this._getTabId(),
|
|
699
536
|
});
|
|
537
|
+
return redirectUrl;
|
|
700
538
|
}
|
|
701
539
|
|
|
702
540
|
if (this._enableSessionCaching) {
|
|
703
|
-
const expiresIn =
|
|
541
|
+
const expiresIn =
|
|
542
|
+
1000 *
|
|
543
|
+
(isSessionSuccessResponse(sessionData)
|
|
544
|
+
? sessionData.expiresIn || 300
|
|
545
|
+
: 300);
|
|
704
546
|
this.sessionStorageCache.set(HAS_SESSION_CACHE_KEY, sessionData, expiresIn);
|
|
705
547
|
}
|
|
706
548
|
}
|
|
707
549
|
|
|
708
|
-
return _postProcess(sessionData);
|
|
550
|
+
return _postProcess(sessionData ?? {});
|
|
709
551
|
};
|
|
710
552
|
this._hasSessionInProgress = _getSession().then(
|
|
711
553
|
(sessionData) => {
|
|
712
554
|
this._hasSessionInProgress = false;
|
|
713
|
-
|
|
714
|
-
if (isUrl(sessionData)) {
|
|
555
|
+
if (typeof sessionData === 'string') {
|
|
715
556
|
this.window.location.href = sessionData;
|
|
716
|
-
return sessionData;
|
|
717
557
|
}
|
|
718
|
-
|
|
719
558
|
return sessionData;
|
|
720
559
|
},
|
|
721
560
|
(err) => {
|
|
@@ -725,9 +564,7 @@ export class Identity extends TinyEmitter {
|
|
|
725
564
|
},
|
|
726
565
|
);
|
|
727
566
|
|
|
728
|
-
return this._hasSessionInProgress
|
|
729
|
-
HasSessionSuccessResponse | HasSessionFailureResponse
|
|
730
|
-
>;
|
|
567
|
+
return this._hasSessionInProgress;
|
|
731
568
|
}
|
|
732
569
|
|
|
733
570
|
/**
|
|
@@ -738,7 +575,7 @@ export class Identity extends TinyEmitter {
|
|
|
738
575
|
async isLoggedIn(): Promise<boolean> {
|
|
739
576
|
try {
|
|
740
577
|
const data = await this.hasSession();
|
|
741
|
-
return
|
|
578
|
+
return isSessionSuccessResponse(data);
|
|
742
579
|
} catch (_) {
|
|
743
580
|
return false;
|
|
744
581
|
}
|
|
@@ -761,10 +598,7 @@ export class Identity extends TinyEmitter {
|
|
|
761
598
|
*/
|
|
762
599
|
async isConnected(): Promise<boolean> {
|
|
763
600
|
try {
|
|
764
|
-
|
|
765
|
-
// if data is not an object, the promise will fail.
|
|
766
|
-
// if the result is present, it's boolean. But if it's not, it should be assumed false.
|
|
767
|
-
return !!data.result;
|
|
601
|
+
return isConnectedSessionResponse(await this.hasSession());
|
|
768
602
|
} catch (_) {
|
|
769
603
|
return false;
|
|
770
604
|
}
|
|
@@ -777,9 +611,9 @@ export class Identity extends TinyEmitter {
|
|
|
777
611
|
* @throws {SDKError} If the user isn't connected to the merchant
|
|
778
612
|
* @throws {SDKError} If we couldn't get the user
|
|
779
613
|
*/
|
|
780
|
-
async getUser(): Promise<
|
|
781
|
-
const user =
|
|
782
|
-
if (!user
|
|
614
|
+
async getUser(): Promise<ConnectedSessionResponse> {
|
|
615
|
+
const user = await this.hasSession();
|
|
616
|
+
if (!isConnectedSessionResponse(user)) {
|
|
783
617
|
throw new SDKError('The user is not connected to this merchant');
|
|
784
618
|
}
|
|
785
619
|
return cloneDeep(user);
|
|
@@ -800,9 +634,9 @@ export class Identity extends TinyEmitter {
|
|
|
800
634
|
* @return The `userId` field (not to be confused with the `uuid`)
|
|
801
635
|
*/
|
|
802
636
|
async getUserId(): Promise<string> {
|
|
803
|
-
const user =
|
|
804
|
-
if (user
|
|
805
|
-
return user.userId;
|
|
637
|
+
const user = await this.hasSession();
|
|
638
|
+
if (isConnectedSessionWithUserIdResponse(user)) {
|
|
639
|
+
return String(user.userId);
|
|
806
640
|
}
|
|
807
641
|
throw new SDKError('The user is not connected to this merchant');
|
|
808
642
|
}
|
|
@@ -829,30 +663,35 @@ export class Identity extends TinyEmitter {
|
|
|
829
663
|
* @return The merchant- and 3rd-party-specific `externalId`
|
|
830
664
|
*/
|
|
831
665
|
async getExternalId(externalParty: string, optionalSuffix: string = ''): Promise<string> {
|
|
832
|
-
const
|
|
666
|
+
const user = await this.hasSession();
|
|
667
|
+
const pairId = isSessionSuccessResponse(user) ? user.pairId : undefined;
|
|
833
668
|
|
|
834
669
|
if (!pairId) throw new SDKError('pairId missing in user session!');
|
|
835
670
|
|
|
836
671
|
if (!externalParty || externalParty.length === 0) {
|
|
837
672
|
throw new SDKError('externalParty cannot be empty');
|
|
838
673
|
}
|
|
839
|
-
const _toHexDigest = (hashBuffer:
|
|
674
|
+
const _toHexDigest = (hashBuffer: ArrayBuffer) => {
|
|
840
675
|
// convert buffer to byte array
|
|
841
676
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
842
677
|
// convert bytes to hex string
|
|
843
678
|
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
844
679
|
};
|
|
845
680
|
|
|
846
|
-
const _getSha256Digest = (data:
|
|
681
|
+
const _getSha256Digest = (data: BufferSource) => {
|
|
847
682
|
return crypto.subtle.digest('SHA-256', data);
|
|
848
683
|
};
|
|
849
684
|
|
|
850
|
-
const _hashMessage = async (message:
|
|
685
|
+
const _hashMessage = async (message: string) => {
|
|
851
686
|
const msgUint8 = new TextEncoder().encode(message);
|
|
852
|
-
return _getSha256Digest(msgUint8).then((it
|
|
687
|
+
return _getSha256Digest(msgUint8).then((it) => _toHexDigest(it));
|
|
853
688
|
};
|
|
854
689
|
|
|
855
|
-
const _constructMessage = (
|
|
690
|
+
const _constructMessage = (
|
|
691
|
+
pairId: string,
|
|
692
|
+
externalParty: string,
|
|
693
|
+
optionalSuffix: string,
|
|
694
|
+
) => {
|
|
856
695
|
return optionalSuffix
|
|
857
696
|
? `${pairId}:${externalParty}:${optionalSuffix}`
|
|
858
697
|
: `${pairId}:${externalParty}`;
|
|
@@ -868,9 +707,9 @@ export class Identity extends TinyEmitter {
|
|
|
868
707
|
* @throws {SDKError} If the SDRN is missing in user session object.
|
|
869
708
|
*/
|
|
870
709
|
async getUserSDRN(): Promise<string> {
|
|
871
|
-
const
|
|
872
|
-
if (sdrn) {
|
|
873
|
-
return sdrn;
|
|
710
|
+
const user = await this.hasSession();
|
|
711
|
+
if (isSessionSuccessResponse(user) && user.sdrn) {
|
|
712
|
+
return user.sdrn;
|
|
874
713
|
}
|
|
875
714
|
throw new SDKError('Failed to get SDRN from user session');
|
|
876
715
|
}
|
|
@@ -888,8 +727,8 @@ export class Identity extends TinyEmitter {
|
|
|
888
727
|
* @return The `uuid` field (not to be confused with the `userId`)
|
|
889
728
|
*/
|
|
890
729
|
async getUserUuid(): Promise<string> {
|
|
891
|
-
const user =
|
|
892
|
-
if (user.uuid && user.result) {
|
|
730
|
+
const user = await this.hasSession();
|
|
731
|
+
if (isSessionSuccessResponse(user) && user.uuid && user.result) {
|
|
893
732
|
return user.uuid;
|
|
894
733
|
}
|
|
895
734
|
throw new SDKError('The user is not connected to this merchant');
|
|
@@ -905,7 +744,16 @@ export class Identity extends TinyEmitter {
|
|
|
905
744
|
*/
|
|
906
745
|
async getUserContextData(): Promise<SimplifiedLoginData | null> {
|
|
907
746
|
try {
|
|
908
|
-
return await this._globalSessionService.get('user-context')
|
|
747
|
+
return await this._globalSessionService.get('user-context', undefined, (value) =>
|
|
748
|
+
isObject(value) && isStr(value.identifier)
|
|
749
|
+
? {
|
|
750
|
+
identifier: value.identifier,
|
|
751
|
+
display_text: isStr(value.display_text) ? value.display_text : '',
|
|
752
|
+
client_name: isStr(value.client_name) ? value.client_name : '',
|
|
753
|
+
provider_id: isStr(value.provider_id) ? value.provider_id : undefined,
|
|
754
|
+
}
|
|
755
|
+
: null,
|
|
756
|
+
);
|
|
909
757
|
} catch (_) {
|
|
910
758
|
return null;
|
|
911
759
|
}
|
|
@@ -938,7 +786,7 @@ export class Identity extends TinyEmitter {
|
|
|
938
786
|
*/
|
|
939
787
|
login({
|
|
940
788
|
state,
|
|
941
|
-
acrValues
|
|
789
|
+
acrValues,
|
|
942
790
|
scope = 'openid',
|
|
943
791
|
redirectUri = this.redirectUri,
|
|
944
792
|
preferPopup = false,
|
|
@@ -946,7 +794,7 @@ export class Identity extends TinyEmitter {
|
|
|
946
794
|
tag = '',
|
|
947
795
|
teaser = '',
|
|
948
796
|
maxAge = '',
|
|
949
|
-
locale
|
|
797
|
+
locale,
|
|
950
798
|
oneStepLogin = false,
|
|
951
799
|
prompt = 'select_account',
|
|
952
800
|
xDomainId = '',
|
|
@@ -993,8 +841,12 @@ export class Identity extends TinyEmitter {
|
|
|
993
841
|
*/
|
|
994
842
|
async getSpId(): Promise<string | null> {
|
|
995
843
|
try {
|
|
996
|
-
const user =
|
|
997
|
-
|
|
844
|
+
const user = await this.hasSession();
|
|
845
|
+
if (isSessionSuccessResponse(user)) {
|
|
846
|
+
return user.sp_id || null;
|
|
847
|
+
}
|
|
848
|
+
const spId = isObject(user) && 'sp_id' in user ? user.sp_id : undefined;
|
|
849
|
+
return isStr(spId) ? spId : null;
|
|
998
850
|
} catch (_) {
|
|
999
851
|
return null;
|
|
1000
852
|
}
|
|
@@ -1031,37 +883,23 @@ export class Identity extends TinyEmitter {
|
|
|
1031
883
|
* @param options.originCampaign
|
|
1032
884
|
* @return - The url
|
|
1033
885
|
*/
|
|
1034
|
-
loginUrl(
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
..._args: any[]
|
|
1052
|
-
): string {
|
|
1053
|
-
if (typeof arguments[0] !== 'object') {
|
|
1054
|
-
// backward compatibility
|
|
1055
|
-
state = arguments[0];
|
|
1056
|
-
acrValues = arguments[1];
|
|
1057
|
-
scope = arguments[2] || scope;
|
|
1058
|
-
redirectUri = arguments[3] || redirectUri;
|
|
1059
|
-
loginHint = arguments[4] || loginHint;
|
|
1060
|
-
tag = arguments[5] || tag;
|
|
1061
|
-
teaser = arguments[6] || teaser;
|
|
1062
|
-
maxAge = isNaN(arguments[7]) ? maxAge : arguments[7];
|
|
1063
|
-
}
|
|
1064
|
-
const isValidAcrValue = (acrValue: any) =>
|
|
886
|
+
loginUrl({
|
|
887
|
+
state,
|
|
888
|
+
acrValues = '',
|
|
889
|
+
scope = 'openid',
|
|
890
|
+
redirectUri = this.redirectUri,
|
|
891
|
+
loginHint = '',
|
|
892
|
+
tag = '',
|
|
893
|
+
teaser = '',
|
|
894
|
+
maxAge = '',
|
|
895
|
+
locale,
|
|
896
|
+
oneStepLogin = false,
|
|
897
|
+
prompt = 'select_account',
|
|
898
|
+
xDomainId = '',
|
|
899
|
+
xEnvironmentId = '',
|
|
900
|
+
originCampaign = '',
|
|
901
|
+
}: LoginOptions): string {
|
|
902
|
+
const isValidAcrValue = (acrValue: string) =>
|
|
1065
903
|
isStrIn(
|
|
1066
904
|
acrValue,
|
|
1067
905
|
['password', 'otp', 'sms', 'eid-dk', 'eid-no', 'eid-se', 'eid-fi', 'eid'],
|
|
@@ -1074,7 +912,7 @@ export class Identity extends TinyEmitter {
|
|
|
1074
912
|
`The acrValues parameter is not acceptable: ${acrValues}`,
|
|
1075
913
|
);
|
|
1076
914
|
assert(
|
|
1077
|
-
isUrl(redirectUri
|
|
915
|
+
isUrl(redirectUri),
|
|
1078
916
|
`loginUrl(): redirectUri must be a valid url but is ${redirectUri}`,
|
|
1079
917
|
);
|
|
1080
918
|
assert(
|
|
@@ -1144,12 +982,13 @@ export class Identity extends TinyEmitter {
|
|
|
1144
982
|
* @param options - additional configuration of Simplified Login Widget
|
|
1145
983
|
* @fires Identity#simplifiedLoginOpened
|
|
1146
984
|
* @fires Identity#simplifiedLoginCancelled
|
|
1147
|
-
* @
|
|
985
|
+
* @throws {SDKError} - If user context data is missing or the widget script cannot be loaded
|
|
986
|
+
* @return - will resolve to true if widget will be displayed
|
|
1148
987
|
*/
|
|
1149
988
|
async showSimplifiedLoginWidget(
|
|
1150
989
|
loginParams: SimplifiedLoginWidgetLoginOptions,
|
|
1151
990
|
options?: SimplifiedLoginWidgetOptions,
|
|
1152
|
-
): Promise<boolean
|
|
991
|
+
): Promise<boolean> {
|
|
1153
992
|
// getUserContextData doesn't throw exception
|
|
1154
993
|
const userData = await this.getUserContextData();
|
|
1155
994
|
|
|
@@ -1159,25 +998,32 @@ export class Identity extends TinyEmitter {
|
|
|
1159
998
|
}
|
|
1160
999
|
const widgetUrl = this._bffService.makeUrl('simplified-login-widget', queryParams, false);
|
|
1161
1000
|
|
|
1162
|
-
const prepareLoginParams = async (
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1001
|
+
const prepareLoginParams = async (
|
|
1002
|
+
loginParams: SimplifiedLoginWidgetLoginOptions,
|
|
1003
|
+
): Promise<LoginOptions> => {
|
|
1004
|
+
const state =
|
|
1005
|
+
typeof loginParams.state === 'function'
|
|
1006
|
+
? await loginParams.state()
|
|
1007
|
+
: loginParams.state;
|
|
1008
|
+
|
|
1009
|
+
return {
|
|
1010
|
+
...loginParams,
|
|
1011
|
+
state,
|
|
1012
|
+
};
|
|
1168
1013
|
};
|
|
1169
1014
|
|
|
1170
|
-
return new Promise((resolve, reject) => {
|
|
1171
|
-
if (!userData
|
|
1172
|
-
|
|
1015
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
1016
|
+
if (!userData?.display_text || !userData.identifier) {
|
|
1017
|
+
reject(new SDKError('Missing user data'));
|
|
1018
|
+
return;
|
|
1173
1019
|
}
|
|
1174
1020
|
|
|
1175
|
-
const initialParams:
|
|
1021
|
+
const initialParams: SimplifiedLoginWidgetInitialParams = {
|
|
1176
1022
|
displayText: userData.display_text,
|
|
1177
1023
|
env: this.env,
|
|
1178
|
-
clientName: userData.client_name,
|
|
1024
|
+
clientName: userData.client_name || '',
|
|
1179
1025
|
clientId: this.clientId,
|
|
1180
|
-
providerId:
|
|
1026
|
+
providerId: userData.provider_id,
|
|
1181
1027
|
windowWidth: () => window.innerWidth,
|
|
1182
1028
|
windowOnResize: (f: () => void) => {
|
|
1183
1029
|
window.onresize = f;
|
|
@@ -1214,15 +1060,11 @@ export class Identity extends TinyEmitter {
|
|
|
1214
1060
|
};
|
|
1215
1061
|
|
|
1216
1062
|
const cancelLoginHandler = () => {
|
|
1217
|
-
/**
|
|
1218
|
-
* Emitted when the user closes the simplified login widget
|
|
1219
|
-
* @event Identity#simplifiedLoginCancelled
|
|
1220
|
-
*/
|
|
1221
1063
|
this.emit('simplifiedLoginCancelled');
|
|
1222
1064
|
};
|
|
1223
1065
|
|
|
1224
|
-
if ((window
|
|
1225
|
-
|
|
1066
|
+
if (hasOpenSimplifiedLoginWidget(window)) {
|
|
1067
|
+
window.openSimplifiedLoginWidget(
|
|
1226
1068
|
initialParams,
|
|
1227
1069
|
loginHandler,
|
|
1228
1070
|
loginNotYouHandler,
|
|
@@ -1236,7 +1078,11 @@ export class Identity extends TinyEmitter {
|
|
|
1236
1078
|
simplifiedLoginWidget.type = 'text/javascript';
|
|
1237
1079
|
simplifiedLoginWidget.src = widgetUrl;
|
|
1238
1080
|
simplifiedLoginWidget.onload = () => {
|
|
1239
|
-
(window
|
|
1081
|
+
if (!hasOpenSimplifiedLoginWidget(window)) {
|
|
1082
|
+
reject(new SDKError('Error when loading simplified login widget content'));
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
window.openSimplifiedLoginWidget(
|
|
1240
1086
|
initialParams,
|
|
1241
1087
|
loginHandler,
|
|
1242
1088
|
loginNotYouHandler,
|