@imtbl/auth 2.10.7-alpha.2

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.
Files changed (43) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/LICENSE.md +176 -0
  3. package/dist/browser/index.mjs +397 -0
  4. package/dist/node/index.js +416 -0
  5. package/dist/node/index.mjs +397 -0
  6. package/dist/types/authManager.d.ts +62 -0
  7. package/dist/types/config.d.ts +19 -0
  8. package/dist/types/confirmation/confirmation.d.ts +28 -0
  9. package/dist/types/confirmation/embeddedLoginPrompt.d.ts +10 -0
  10. package/dist/types/confirmation/index.d.ts +3 -0
  11. package/dist/types/confirmation/popup.d.ts +8 -0
  12. package/dist/types/confirmation/types.d.ts +33 -0
  13. package/dist/types/errors.d.ts +30 -0
  14. package/dist/types/index.d.ts +8 -0
  15. package/dist/types/overlay/confirmationOverlay.d.ts +17 -0
  16. package/dist/types/overlay/constants.d.ts +7 -0
  17. package/dist/types/overlay/elements.d.ts +12 -0
  18. package/dist/types/overlay/embeddedLoginPromptOverlay.d.ts +7 -0
  19. package/dist/types/storage/LocalForageAsyncStorage.d.ts +11 -0
  20. package/dist/types/storage/device_credentials_manager.d.ts +6 -0
  21. package/dist/types/types.d.ts +112 -0
  22. package/dist/types/utils/logger.d.ts +4 -0
  23. package/dist/types/utils/token.d.ts +2 -0
  24. package/package.json +49 -0
  25. package/src/authManager.ts +659 -0
  26. package/src/config.ts +70 -0
  27. package/src/confirmation/confirmation.ts +275 -0
  28. package/src/confirmation/embeddedLoginPrompt.ts +146 -0
  29. package/src/confirmation/index.ts +3 -0
  30. package/src/confirmation/popup.ts +41 -0
  31. package/src/confirmation/types.ts +36 -0
  32. package/src/errors.ts +62 -0
  33. package/src/index.ts +33 -0
  34. package/src/overlay/confirmationOverlay.ts +85 -0
  35. package/src/overlay/constants.ts +221 -0
  36. package/src/overlay/elements.ts +187 -0
  37. package/src/overlay/embeddedLoginPromptOverlay.ts +37 -0
  38. package/src/storage/LocalForageAsyncStorage.ts +34 -0
  39. package/src/storage/device_credentials_manager.ts +34 -0
  40. package/src/types.ts +128 -0
  41. package/src/utils/logger.ts +15 -0
  42. package/src/utils/token.ts +35 -0
  43. package/tsconfig.json +15 -0
package/src/config.ts ADDED
@@ -0,0 +1,70 @@
1
+ import {
2
+ OidcConfiguration,
3
+ AuthModuleConfiguration,
4
+ PopupOverlayOptions,
5
+ } from './types';
6
+ import { PassportError, PassportErrorType } from './errors';
7
+
8
+ const validateConfiguration = <T>(
9
+ configuration: T,
10
+ requiredKeys: Array<keyof T>,
11
+ prefix?: string,
12
+ ) => {
13
+ const missingKeys = requiredKeys
14
+ .map((key) => !configuration[key] && key)
15
+ .filter((n) => n)
16
+ .join(', ');
17
+ if (missingKeys !== '') {
18
+ const errorMessage = prefix
19
+ ? `${prefix} - ${missingKeys} cannot be null`
20
+ : `${missingKeys} cannot be null`;
21
+ throw new PassportError(
22
+ errorMessage,
23
+ PassportErrorType.INVALID_CONFIGURATION,
24
+ );
25
+ }
26
+ };
27
+
28
+ /**
29
+ * Interface that any configuration must implement to work with AuthManager
30
+ */
31
+ export interface IAuthConfiguration {
32
+ readonly authenticationDomain: string;
33
+ readonly passportDomain: string;
34
+ readonly oidcConfiguration: OidcConfiguration;
35
+ readonly crossSdkBridgeEnabled: boolean;
36
+ readonly popupOverlayOptions?: PopupOverlayOptions;
37
+ }
38
+
39
+ export class AuthConfiguration implements IAuthConfiguration {
40
+ readonly authenticationDomain: string;
41
+
42
+ readonly passportDomain: string;
43
+
44
+ readonly oidcConfiguration: OidcConfiguration;
45
+
46
+ readonly crossSdkBridgeEnabled: boolean;
47
+
48
+ readonly popupOverlayOptions?: PopupOverlayOptions;
49
+
50
+ constructor({
51
+ authenticationDomain,
52
+ passportDomain,
53
+ crossSdkBridgeEnabled,
54
+ popupOverlayOptions,
55
+ ...oidcConfiguration
56
+ }: AuthModuleConfiguration) {
57
+ validateConfiguration(oidcConfiguration, [
58
+ 'clientId',
59
+ 'redirectUri',
60
+ ]);
61
+
62
+ this.oidcConfiguration = oidcConfiguration;
63
+ this.crossSdkBridgeEnabled = crossSdkBridgeEnabled || false;
64
+ this.popupOverlayOptions = popupOverlayOptions;
65
+
66
+ // Default to production auth domain if not provided
67
+ this.authenticationDomain = authenticationDomain || 'https://auth.immutable.com';
68
+ this.passportDomain = passportDomain || 'https://passport.immutable.com';
69
+ }
70
+ }
@@ -0,0 +1,275 @@
1
+ import * as GeneratedClients from '@imtbl/generated-clients';
2
+ import { trackError } from '@imtbl/metrics';
3
+
4
+ import {
5
+ ConfirmationResult,
6
+ PASSPORT_CONFIRMATION_EVENT_TYPE,
7
+ ConfirmationReceiveMessage,
8
+ ConfirmationSendMessage,
9
+ } from './types';
10
+ import { openPopupCenter } from './popup';
11
+ import { IAuthConfiguration } from '../config';
12
+ import ConfirmationOverlay from '../overlay/confirmationOverlay';
13
+
14
+ const CONFIRMATION_WINDOW_TITLE = 'Confirm this transaction';
15
+ const CONFIRMATION_WINDOW_HEIGHT = 720;
16
+ const CONFIRMATION_WINDOW_WIDTH = 480;
17
+ const CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION = 1000;
18
+
19
+ export const CONFIRMATION_IFRAME_ID = 'passport-confirm';
20
+ export const CONFIRMATION_IFRAME_STYLE = 'display: none; position: absolute;width:0px;height:0px;border:0;';
21
+
22
+ type MessageHandler = (arg0: MessageEvent) => void;
23
+
24
+ type MessageType = 'erc191' | 'eip712';
25
+
26
+ export default class ConfirmationScreen {
27
+ private config: IAuthConfiguration;
28
+
29
+ private confirmationWindow: Window | undefined;
30
+
31
+ private popupOptions: { width: number; height: number } | undefined;
32
+
33
+ private overlay: ConfirmationOverlay | undefined;
34
+
35
+ private overlayClosed: boolean;
36
+
37
+ private timer: NodeJS.Timeout | undefined;
38
+
39
+ constructor(config: IAuthConfiguration) {
40
+ this.config = config;
41
+ this.overlayClosed = false;
42
+ }
43
+
44
+ private getHref(relativePath: string, queryStringParams?: { [key: string]: any }) {
45
+ let href = `${this.config.passportDomain}/transaction-confirmation/${relativePath}`;
46
+
47
+ if (queryStringParams) {
48
+ const queryString = queryStringParams
49
+ ? Object.keys(queryStringParams)
50
+ .map((key) => `${key}=${queryStringParams[key]}`)
51
+ .join('&')
52
+ : '';
53
+
54
+ href = `${href}?${queryString}`;
55
+ }
56
+
57
+ return href;
58
+ }
59
+
60
+ requestConfirmation(
61
+ transactionId: string,
62
+ etherAddress: string,
63
+ chainType: GeneratedClients.mr.TransactionApprovalRequestChainTypeEnum,
64
+ chainId?: string,
65
+ ): Promise<ConfirmationResult> {
66
+ return new Promise((resolve, reject) => {
67
+ const messageHandler = ({ data, origin }: MessageEvent) => {
68
+ if (
69
+ origin !== this.config.passportDomain
70
+ || data.eventType !== PASSPORT_CONFIRMATION_EVENT_TYPE
71
+ ) {
72
+ return;
73
+ }
74
+
75
+ switch (data.messageType as ConfirmationReceiveMessage) {
76
+ case ConfirmationReceiveMessage.CONFIRMATION_WINDOW_READY: {
77
+ this.confirmationWindow?.postMessage({
78
+ eventType: PASSPORT_CONFIRMATION_EVENT_TYPE,
79
+ messageType: ConfirmationSendMessage.CONFIRMATION_START,
80
+ }, this.config.passportDomain);
81
+ break;
82
+ }
83
+ case ConfirmationReceiveMessage.TRANSACTION_CONFIRMED: {
84
+ this.closeWindow();
85
+ resolve({ confirmed: true });
86
+ break;
87
+ }
88
+ case ConfirmationReceiveMessage.TRANSACTION_REJECTED: {
89
+ this.closeWindow();
90
+ resolve({ confirmed: false });
91
+ break;
92
+ }
93
+ case ConfirmationReceiveMessage.TRANSACTION_ERROR: {
94
+ this.closeWindow();
95
+ reject(new Error('Error during transaction confirmation'));
96
+ break;
97
+ }
98
+ default:
99
+ this.closeWindow();
100
+ reject(new Error('Unsupported message type'));
101
+ }
102
+ };
103
+
104
+ let href = '';
105
+ if (chainType === GeneratedClients.mr.TransactionApprovalRequestChainTypeEnum.Starkex) {
106
+ href = this.getHref('transaction', { transactionId, etherAddress, chainType });
107
+ } else {
108
+ href = this.getHref('zkevm/transaction', {
109
+ transactionID: transactionId, etherAddress, chainType, chainID: chainId,
110
+ });
111
+ }
112
+ window.addEventListener('message', messageHandler);
113
+ this.showConfirmationScreen(href, messageHandler, resolve);
114
+ });
115
+ }
116
+
117
+ requestMessageConfirmation(
118
+ messageID: string,
119
+ etherAddress: string,
120
+ messageType?: MessageType,
121
+ ): Promise<ConfirmationResult> {
122
+ return new Promise((resolve, reject) => {
123
+ const messageHandler = ({ data, origin }: MessageEvent) => {
124
+ if (
125
+ origin !== this.config.passportDomain
126
+ || data.eventType !== PASSPORT_CONFIRMATION_EVENT_TYPE
127
+ ) {
128
+ return;
129
+ }
130
+ switch (data.messageType as ConfirmationReceiveMessage) {
131
+ case ConfirmationReceiveMessage.CONFIRMATION_WINDOW_READY: {
132
+ this.confirmationWindow?.postMessage({
133
+ eventType: PASSPORT_CONFIRMATION_EVENT_TYPE,
134
+ messageType: ConfirmationSendMessage.CONFIRMATION_START,
135
+ }, this.config.passportDomain);
136
+ break;
137
+ }
138
+ case ConfirmationReceiveMessage.MESSAGE_CONFIRMED: {
139
+ this.closeWindow();
140
+ resolve({ confirmed: true });
141
+ break;
142
+ }
143
+ case ConfirmationReceiveMessage.MESSAGE_REJECTED: {
144
+ this.closeWindow();
145
+ resolve({ confirmed: false });
146
+ break;
147
+ }
148
+ case ConfirmationReceiveMessage.MESSAGE_ERROR: {
149
+ this.closeWindow();
150
+ reject(new Error('Error during message confirmation'));
151
+ break;
152
+ }
153
+ default:
154
+ this.closeWindow();
155
+ reject(new Error('Unsupported message type'));
156
+ }
157
+ };
158
+
159
+ window.addEventListener('message', messageHandler);
160
+ const href = this.getHref('zkevm/message', {
161
+ messageID,
162
+ etherAddress,
163
+ ...(messageType ? { messageType } : {}),
164
+ });
165
+ this.showConfirmationScreen(href, messageHandler, resolve);
166
+ });
167
+ }
168
+
169
+ showServiceUnavailable(): Promise<void> {
170
+ return new Promise((_, reject) => {
171
+ this.showConfirmationScreen(
172
+ this.getHref('unavailable'),
173
+ () => {},
174
+ () => {
175
+ this.closeWindow();
176
+ reject(new Error('Service unavailable'));
177
+ },
178
+ );
179
+ });
180
+ }
181
+
182
+ loading(popupOptions?: { width: number; height: number }) {
183
+ if (this.config.crossSdkBridgeEnabled) {
184
+ // There is no need to open a confirmation window if cross-sdk bridge is enabled
185
+ return;
186
+ }
187
+
188
+ this.popupOptions = popupOptions;
189
+
190
+ try {
191
+ this.confirmationWindow = openPopupCenter({
192
+ url: this.getHref('loading'),
193
+ title: CONFIRMATION_WINDOW_TITLE,
194
+ width: popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
195
+ height: popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
196
+ });
197
+ this.overlay = new ConfirmationOverlay(this.config.popupOverlayOptions || {});
198
+ } catch (error) {
199
+ // If an error is thrown here then the popup is blocked
200
+ const errorMessage = error instanceof Error ? error.message : String(error);
201
+ trackError('passport', 'confirmationPopupDenied', new Error(errorMessage));
202
+ this.overlay = new ConfirmationOverlay(this.config.popupOverlayOptions || {}, true);
203
+ }
204
+
205
+ this.overlay.append(
206
+ () => {
207
+ try {
208
+ this.confirmationWindow?.close();
209
+ this.confirmationWindow = openPopupCenter({
210
+ url: this.getHref('loading'),
211
+ title: CONFIRMATION_WINDOW_TITLE,
212
+ width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
213
+ height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
214
+ });
215
+ } catch { /* Empty */ }
216
+ },
217
+ () => {
218
+ this.overlayClosed = true;
219
+ this.closeWindow();
220
+ },
221
+ );
222
+ }
223
+
224
+ closeWindow() {
225
+ this.confirmationWindow?.close();
226
+ this.overlay?.remove();
227
+ this.overlay = undefined;
228
+ }
229
+
230
+ showConfirmationScreen(href: string, messageHandler: MessageHandler, resolve: Function) {
231
+ // If popup blocked, the confirmation window will not exist
232
+ if (this.confirmationWindow) {
233
+ this.confirmationWindow.location.href = href;
234
+ }
235
+
236
+ // This indicates the user closed the overlay so the transaction should be rejected
237
+ if (!this.overlay) {
238
+ this.overlayClosed = false;
239
+ resolve({ confirmed: false });
240
+ return;
241
+ }
242
+
243
+ // https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
244
+ const timerCallback = () => {
245
+ if (this.confirmationWindow?.closed || this.overlayClosed) {
246
+ clearInterval(this.timer);
247
+ window.removeEventListener('message', messageHandler);
248
+ resolve({ confirmed: false });
249
+ this.overlayClosed = false;
250
+ this.confirmationWindow = undefined;
251
+ }
252
+ };
253
+ this.timer = setInterval(
254
+ timerCallback,
255
+ CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION,
256
+ );
257
+ this.overlay.update(() => this.recreateConfirmationWindow(href, timerCallback));
258
+ }
259
+
260
+ private recreateConfirmationWindow(href: string, timerCallback: () => void) {
261
+ try {
262
+ // Clears and recreates the timer to ensure when the confirmation window
263
+ // is closed and recreated the transaction is not rejected.
264
+ clearInterval(this.timer);
265
+ this.confirmationWindow?.close();
266
+ this.confirmationWindow = openPopupCenter({
267
+ url: href,
268
+ title: CONFIRMATION_WINDOW_TITLE,
269
+ width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
270
+ height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
271
+ });
272
+ this.timer = setInterval(timerCallback, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
273
+ } catch { /* Empty */ }
274
+ }
275
+ }
@@ -0,0 +1,146 @@
1
+ import { Detail, getDetail } from '@imtbl/metrics';
2
+ import {
3
+ EMBEDDED_LOGIN_PROMPT_EVENT_TYPE,
4
+ EmbeddedLoginPromptResult,
5
+ EmbeddedLoginPromptReceiveMessage,
6
+ } from './types';
7
+ import { IAuthConfiguration } from '../config';
8
+ import EmbeddedLoginPromptOverlay from '../overlay/embeddedLoginPromptOverlay';
9
+
10
+ const LOGIN_PROMPT_WINDOW_HEIGHT = 660;
11
+ const LOGIN_PROMPT_WINDOW_WIDTH = 440;
12
+ const LOGIN_PROMPT_WINDOW_BORDER_RADIUS = '16px';
13
+ const LOGIN_PROMPT_KEYFRAME_STYLES_ID = 'passport-embedded-login-keyframes';
14
+ const LOGIN_PROMPT_IFRAME_ID = 'passport-embedded-login-iframe';
15
+
16
+ export default class EmbeddedLoginPrompt {
17
+ private config: IAuthConfiguration;
18
+
19
+ constructor(config: IAuthConfiguration) {
20
+ this.config = config;
21
+ }
22
+
23
+ private getHref = (anonymousId?: string) => {
24
+ let href = `${this.config.authenticationDomain}/im-embedded-login-prompt`
25
+ + `?client_id=${this.config.oidcConfiguration.clientId}`
26
+ + `&rid=${getDetail(Detail.RUNTIME_ID)}`;
27
+
28
+ if (anonymousId) {
29
+ href += `&third_party_a_id=${anonymousId}`;
30
+ }
31
+
32
+ return href;
33
+ };
34
+
35
+ private static appendIFrameStylesIfNeeded = () => {
36
+ if (document.getElementById(LOGIN_PROMPT_KEYFRAME_STYLES_ID)) {
37
+ return;
38
+ }
39
+
40
+ const style = document.createElement('style');
41
+ style.id = LOGIN_PROMPT_KEYFRAME_STYLES_ID;
42
+ style.textContent = `
43
+ @keyframes passportEmbeddedLoginPromptPopBounceIn {
44
+ 0% {
45
+ opacity: 0.5;
46
+ }
47
+ 50% {
48
+ opacity: 1;
49
+ transform: scale(1.05);
50
+ }
51
+ 75% {
52
+ transform: scale(0.98);
53
+ }
54
+ 100% {
55
+ opacity: 1;
56
+ transform: scale(1);
57
+ }
58
+ }
59
+
60
+ @media (max-height: 400px) {
61
+ #${LOGIN_PROMPT_IFRAME_ID} {
62
+ width: 100% !important;
63
+ max-width: none !important;
64
+ }
65
+ }
66
+
67
+ @keyframes passportEmbeddedLoginPromptOverlayFadeIn {
68
+ from {
69
+ opacity: 0;
70
+ }
71
+ to {
72
+ opacity: 1;
73
+ }
74
+ }
75
+ `;
76
+
77
+ document.head.appendChild(style);
78
+ };
79
+
80
+ private getEmbeddedLoginIFrame = (anonymousId?: string) => {
81
+ const embeddedLoginPrompt = document.createElement('iframe');
82
+ embeddedLoginPrompt.id = LOGIN_PROMPT_IFRAME_ID;
83
+ embeddedLoginPrompt.src = this.getHref(anonymousId);
84
+ embeddedLoginPrompt.style.height = '100vh';
85
+ embeddedLoginPrompt.style.width = '100vw';
86
+ embeddedLoginPrompt.style.maxHeight = `${LOGIN_PROMPT_WINDOW_HEIGHT}px`;
87
+ embeddedLoginPrompt.style.maxWidth = `${LOGIN_PROMPT_WINDOW_WIDTH}px`;
88
+ embeddedLoginPrompt.style.borderRadius = LOGIN_PROMPT_WINDOW_BORDER_RADIUS;
89
+
90
+ // Animation styles
91
+ embeddedLoginPrompt.style.opacity = '0';
92
+ embeddedLoginPrompt.style.transform = 'scale(0.6)';
93
+ embeddedLoginPrompt.style.animation = 'passportEmbeddedLoginPromptPopBounceIn 1s ease forwards';
94
+ EmbeddedLoginPrompt.appendIFrameStylesIfNeeded();
95
+
96
+ return embeddedLoginPrompt;
97
+ };
98
+
99
+ public displayEmbeddedLoginPrompt(anonymousId?: string): Promise<EmbeddedLoginPromptResult> {
100
+ return new Promise((resolve, reject) => {
101
+ const embeddedLoginPrompt = this.getEmbeddedLoginIFrame(anonymousId);
102
+ const messageHandler = ({ data, origin }: MessageEvent) => {
103
+ if (
104
+ origin !== this.config.authenticationDomain
105
+ || data.eventType !== EMBEDDED_LOGIN_PROMPT_EVENT_TYPE
106
+ ) {
107
+ return;
108
+ }
109
+
110
+ switch (data.messageType as EmbeddedLoginPromptReceiveMessage) {
111
+ case EmbeddedLoginPromptReceiveMessage.LOGIN_METHOD_SELECTED: {
112
+ const result = data.payload as EmbeddedLoginPromptResult;
113
+ window.removeEventListener('message', messageHandler);
114
+ EmbeddedLoginPromptOverlay.remove();
115
+ resolve(result);
116
+ break;
117
+ }
118
+ case EmbeddedLoginPromptReceiveMessage.LOGIN_PROMPT_ERROR: {
119
+ window.removeEventListener('message', messageHandler);
120
+ EmbeddedLoginPromptOverlay.remove();
121
+ reject(new Error('Error during embedded login prompt', { cause: data.payload }));
122
+ break;
123
+ }
124
+ case EmbeddedLoginPromptReceiveMessage.LOGIN_PROMPT_CLOSED: {
125
+ window.removeEventListener('message', messageHandler);
126
+ EmbeddedLoginPromptOverlay.remove();
127
+ reject(new Error('Popup closed by user'));
128
+ break;
129
+ }
130
+ default:
131
+ window.removeEventListener('message', messageHandler);
132
+ EmbeddedLoginPromptOverlay.remove();
133
+ reject(new Error(`Unsupported message type: ${data.messageType}`));
134
+ break;
135
+ }
136
+ };
137
+
138
+ window.addEventListener('message', messageHandler);
139
+ EmbeddedLoginPromptOverlay.appendOverlay(embeddedLoginPrompt, () => {
140
+ window.removeEventListener('message', messageHandler);
141
+ EmbeddedLoginPromptOverlay.remove();
142
+ reject(new Error('Popup closed by user'));
143
+ });
144
+ });
145
+ }
146
+ }
@@ -0,0 +1,3 @@
1
+ export { default as ConfirmationScreen } from './confirmation';
2
+ export { default as EmbeddedLoginPrompt } from './embeddedLoginPrompt';
3
+ export * from './types';
@@ -0,0 +1,41 @@
1
+ export type PopUpProps = {
2
+ url: string;
3
+ title: string;
4
+ width: number;
5
+ height: number;
6
+ query?: string;
7
+ };
8
+
9
+ export const openPopupCenter = ({
10
+ url,
11
+ title,
12
+ width,
13
+ height,
14
+ }: PopUpProps): Window => {
15
+ const left = Math.max(
16
+ 0,
17
+ Math.round(window.screenX + (window.outerWidth - width) / 2),
18
+ );
19
+ const top = Math.max(
20
+ 0,
21
+ Math.round(window.screenY + (window.outerHeight - height) / 2),
22
+ );
23
+
24
+ const newWindow = window.open(
25
+ url,
26
+ title,
27
+ `
28
+ scrollbars=yes,
29
+ width=${width},
30
+ height=${height},
31
+ top=${top},
32
+ left=${left}
33
+ `,
34
+ );
35
+ if (!newWindow) {
36
+ throw new Error('Failed to open confirmation screen');
37
+ }
38
+
39
+ newWindow.focus();
40
+ return newWindow;
41
+ };
@@ -0,0 +1,36 @@
1
+ import { DirectLoginMethod, MarketingConsentStatus } from '../types';
2
+
3
+ export enum ConfirmationSendMessage {
4
+ CONFIRMATION_START = 'confirmation_start',
5
+ }
6
+
7
+ export enum ConfirmationReceiveMessage {
8
+ CONFIRMATION_WINDOW_READY = 'confirmation_window_ready',
9
+ TRANSACTION_CONFIRMED = 'transaction_confirmed',
10
+ TRANSACTION_ERROR = 'transaction_error',
11
+ TRANSACTION_REJECTED = 'transaction_rejected',
12
+ MESSAGE_CONFIRMED = 'message_confirmed',
13
+ MESSAGE_ERROR = 'message_error',
14
+ MESSAGE_REJECTED = 'message_rejected',
15
+ }
16
+
17
+ export enum EmbeddedLoginPromptReceiveMessage {
18
+ LOGIN_METHOD_SELECTED = 'login_method_selected',
19
+ LOGIN_PROMPT_ERROR = 'login_prompt_error',
20
+ LOGIN_PROMPT_CLOSED = 'login_prompt_closed',
21
+ }
22
+
23
+ export type ConfirmationResult = {
24
+ confirmed: boolean;
25
+ };
26
+
27
+ export type EmbeddedLoginPromptResult = {
28
+ marketingConsentStatus: MarketingConsentStatus;
29
+ imPassportTraceId: string;
30
+ } & (
31
+ | { directLoginMethod: 'email'; email: string }
32
+ | { directLoginMethod: Exclude<DirectLoginMethod, 'email'>; email?: never }
33
+ );
34
+
35
+ export const PASSPORT_CONFIRMATION_EVENT_TYPE = 'imx_passport_confirmation';
36
+ export const EMBEDDED_LOGIN_PROMPT_EVENT_TYPE = 'im_passport_embedded_login_prompt';
package/src/errors.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { isAxiosError } from 'axios';
2
+ import { imx } from '@imtbl/generated-clients';
3
+
4
+ export enum PassportErrorType {
5
+ AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR',
6
+ INVALID_CONFIGURATION = 'INVALID_CONFIGURATION',
7
+ WALLET_CONNECTION_ERROR = 'WALLET_CONNECTION_ERROR',
8
+ NOT_LOGGED_IN_ERROR = 'NOT_LOGGED_IN_ERROR',
9
+ SILENT_LOGIN_ERROR = 'SILENT_LOGIN_ERROR',
10
+ REFRESH_TOKEN_ERROR = 'REFRESH_TOKEN_ERROR',
11
+ USER_REGISTRATION_ERROR = 'USER_REGISTRATION_ERROR',
12
+ USER_NOT_REGISTERED_ERROR = 'USER_NOT_REGISTERED_ERROR',
13
+ LOGOUT_ERROR = 'LOGOUT_ERROR',
14
+ TRANSFER_ERROR = 'TRANSFER_ERROR',
15
+ CREATE_ORDER_ERROR = 'CREATE_ORDER_ERROR',
16
+ CANCEL_ORDER_ERROR = 'CANCEL_ORDER_ERROR',
17
+ EXCHANGE_TRANSFER_ERROR = 'EXCHANGE_TRANSFER_ERROR',
18
+ CREATE_TRADE_ERROR = 'CREATE_TRADE_ERROR',
19
+ OPERATION_NOT_SUPPORTED_ERROR = 'OPERATION_NOT_SUPPORTED_ERROR',
20
+ LINK_WALLET_ALREADY_LINKED_ERROR = 'LINK_WALLET_ALREADY_LINKED_ERROR',
21
+ LINK_WALLET_MAX_WALLETS_LINKED_ERROR = 'LINK_WALLET_MAX_WALLETS_LINKED_ERROR',
22
+ LINK_WALLET_VALIDATION_ERROR = 'LINK_WALLET_VALIDATION_ERROR',
23
+ LINK_WALLET_DUPLICATE_NONCE_ERROR = 'LINK_WALLET_DUPLICATE_NONCE_ERROR',
24
+ LINK_WALLET_GENERIC_ERROR = 'LINK_WALLET_GENERIC_ERROR',
25
+ SERVICE_UNAVAILABLE_ERROR = 'SERVICE_UNAVAILABLE_ERROR',
26
+ }
27
+
28
+ export function isAPIError(error: any): error is imx.APIError {
29
+ return 'code' in error && 'message' in error;
30
+ }
31
+
32
+ export class PassportError extends Error {
33
+ public type: PassportErrorType;
34
+
35
+ constructor(message: string, type: PassportErrorType) {
36
+ super(message);
37
+ this.type = type;
38
+ }
39
+ }
40
+
41
+ export const withPassportError = async <T>(
42
+ fn: () => Promise<T>,
43
+ customErrorType: PassportErrorType,
44
+ ): Promise<T> => {
45
+ try {
46
+ return await fn();
47
+ } catch (error) {
48
+ let errorMessage: string;
49
+
50
+ if (error instanceof PassportError && error.type === PassportErrorType.SERVICE_UNAVAILABLE_ERROR) {
51
+ throw new PassportError(error.message, error.type);
52
+ }
53
+
54
+ if (isAxiosError(error) && error.response?.data && isAPIError(error.response.data)) {
55
+ errorMessage = error.response.data.message;
56
+ } else {
57
+ errorMessage = (error as Error).message;
58
+ }
59
+
60
+ throw new PassportError(errorMessage, customErrorType);
61
+ }
62
+ };
package/src/index.ts ADDED
@@ -0,0 +1,33 @@
1
+ // Export AuthManager for use by other packages
2
+ export { default as AuthManager } from './authManager';
3
+
4
+ // Export configuration
5
+ export { AuthConfiguration, type IAuthConfiguration } from './config';
6
+
7
+ // Export types
8
+ export type {
9
+ User,
10
+ UserProfile,
11
+ UserImx,
12
+ UserZkEvm,
13
+ DirectLoginMethod,
14
+ DirectLoginOptions,
15
+ DeviceTokenResponse,
16
+ OidcConfiguration,
17
+ AuthModuleConfiguration,
18
+ PopupOverlayOptions,
19
+ PassportMetadata,
20
+ IdTokenPayload,
21
+ PKCEData,
22
+ } from './types';
23
+ export {
24
+ isUserZkEvm, isUserImx, RollupType, MarketingConsentStatus,
25
+ } from './types';
26
+
27
+ // Export errors
28
+ export { PassportError, PassportErrorType, withPassportError } from './errors';
29
+
30
+ // Export confirmation and overlay classes
31
+ export { default as ConfirmationScreen } from './confirmation/confirmation';
32
+ export { default as EmbeddedLoginPrompt } from './confirmation/embeddedLoginPrompt';
33
+ export { default as ConfirmationOverlay } from './overlay/confirmationOverlay';