@imtbl/auth 2.10.7-alpha.6 → 2.11.1-alpha.1
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/.eslintrc.cjs +1 -1
- package/dist/browser/index.js +29 -31
- package/dist/node/index.cjs +26 -29
- package/dist/node/index.js +19 -22
- package/dist/types/Auth.d.ts +1 -1
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types/utils/jwt.d.ts +1 -0
- package/dist/types/utils/typedEventEmitter.d.ts +7 -4
- package/jest.config.ts +27 -0
- package/jest.setup.js +4 -0
- package/package.json +12 -9
- package/src/Auth.test.ts +188 -0
- package/src/Auth.ts +83 -67
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +35 -4
- package/src/index.ts +5 -1
- package/src/storage/device_credentials_manager.ts +2 -2
- package/src/types.ts +2 -0
- package/src/utils/jwt.ts +78 -0
- package/src/utils/token.ts +2 -2
- package/src/utils/typedEventEmitter.ts +36 -11
- package/tsconfig.eslint.json +6 -0
- package/tsconfig.json +4 -0
package/dist/node/index.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { UserManager, User, ErrorTimeout, ErrorResponse, InMemoryWebStorage, WebStorageStateStore } from 'oidc-client-ts';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import Ce from 'localforage';
|
|
5
|
-
import { track, getDetail, Detail, trackFlow, trackError, identify } from '@imtbl/metrics';
|
|
6
|
-
import { EventEmitter } from 'events';
|
|
2
|
+
import he from 'localforage';
|
|
3
|
+
import { track, identify, getDetail, Detail, trackFlow, trackError } from '@imtbl/metrics';
|
|
7
4
|
|
|
8
|
-
var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION="INVALID_CONFIGURATION",s.WALLET_CONNECTION_ERROR="WALLET_CONNECTION_ERROR",s.NOT_LOGGED_IN_ERROR="NOT_LOGGED_IN_ERROR",s.SILENT_LOGIN_ERROR="SILENT_LOGIN_ERROR",s.REFRESH_TOKEN_ERROR="REFRESH_TOKEN_ERROR",s.USER_REGISTRATION_ERROR="USER_REGISTRATION_ERROR",s.USER_NOT_REGISTERED_ERROR="USER_NOT_REGISTERED_ERROR",s.LOGOUT_ERROR="LOGOUT_ERROR",s.TRANSFER_ERROR="TRANSFER_ERROR",s.CREATE_ORDER_ERROR="CREATE_ORDER_ERROR",s.CANCEL_ORDER_ERROR="CANCEL_ORDER_ERROR",s.EXCHANGE_TRANSFER_ERROR="EXCHANGE_TRANSFER_ERROR",s.CREATE_TRADE_ERROR="CREATE_TRADE_ERROR",s.OPERATION_NOT_SUPPORTED_ERROR="OPERATION_NOT_SUPPORTED_ERROR",s.LINK_WALLET_ALREADY_LINKED_ERROR="LINK_WALLET_ALREADY_LINKED_ERROR",s.LINK_WALLET_MAX_WALLETS_LINKED_ERROR="LINK_WALLET_MAX_WALLETS_LINKED_ERROR",s.LINK_WALLET_VALIDATION_ERROR="LINK_WALLET_VALIDATION_ERROR",s.LINK_WALLET_DUPLICATE_NONCE_ERROR="LINK_WALLET_DUPLICATE_NONCE_ERROR",s.LINK_WALLET_GENERIC_ERROR="LINK_WALLET_GENERIC_ERROR",s.SERVICE_UNAVAILABLE_ERROR="SERVICE_UNAVAILABLE_ERROR",s))(P||{});function
|
|
5
|
+
var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION="INVALID_CONFIGURATION",s.WALLET_CONNECTION_ERROR="WALLET_CONNECTION_ERROR",s.NOT_LOGGED_IN_ERROR="NOT_LOGGED_IN_ERROR",s.SILENT_LOGIN_ERROR="SILENT_LOGIN_ERROR",s.REFRESH_TOKEN_ERROR="REFRESH_TOKEN_ERROR",s.USER_REGISTRATION_ERROR="USER_REGISTRATION_ERROR",s.USER_NOT_REGISTERED_ERROR="USER_NOT_REGISTERED_ERROR",s.LOGOUT_ERROR="LOGOUT_ERROR",s.TRANSFER_ERROR="TRANSFER_ERROR",s.CREATE_ORDER_ERROR="CREATE_ORDER_ERROR",s.CANCEL_ORDER_ERROR="CANCEL_ORDER_ERROR",s.EXCHANGE_TRANSFER_ERROR="EXCHANGE_TRANSFER_ERROR",s.CREATE_TRADE_ERROR="CREATE_TRADE_ERROR",s.OPERATION_NOT_SUPPORTED_ERROR="OPERATION_NOT_SUPPORTED_ERROR",s.LINK_WALLET_ALREADY_LINKED_ERROR="LINK_WALLET_ALREADY_LINKED_ERROR",s.LINK_WALLET_MAX_WALLETS_LINKED_ERROR="LINK_WALLET_MAX_WALLETS_LINKED_ERROR",s.LINK_WALLET_VALIDATION_ERROR="LINK_WALLET_VALIDATION_ERROR",s.LINK_WALLET_DUPLICATE_NONCE_ERROR="LINK_WALLET_DUPLICATE_NONCE_ERROR",s.LINK_WALLET_GENERIC_ERROR="LINK_WALLET_GENERIC_ERROR",s.SERVICE_UNAVAILABLE_ERROR="SERVICE_UNAVAILABLE_ERROR",s.TRANSACTION_REJECTED="TRANSACTION_REJECTED",s))(P||{});function b(n){return typeof n=="object"&&n!==null&&"code"in n&&"message"in n}var X=n=>{if(b(n))return n;if(typeof n=="object"&&n!==null&&"response"in n){let{response:e}=n;if(e?.data&&b(e.data))return e.data}},p=class extends Error{type;constructor(e,t){super(e),this.type=t;}},g=async(n,e)=>{try{return await n()}catch(t){let r;if(t instanceof p&&t.type==="SERVICE_UNAVAILABLE_ERROR")throw new p(t.message,t.type);let i=X(t);throw i?r=i.message:r=t.message,new p(r,e)}};var ee=(n,e,t)=>{let r=e.map(i=>!n[i]&&i).filter(i=>i).join(", ");if(r!==""){let i=`${r} cannot be null`;throw new p(i,"INVALID_CONFIGURATION")}},_=class{authenticationDomain;passportDomain;oidcConfiguration;crossSdkBridgeEnabled;popupOverlayOptions;constructor({authenticationDomain:e,passportDomain:t,crossSdkBridgeEnabled:r,popupOverlayOptions:i,...o}){ee(o,["clientId","redirectUri"]),this.oidcConfiguration=o,this.crossSdkBridgeEnabled=r||!1,this.popupOverlayOptions=i,this.authenticationDomain=e||"https://auth.immutable.com",this.passportDomain=t||"https://passport.immutable.com";}};var D=(e=>(e.ZKEVM="zkEvm",e))(D||{}),U=n=>!!n.zkEvm,F=(t=>(t.OptedIn="opted_in",t.Unsubscribed="unsubscribed",t))(F||{}),x=(t=>(t.LOGGED_OUT="loggedOut",t.LOGGED_IN="loggedIn",t))(x||{});var H="im_passport_embedded_login_prompt";var h="passport-overlay",R="passport-overlay-contents",I=`${h}-close`,w=`${h}-try-again`,V=`
|
|
9
6
|
<svg
|
|
10
7
|
viewBox="0 0 20 20"
|
|
11
8
|
fill="none"
|
|
@@ -31,7 +28,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
31
28
|
fill="#E01A3D"
|
|
32
29
|
/>
|
|
33
30
|
</svg>
|
|
34
|
-
`,
|
|
31
|
+
`,S=`
|
|
35
32
|
<svg
|
|
36
33
|
style="
|
|
37
34
|
max-width: 123px !important;
|
|
@@ -214,9 +211,9 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
214
211
|
</clipPath>
|
|
215
212
|
</defs>
|
|
216
213
|
</svg>
|
|
217
|
-
`;var
|
|
214
|
+
`;var te=()=>`
|
|
218
215
|
<button
|
|
219
|
-
id="${
|
|
216
|
+
id="${I}"
|
|
220
217
|
style="
|
|
221
218
|
background: #f3f3f326 !important;
|
|
222
219
|
border: none !important;
|
|
@@ -236,7 +233,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
236
233
|
</button>
|
|
237
234
|
`,B=()=>`
|
|
238
235
|
<button
|
|
239
|
-
id="${
|
|
236
|
+
id="${w}"
|
|
240
237
|
style="
|
|
241
238
|
margin-top: 27px !important;
|
|
242
239
|
color: #f3f3f3 !important;
|
|
@@ -251,8 +248,8 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
251
248
|
>
|
|
252
249
|
Try again
|
|
253
250
|
</button>
|
|
254
|
-
`,
|
|
255
|
-
${
|
|
251
|
+
`,re=()=>`
|
|
252
|
+
${S}
|
|
256
253
|
<div
|
|
257
254
|
style="
|
|
258
255
|
color: #e01a3d !important;
|
|
@@ -276,8 +273,8 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
276
273
|
browser settings.
|
|
277
274
|
</p>
|
|
278
275
|
${B()}
|
|
279
|
-
`,
|
|
280
|
-
${
|
|
276
|
+
`,ne=()=>`
|
|
277
|
+
${S}
|
|
281
278
|
<p style="
|
|
282
279
|
color: #b6b6b6 !important;
|
|
283
280
|
text-align: center !important;
|
|
@@ -287,7 +284,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
287
284
|
Secure pop-up not showing?<br />We'll help you re-launch
|
|
288
285
|
</p>
|
|
289
286
|
${B()}
|
|
290
|
-
`,
|
|
287
|
+
`,K=n=>`
|
|
291
288
|
<div
|
|
292
289
|
id="${h}"
|
|
293
290
|
style="
|
|
@@ -312,9 +309,9 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
312
309
|
z-index: 2147483647 !important;
|
|
313
310
|
"
|
|
314
311
|
>
|
|
315
|
-
${
|
|
312
|
+
${te()}
|
|
316
313
|
<div
|
|
317
|
-
id="${
|
|
314
|
+
id="${R}"
|
|
318
315
|
style="
|
|
319
316
|
display: flex !important;
|
|
320
317
|
flex-direction: column !important;
|
|
@@ -325,7 +322,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
325
322
|
${n??""}
|
|
326
323
|
</div>
|
|
327
324
|
</div>
|
|
328
|
-
`,
|
|
325
|
+
`,Z=()=>`
|
|
329
326
|
<div
|
|
330
327
|
id="${h}"
|
|
331
328
|
style="
|
|
@@ -345,7 +342,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
345
342
|
"
|
|
346
343
|
>
|
|
347
344
|
<div
|
|
348
|
-
id="${
|
|
345
|
+
id="${R}"
|
|
349
346
|
style="
|
|
350
347
|
display: flex;
|
|
351
348
|
flex-direction: column;
|
|
@@ -354,7 +351,7 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
354
351
|
"
|
|
355
352
|
/>
|
|
356
353
|
</div>
|
|
357
|
-
`;function
|
|
354
|
+
`;function A({id:n,href:e,rel:t,crossOrigin:r}){let i=`${h}-${n}`;if(!document.getElementById(i)){let o=document.createElement("link");o.id=i,o.href=e,t&&(o.rel=t),r&&(o.crossOrigin=r),document.head.appendChild(o);}}var W=()=>K(re()),$=()=>K(ne());var u=class{static overlay;static onCloseListener;static closeButton;static remove(){this.onCloseListener&&this.closeButton?.removeEventListener?.("click",this.onCloseListener),this.overlay?.remove(),this.closeButton=void 0,this.onCloseListener=void 0,this.overlay=void 0;}static appendOverlay(e,t){if(!this.overlay){let r=document.createElement("div");r.innerHTML=Z(),document.body.insertAdjacentElement("beforeend",r);let i=document.querySelector(`#${R}`);i&&i.appendChild(e),r.addEventListener("click",t),this.overlay=r;}}};var se=660,ae=440,le="16px",Y="passport-embedded-login-keyframes",z="passport-embedded-login-iframe",O=class n{config;constructor(e){this.config=e;}getHref=()=>`${this.config.authenticationDomain}/im-embedded-login-prompt?client_id=${this.config.oidcConfiguration.clientId}&rid=${getDetail(Detail.RUNTIME_ID)}`;static appendIFrameStylesIfNeeded=()=>{if(document.getElementById(Y))return;let e=document.createElement("style");e.id=Y,e.textContent=`
|
|
358
355
|
@keyframes passportEmbeddedLoginPromptPopBounceIn {
|
|
359
356
|
0% {
|
|
360
357
|
opacity: 0.5;
|
|
@@ -387,6 +384,6 @@ var P=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
387
384
|
opacity: 1;
|
|
388
385
|
}
|
|
389
386
|
}
|
|
390
|
-
`,document.head.appendChild(e);};getEmbeddedLoginIFrame=()=>{let e=document.createElement("iframe");return e.id=z,e.src=this.getHref(),e.style.height="100vh",e.style.width="100vw",e.style.maxHeight=`${le}px`,e.style.maxWidth=`${de}px`,e.style.borderRadius=ce,e.style.opacity="0",e.style.transform="scale(0.6)",e.style.animation="passportEmbeddedLoginPromptPopBounceIn 1s ease forwards",n.appendIFrameStylesIfNeeded(),e};displayEmbeddedLoginPrompt(){return new Promise((e,t)=>{let r=this.getEmbeddedLoginIFrame(),i=({data:o,origin:d})=>{if(!(d!==this.config.authenticationDomain||o.eventType!==H))switch(o.messageType){case"login_method_selected":{let l=o.payload;window.removeEventListener("message",i),u.remove(),e(l);break}case"login_prompt_error":{window.removeEventListener("message",i),u.remove(),t(new Error("Error during embedded login prompt",{cause:o.payload}));break}case"login_prompt_closed":{window.removeEventListener("message",i),u.remove(),t(new Error("Popup closed by user"));break}default:window.removeEventListener("message",i),u.remove(),t(new Error(`Unsupported message type: ${o.messageType}`));break}};window.addEventListener("message",i),u.appendOverlay(r,()=>{window.removeEventListener("message",i),u.remove(),t(new Error("Popup closed by user"));});})}};var C=class{emitter=new EventEmitter;emit(e,...t){this.emitter.emit(e,...t);}on(e,t){this.emitter.on(e,t);}removeListener(e,t){this.emitter.removeListener(e,t);}};var p=async(n,e,t=!0,r=!0)=>{let i=trackFlow("passport",e,t);try{return await n(i)}catch(o){throw o instanceof Error?trackError("passport",e,o,{flowId:i.details.flowId}):i.addEvent("errored"),o}finally{r&&i.addEvent("End");}};var j="pkce_state",Q="pkce_verifier",fe=3600,v=class{isTokenValid(e){try{let r=J(e).exp??0,i=Date.now()/1e3+fe;return r>i}catch{return !1}}savePKCEData(e){localStorage.setItem(j,e.state),localStorage.setItem(Q,e.verifier);}getPKCEData(){let e=localStorage.getItem(j),t=localStorage.getItem(Q);return e&&t?{state:e,verifier:t}:null}};var Ee=(...n)=>{if(typeof process>"u")return;process?.env?.JEST_WORKER_ID===void 0&&console.warn(...n);},_={warn:Ee};function q(n){try{let e=J(n),t=Math.floor(Date.now()/1e3);return e.exp?e.exp<=t+30:!0}catch{return !0}}function X(n){let{id_token:e,access_token:t}=n;return !t||!e?!0:q(t)||q(e)}var T=class{disableGenericPopupOverlay;disableBlockedPopupOverlay;overlay;isBlockedOverlay;tryAgainListener;onCloseListener;constructor(e,t=!1){this.disableBlockedPopupOverlay=e.disableBlockedPopupOverlay||!1,this.disableGenericPopupOverlay=e.disableGenericPopupOverlay||!1,this.isBlockedOverlay=t;}append(e,t){this.shouldAppendOverlay()&&(this.appendOverlay(),this.updateTryAgainButton(e),this.updateCloseButton(t));}update(e){this.updateTryAgainButton(e);}remove(){this.overlay&&this.overlay.remove();}shouldAppendOverlay(){return !(this.disableGenericPopupOverlay&&this.disableBlockedPopupOverlay||this.disableGenericPopupOverlay&&!this.isBlockedOverlay||this.disableBlockedPopupOverlay&&this.isBlockedOverlay)}appendOverlay(){if(!this.overlay){k({id:"link-googleapis",href:"https://fonts.googleapis.com"}),k({id:"link-gstatic",href:"https://fonts.gstatic.com",crossOrigin:"anonymous"}),k({id:"link-roboto",href:"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap",rel:"stylesheet"});let t=document.createElement("div");t.innerHTML=this.isBlockedOverlay?$():W(),document.body.insertAdjacentElement("beforeend",t),this.overlay=t;}}updateTryAgainButton(e){let t=document.getElementById(I);t&&(this.tryAgainListener&&t.removeEventListener("click",this.tryAgainListener),this.tryAgainListener=e,t.addEventListener("click",e));}updateCloseButton(e){let t=document.getElementById(L);t&&(this.onCloseListener&&t.removeEventListener("click",this.onCloseListener),this.onCloseListener=e,t.addEventListener("click",e));}};var w=class{storage;constructor(e,t){this.storage=Ce.createInstance({name:e,driver:t});}get length(){return this.storage.length()}clear(){return this.storage.clear()}getItem(e){return this.storage.getItem(e)}key(e){return this.storage.key(e)}async removeItem(e){await this.storage.removeItem(e);}async setItem(e,t){await this.storage.setItem(e,t);}};var Ue=500,be={headers:{"Content-Type":"application/x-www-form-urlencoded"}},xe="/v2/logout",Me="/im-logged-out",De="/authorize",Ne=n=>n?Me:xe,Se=n=>{let{authenticationDomain:e,oidcConfiguration:t}=n,r;n.crossSdkBridgeEnabled?r=new w("ImmutableSDKPassport",Ce.INDEXEDDB):typeof window<"u"?r=window.localStorage:r=new InMemoryWebStorage;let i=new WebStorageStateStore({store:r}),o=new URL(Ne(n.crossSdkBridgeEnabled),e.replace(/^(?:https?:\/\/)?(.*)/,"https://$1"));return o.searchParams.set("client_id",t.clientId),t.logoutRedirectUri&&o.searchParams.set("returnTo",t.logoutRedirectUri),{authority:e,redirect_uri:t.redirectUri,popup_redirect_uri:t.popupRedirectUri||t.redirectUri,client_id:t.clientId,metadata:{authorization_endpoint:`${e}/authorize`,token_endpoint:`${e}/oauth/token`,userinfo_endpoint:`${e}/userinfo`,end_session_endpoint:o.toString(),revocation_endpoint:`${e}/oauth/revoke`},automaticSilentRenew:!1,scope:t.scope,userStore:i,revokeTokenTypes:["refresh_token"],extraQueryParams:{...t.audience?{audience:t.audience}:{}}}};function M(n){return btoa(String.fromCharCode(...new Uint8Array(n))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function Fe(n){let t=new TextEncoder().encode(n);return window.crypto.subtle.digest("SHA-256",t)}var D=class n{config;userManager;deviceCredentialsManager;embeddedLoginPrompt;logoutMode;refreshingPromise=null;eventEmitter;constructor(e){this.config=new R(e),this.embeddedLoginPrompt=new y(this.config),this.userManager=new UserManager(Se(this.config)),this.deviceCredentialsManager=new v,this.logoutMode=this.config.oidcConfiguration.logoutMode||"redirect",this.eventEmitter=new C,track("passport","initialise");}async login(e){return p(async()=>{let{useCachedSession:t=!1,useSilentLogin:r}=e||{},i=null;try{i=await this.getUserInternal();}catch(o){if(o instanceof Error&&!o.message.includes("Unknown or invalid refresh token")&&trackError("passport","login",o),t)throw o;_.warn("Failed to retrieve a cached user session",o);}if(!i&&r)i=await this.forceUserRefreshInternal();else if(!i&&!t){if(e?.useRedirectFlow)return await this.loginWithRedirectInternal(e?.directLoginOptions),null;i=await this.loginWithPopup(e?.directLoginOptions);}return i&&(this.eventEmitter.emit("loggedIn",i),identify({passportId:i.profile.sub})),i},"login")}async loginWithRedirect(e){await this.loginWithRedirectInternal(e);}async loginCallback(){return p(async()=>{let e=await this.loginCallbackInternal();return e&&(this.eventEmitter.emit("loggedIn",e),identify({passportId:e.profile.sub})),e},"loginCallback")}async logout(){await p(async()=>{await this.logoutInternal(),this.eventEmitter.emit("loggedOut");},"logout");}async getUser(){return p(async()=>this.getUserInternal(),"getUserInfo",!1)}async getUserOrLogin(){let e=null;try{e=await this.getUserInternal();}catch(t){_.warn("Failed to retrieve a cached user session",t);}return e||this.loginWithPopup()}async getUserZkEvm(){return this.getUserZkEvmInternal()}async getIdToken(){return p(async()=>(await this.getUserInternal())?.idToken,"getIdToken",!1)}async getAccessToken(){return p(async()=>(await this.getUserInternal())?.accessToken,"getAccessToken",!1,!1)}async isLoggedIn(){return await this.getUser()!==null}async forceUserRefresh(){return this.forceUserRefreshInternal()}forceUserRefreshInBackground(){this.forceUserRefreshInBackgroundInternal();}async loginWithPKCEFlow(e,t){return p(async()=>this.getPKCEAuthorizationUrl(e,t),"loginWithPKCEFlow")}async loginWithPKCEFlowCallback(e,t){return p(async()=>{let r=await this.loginWithPKCEFlowCallbackInternal(e,t);return this.eventEmitter.emit("loggedIn",r),identify({passportId:r.profile.sub}),r},"loginWithPKCEFlowCallback")}async storeTokens(e){return p(async()=>{let t=await this.storeTokensInternal(e);return this.eventEmitter.emit("loggedIn",t),identify({passportId:t.profile.sub}),t},"storeTokens")}async getLogoutUrl(){return p(async()=>(await this.userManager.removeUser(),this.eventEmitter.emit("loggedOut"),await this.getLogoutUrlInternal()||void 0),"getLogoutUrl")}async logoutSilentCallback(e){return p(()=>this.userManager.signoutSilentCallback(e),"logoutSilentCallback")}getConfig(){return this.config}async getClientId(){return this.config.oidcConfiguration.clientId}buildExtraQueryParams(e,t){let r={...this.userManager.settings?.extraQueryParams??{},rid:getDetail(Detail.RUNTIME_ID)||"",third_party_a_id:""};if(e){if(e.directLoginMethod==="email"){let i=e.email;i&&(r.direct=e.directLoginMethod,r.email=i);}else r.direct=e.directLoginMethod;e.marketingConsentStatus&&(r.marketingConsent=e.marketingConsentStatus);}return t&&(r.im_passport_trace_id=t),r}async loginWithRedirectInternal(e){await this.userManager.clearStaleState(),await f(async()=>{let t=this.buildExtraQueryParams(e);await this.userManager.signinRedirect({extraQueryParams:t});},"AUTHENTICATION_ERROR");}async loginWithPopup(e){return f(async()=>{let t,r;if(e)t=e;else if(!this.config.popupOverlayOptions?.disableHeadlessLoginPromptOverlay){let{imPassportTraceId:d,...l}=await this.embeddedLoginPrompt.displayEmbeddedLoginPrompt();t=l,r=d;}let i=window.crypto.randomUUID(),o=async()=>{let d=this.buildExtraQueryParams(t,r),l=this.userManager.signinPopup({extraQueryParams:d,popupWindowFeatures:{width:410,height:450},popupWindowTarget:i}),c=window.open("",i);if(c){let E=new Promise((a,g)=>{let N=setInterval(()=>{c.closed&&(clearInterval(N),g(new Error("Popup closed by user")));},Ue);l.finally(()=>{clearInterval(N),c.close();});});return Promise.race([l,E])}return l};return new Promise((d,l)=>{o().then(c=>d(n.mapOidcUserToDomainModel(c))).catch(c=>{if(!(c instanceof Error)||c.message!=="Attempted to navigate on a disposed window"){l(c);return}let E=!1,a=new T(this.config.popupOverlayOptions||{},!0);a.append(async()=>{try{if(E)window.open("",i);else {E=!0;let g=await o();a.remove(),d(n.mapOidcUserToDomainModel(g));}}catch(g){a.remove(),l(g);}},()=>{a.remove(),l(new Error("Popup closed by user"));});});})},"AUTHENTICATION_ERROR")}static mapOidcUserToDomainModel=e=>{let t;e.id_token&&(t=J(e.id_token)?.passport);let r={expired:e.expired,idToken:e.id_token,accessToken:e.access_token,refreshToken:e.refresh_token,profile:{sub:e.profile.sub,email:e.profile.email,nickname:e.profile.nickname}};return t?.zkevm_eth_address&&t?.zkevm_user_admin_address&&(r.zkEvm={ethAddress:t.zkevm_eth_address,userAdminAddress:t.zkevm_user_admin_address}),r};static mapDeviceTokenResponseToOidcUser=e=>{let t=J(e.id_token);return new User({id_token:e.id_token,access_token:e.access_token,refresh_token:e.refresh_token,token_type:e.token_type,profile:{sub:t.sub,iss:t.iss,aud:t.aud,exp:t.exp,iat:t.iat,email:t.email,nickname:t.nickname,passport:t.passport}})};static shouldUseSigninPopupCallback(){try{let r=`oidc.${new URLSearchParams(window.location.search).get("state")}`,i=localStorage.getItem(r);return JSON.parse(i||"{}")?.request_type==="si:p"}catch{return !1}}async loginCallbackInternal(){return f(async()=>{if(n.shouldUseSigninPopupCallback()){await this.userManager.signinPopupCallback(void 0,!0);return}let e=await this.userManager.signinCallback();if(e)return n.mapOidcUserToDomainModel(e)},"AUTHENTICATION_ERROR")}async getPKCEAuthorizationUrl(e,t){let r=M(window.crypto.getRandomValues(new Uint8Array(32))),i=M(await Fe(r)),o=M(window.crypto.getRandomValues(new Uint8Array(32))),{redirectUri:d,scope:l,audience:c,clientId:E}=this.config.oidcConfiguration;this.deviceCredentialsManager.savePKCEData({state:o,verifier:r});let a=new URL(De,this.config.authenticationDomain);if(a.searchParams.set("response_type","code"),a.searchParams.set("code_challenge",i),a.searchParams.set("code_challenge_method","S256"),a.searchParams.set("client_id",E),a.searchParams.set("redirect_uri",d),a.searchParams.set("state",o),l&&a.searchParams.set("scope",l),c&&a.searchParams.set("audience",c),e){if(e.directLoginMethod==="email"){let g=e.email;g&&(a.searchParams.set("direct",e.directLoginMethod),a.searchParams.set("email",g));}else a.searchParams.set("direct",e.directLoginMethod);e.marketingConsentStatus&&a.searchParams.set("marketingConsent",e.marketingConsentStatus);}return t&&a.searchParams.set("im_passport_trace_id",t),a.toString()}async loginWithPKCEFlowCallbackInternal(e,t){return f(async()=>{let r=this.deviceCredentialsManager.getPKCEData();if(!r)throw new Error("No code verifier or state for PKCE");if(t!==r.state)throw new Error("Provided state does not match stored state");let i=await this.getPKCEToken(e,r.verifier),o=n.mapDeviceTokenResponseToOidcUser(i),d=n.mapOidcUserToDomainModel(o);return await this.userManager.storeUser(o),d},"AUTHENTICATION_ERROR")}async getPKCEToken(e,t){return (await Pe.post(`${this.config.authenticationDomain}/oauth/token`,{client_id:this.config.oidcConfiguration.clientId,grant_type:"authorization_code",code_verifier:t,code:e,redirect_uri:this.config.oidcConfiguration.redirectUri},be)).data}async storeTokensInternal(e){return f(async()=>{let t=n.mapDeviceTokenResponseToOidcUser(e),r=n.mapOidcUserToDomainModel(t);return await this.userManager.storeUser(t),r},"AUTHENTICATION_ERROR")}async logoutInternal(){await f(async()=>{await this.userManager.revokeTokens(["refresh_token"]),this.logoutMode==="silent"?await this.userManager.signoutSilent():await this.userManager.signoutRedirect();},"LOGOUT_ERROR");}async getLogoutUrlInternal(){let e=this.userManager.settings?.metadata?.end_session_endpoint;return e||(_.warn("Failed to get logout URL"),null)}forceUserRefreshInBackgroundInternal(){this.refreshTokenAndUpdatePromise().catch(e=>{_.warn("Failed to refresh user token",e);});}async forceUserRefreshInternal(){return this.refreshTokenAndUpdatePromise().catch(e=>(_.warn("Failed to refresh user token",e),null))}async refreshTokenAndUpdatePromise(){return this.refreshingPromise?this.refreshingPromise:(this.refreshingPromise=new Promise((e,t)=>{(async()=>{try{let r=await this.userManager.signinSilent();if(r){e(n.mapOidcUserToDomainModel(r));return}e(null);}catch(r){let i="AUTHENTICATION_ERROR",o="Failed to refresh token",d=!0;if(r instanceof ErrorTimeout?(i="SILENT_LOGIN_ERROR",o=`${o}: ${r.message}`,d=!1):r instanceof ErrorResponse?(i="NOT_LOGGED_IN_ERROR",o=`${o}: ${r.message||r.error_description}`):r instanceof Error?o=`${o}: ${r.message}`:typeof r=="string"&&(o=`${o}: ${r}`),d)try{await this.userManager.removeUser();}catch(l){l instanceof Error&&(o=`${o}: Failed to remove user: ${l.message}`);}t(new m(o,i));}finally{this.refreshingPromise=null;}})();}),this.refreshingPromise)}async getUserInternal(e=t=>!0){if(this.refreshingPromise){let r=await this.refreshingPromise;return r&&e(r)?r:null}let t=await this.userManager.getUser();if(!t)return null;if(!X(t)){let r=n.mapOidcUserToDomainModel(t);if(r&&e(r))return r}if(t.refresh_token){let r=await this.refreshTokenAndUpdatePromise();if(r&&e(r))return r}return null}async getUserZkEvmInternal(){let e=await this.getUserInternal(U);if(!e)throw new Error("Failed to obtain a User with the required ZkEvm attributes");return e}};
|
|
387
|
+
`,document.head.appendChild(e);};getEmbeddedLoginIFrame=()=>{let e=document.createElement("iframe");return e.id=z,e.src=this.getHref(),e.style.height="100vh",e.style.width="100vw",e.style.maxHeight=`${se}px`,e.style.maxWidth=`${ae}px`,e.style.borderRadius=le,e.style.opacity="0",e.style.transform="scale(0.6)",e.style.animation="passportEmbeddedLoginPromptPopBounceIn 1s ease forwards",n.appendIFrameStylesIfNeeded(),e};displayEmbeddedLoginPrompt(){return new Promise((e,t)=>{let r=this.getEmbeddedLoginIFrame(),i=({data:o,origin:l})=>{if(!(l!==this.config.authenticationDomain||o.eventType!==H))switch(o.messageType){case"login_method_selected":{let d=o.payload;window.removeEventListener("message",i),u.remove(),e(d);break}case"login_prompt_error":{window.removeEventListener("message",i),u.remove(),t(new Error("Error during embedded login prompt",{cause:o.payload}));break}case"login_prompt_closed":{window.removeEventListener("message",i),u.remove(),t(new Error("Popup closed by user"));break}default:window.removeEventListener("message",i),u.remove(),t(new Error(`Unsupported message type: ${o.messageType}`));break}};window.addEventListener("message",i),u.appendOverlay(r,()=>{window.removeEventListener("message",i),u.remove(),t(new Error("Popup closed by user"));});})}};var C=class{listeners=new Map;emit(e,...t){let r=this.listeners.get(e);!r||r.size===0||[...r].forEach(i=>{i(...t);});}on(e,t){let r=this.listeners.get(e)??new Set;r.add(t),this.listeners.set(e,r);}removeListener(e,t){let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e));}};var c=async(n,e,t=!0,r=!0)=>{let i=trackFlow("passport",e,t);try{return await n(i)}catch(o){throw o instanceof Error?trackError("passport",e,o,{flowId:i.details.flowId}):i.addEvent("errored"),o}finally{r&&i.addEvent("End");}};var pe=()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{},ue=n=>{let e=n.replace(/-/g,"+").replace(/_/g,"/"),t=e.length%4===0?"":"=".repeat(4-e.length%4);return e+t},me=n=>{let e=pe();if(typeof e.atob!="function")return null;let t=e.atob(n),r=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)r[o]=t.charCodeAt(o);if(typeof e.TextDecoder=="function")return new e.TextDecoder("utf-8").decode(r);let i="";for(let o=0;o<r.length;o+=1)i+=String.fromCharCode(r[o]);return i},ge=n=>{if(typeof Buffer<"u")return Buffer.from(n,"base64").toString("utf-8");let e=me(n);if(e===null)throw new Error("Base64 decoding is not supported in this environment");return e},f=n=>{if(typeof n!="string")throw new Error("JWT must be a string");let e=n.split(".");if(e.length<2)throw new Error("Invalid JWT: payload segment is missing");let t=e[1],r=ge(ue(t));try{return JSON.parse(r)}catch{throw new Error("Invalid JWT payload: unable to parse JSON")}};var J="pkce_state",j="pkce_verifier",fe=3600,v=class{isTokenValid(e){try{let r=f(e).exp??0,i=Date.now()/1e3+fe;return r>i}catch{return !1}}savePKCEData(e){localStorage.setItem(J,e.state),localStorage.setItem(j,e.verifier);}getPKCEData(){let e=localStorage.getItem(J),t=localStorage.getItem(j);return e&&t?{state:e,verifier:t}:null}};var Ee=(...n)=>{if(typeof process>"u")return;process?.env?.JEST_WORKER_ID===void 0&&console.warn(...n);},y={warn:Ee};function Q(n){try{let e=f(n),t=Math.floor(Date.now()/1e3);return e.exp?e.exp<=t+30:!0}catch{return !0}}function q(n){let{id_token:e,access_token:t}=n;return !t||!e?!0:Q(t)||Q(e)}var T=class{disableGenericPopupOverlay;disableBlockedPopupOverlay;overlay;isBlockedOverlay;tryAgainListener;onCloseListener;constructor(e,t=!1){this.disableBlockedPopupOverlay=e.disableBlockedPopupOverlay||!1,this.disableGenericPopupOverlay=e.disableGenericPopupOverlay||!1,this.isBlockedOverlay=t;}append(e,t){this.shouldAppendOverlay()&&(this.appendOverlay(),this.updateTryAgainButton(e),this.updateCloseButton(t));}update(e){this.updateTryAgainButton(e);}remove(){this.overlay&&this.overlay.remove();}shouldAppendOverlay(){return !(this.disableGenericPopupOverlay&&this.disableBlockedPopupOverlay||this.disableGenericPopupOverlay&&!this.isBlockedOverlay||this.disableBlockedPopupOverlay&&this.isBlockedOverlay)}appendOverlay(){if(!this.overlay){A({id:"link-googleapis",href:"https://fonts.googleapis.com"}),A({id:"link-gstatic",href:"https://fonts.gstatic.com",crossOrigin:"anonymous"}),A({id:"link-roboto",href:"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap",rel:"stylesheet"});let t=document.createElement("div");t.innerHTML=this.isBlockedOverlay?W():$(),document.body.insertAdjacentElement("beforeend",t),this.overlay=t;}}updateTryAgainButton(e){let t=document.getElementById(w);t&&(this.tryAgainListener&&t.removeEventListener("click",this.tryAgainListener),this.tryAgainListener=e,t.addEventListener("click",e));}updateCloseButton(e){let t=document.getElementById(I);t&&(this.onCloseListener&&t.removeEventListener("click",this.onCloseListener),this.onCloseListener=e,t.addEventListener("click",e));}};var k=class{storage;constructor(e,t){this.storage=he.createInstance({name:e,driver:t});}get length(){return this.storage.length()}clear(){return this.storage.clear()}getItem(e){return this.storage.getItem(e)}key(e){return this.storage.key(e)}async removeItem(e){await this.storage.removeItem(e);}async setItem(e,t){await this.storage.setItem(e,t);}};var ke={"Content-Type":"application/x-www-form-urlencoded"},be=n=>{if(n)try{return JSON.parse(n)}catch{return}},Ue=(n,e,t)=>{if(n&&typeof n=="object"){let r=n,i=r.error_description??r.message??r.error;if(typeof i=="string"&&i.trim().length>0)return i}return e.trim().length>0?e:`Token request failed with status ${t}`},xe="/v2/logout",Se="/im-logged-out",Me="/authorize",Ne=n=>n?Se:xe,De=n=>{let{authenticationDomain:e,oidcConfiguration:t}=n,r;n.crossSdkBridgeEnabled?r=new k("ImmutableSDKPassport",he.INDEXEDDB):typeof window<"u"?r=window.localStorage:r=new InMemoryWebStorage;let i=new WebStorageStateStore({store:r}),o=new URL(Ne(n.crossSdkBridgeEnabled),e.replace(/^(?:https?:\/\/)?(.*)/,"https://$1"));return o.searchParams.set("client_id",t.clientId),t.logoutRedirectUri&&o.searchParams.set("returnTo",t.logoutRedirectUri),{authority:e,redirect_uri:t.redirectUri,popup_redirect_uri:t.popupRedirectUri||t.redirectUri,client_id:t.clientId,metadata:{authorization_endpoint:`${e}/authorize`,token_endpoint:`${e}/oauth/token`,userinfo_endpoint:`${e}/userinfo`,end_session_endpoint:o.toString(),revocation_endpoint:`${e}/oauth/revoke`},automaticSilentRenew:!1,scope:t.scope,userStore:i,revokeTokenTypes:["refresh_token"],extraQueryParams:{...t.audience?{audience:t.audience}:{}}}};function M(n){return btoa(String.fromCharCode(...new Uint8Array(n))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function Fe(n){let t=new TextEncoder().encode(n);return window.crypto.subtle.digest("SHA-256",t)}var N=class n{config;userManager;deviceCredentialsManager;embeddedLoginPrompt;logoutMode;refreshingPromise=null;eventEmitter;constructor(e){this.config=new _(e),this.embeddedLoginPrompt=new O(this.config),this.userManager=new UserManager(De(this.config)),this.deviceCredentialsManager=new v,this.logoutMode=this.config.oidcConfiguration.logoutMode||"redirect",this.eventEmitter=new C,track("passport","initialise");}async login(e){return c(async()=>{let{useCachedSession:t=!1,useSilentLogin:r}=e||{},i=null;try{i=await this.getUserInternal();}catch(o){if(o instanceof Error&&!o.message.includes("Unknown or invalid refresh token")&&trackError("passport","login",o),t)throw o;y.warn("Failed to retrieve a cached user session",o);}if(!i&&r)i=await this.forceUserRefreshInternal();else if(!i&&!t){if(e?.useRedirectFlow)return await this.loginWithRedirectInternal(e?.directLoginOptions),null;i=await this.loginWithPopup(e?.directLoginOptions);}return i&&this.handleSuccessfulLogin(i),i},"login")}async loginWithRedirect(e){await this.loginWithRedirectInternal(e);}async loginCallback(){return c(async()=>{let e=await this.loginCallbackInternal();return e&&this.handleSuccessfulLogin(e),e},"loginCallback")}async logout(){await c(async()=>{await this.logoutInternal(),this.eventEmitter.emit("loggedOut");},"logout");}async getUser(){return this.getUserInternal()}async getUserOrLogin(){let e=null;try{e=await this.getUserInternal();}catch(r){y.warn("Failed to retrieve a cached user session",r);}if(e)return e;let t=await this.loginWithPopup();return this.handleSuccessfulLogin(t),t}async getUserZkEvm(){return this.getUserZkEvmInternal()}async getIdToken(){return c(async()=>(await this.getUserInternal())?.idToken,"getIdToken",!1)}async getAccessToken(){return c(async()=>(await this.getUserInternal())?.accessToken,"getAccessToken",!1,!1)}async isLoggedIn(){return await this.getUser()!==null}async forceUserRefresh(){return this.forceUserRefreshInternal()}forceUserRefreshInBackground(){this.forceUserRefreshInBackgroundInternal();}async loginWithPKCEFlow(e,t){return c(async()=>this.getPKCEAuthorizationUrl(e,t),"loginWithPKCEFlow")}async loginWithPKCEFlowCallback(e,t){return c(async()=>{let r=await this.loginWithPKCEFlowCallbackInternal(e,t);return this.handleSuccessfulLogin(r),r},"loginWithPKCEFlowCallback")}async storeTokens(e){return c(async()=>{let t=await this.storeTokensInternal(e);return this.handleSuccessfulLogin(t),t},"storeTokens")}async getLogoutUrl(){return c(async()=>(await this.userManager.removeUser(),this.eventEmitter.emit("loggedOut"),await this.getLogoutUrlInternal()||void 0),"getLogoutUrl")}async logoutSilentCallback(e){return c(()=>this.userManager.signoutSilentCallback(e),"logoutSilentCallback")}getConfig(){return this.config}async getClientId(){return this.config.oidcConfiguration.clientId}handleSuccessfulLogin(e){this.eventEmitter.emit("loggedIn",e),identify({passportId:e.profile.sub});}buildExtraQueryParams(e,t){let r={...this.userManager.settings?.extraQueryParams??{},rid:getDetail(Detail.RUNTIME_ID)||""};if(e){if(e.directLoginMethod==="email"){let i=e.email;i&&(r.direct=e.directLoginMethod,r.email=i);}else r.direct=e.directLoginMethod;e.marketingConsentStatus&&(r.marketingConsent=e.marketingConsentStatus);}return t&&(r.im_passport_trace_id=t),r}async loginWithRedirectInternal(e){await this.userManager.clearStaleState(),await g(async()=>{let t=this.buildExtraQueryParams(e);await this.userManager.signinRedirect({extraQueryParams:t});},"AUTHENTICATION_ERROR");}async loginWithPopup(e){return g(async()=>{let t,r;if(e)t=e;else if(!this.config.popupOverlayOptions?.disableHeadlessLoginPromptOverlay){let{imPassportTraceId:l,...d}=await this.embeddedLoginPrompt.displayEmbeddedLoginPrompt();t=d,r=l;}let i=window.crypto.randomUUID(),o=async()=>{let l=this.buildExtraQueryParams(t,r);return this.userManager.signinPopup({extraQueryParams:l,popupWindowFeatures:{width:410,height:450},popupWindowTarget:i})};return new Promise((l,d)=>{o().then(m=>l(n.mapOidcUserToDomainModel(m))).catch(m=>{if(!(m instanceof Error)||m.message!=="Attempted to navigate on a disposed window"){d(m);return}let L=!1,a=new T(this.config.popupOverlayOptions||{},!0);a.append(async()=>{try{if(L)window.open("",i);else {L=!0;let E=await o();a.remove(),l(n.mapOidcUserToDomainModel(E));}}catch(E){a.remove(),d(E);}},()=>{a.remove(),d(new Error("Popup closed by user"));});});})},"AUTHENTICATION_ERROR")}static mapOidcUserToDomainModel=e=>{let t,r;if(e.id_token){let o=f(e.id_token);t=o?.passport,o?.username&&(r=o?.username);}let i={expired:e.expired,idToken:e.id_token,accessToken:e.access_token,refreshToken:e.refresh_token,profile:{sub:e.profile.sub,email:e.profile.email,nickname:e.profile.nickname,username:r}};return t?.zkevm_eth_address&&t?.zkevm_user_admin_address&&(i.zkEvm={ethAddress:t.zkevm_eth_address,userAdminAddress:t.zkevm_user_admin_address}),i};static mapDeviceTokenResponseToOidcUser=e=>{let t=f(e.id_token);return new User({id_token:e.id_token,access_token:e.access_token,refresh_token:e.refresh_token,token_type:e.token_type,profile:{sub:t.sub,iss:t.iss,aud:t.aud,exp:t.exp,iat:t.iat,email:t.email,nickname:t.nickname,passport:t.passport,...t.username?{username:t.username}:{}}})};async loginCallbackInternal(){return g(async()=>{let e=await this.userManager.signinCallback();if(e)return n.mapOidcUserToDomainModel(e)},"AUTHENTICATION_ERROR")}async getPKCEAuthorizationUrl(e,t){let r=M(window.crypto.getRandomValues(new Uint8Array(32))),i=M(await Fe(r)),o=M(window.crypto.getRandomValues(new Uint8Array(32))),{redirectUri:l,scope:d,audience:m,clientId:L}=this.config.oidcConfiguration;this.deviceCredentialsManager.savePKCEData({state:o,verifier:r});let a=new URL(Me,this.config.authenticationDomain);if(a.searchParams.set("response_type","code"),a.searchParams.set("code_challenge",i),a.searchParams.set("code_challenge_method","S256"),a.searchParams.set("client_id",L),a.searchParams.set("redirect_uri",l),a.searchParams.set("state",o),d&&a.searchParams.set("scope",d),m&&a.searchParams.set("audience",m),e){if(e.directLoginMethod==="email"){let E=e.email;E&&(a.searchParams.set("direct",e.directLoginMethod),a.searchParams.set("email",E));}else a.searchParams.set("direct",e.directLoginMethod);e.marketingConsentStatus&&a.searchParams.set("marketingConsent",e.marketingConsentStatus);}return t&&a.searchParams.set("im_passport_trace_id",t),a.toString()}async loginWithPKCEFlowCallbackInternal(e,t){return g(async()=>{let r=this.deviceCredentialsManager.getPKCEData();if(!r)throw new Error("No code verifier or state for PKCE");if(t!==r.state)throw new Error("Provided state does not match stored state");let i=await this.getPKCEToken(e,r.verifier),o=n.mapDeviceTokenResponseToOidcUser(i),l=n.mapOidcUserToDomainModel(o);return await this.userManager.storeUser(o),l},"AUTHENTICATION_ERROR")}async getPKCEToken(e,t){let r=await fetch(`${this.config.authenticationDomain}/oauth/token`,{method:"POST",headers:ke,body:new URLSearchParams({client_id:this.config.oidcConfiguration.clientId,grant_type:"authorization_code",code_verifier:t,code:e,redirect_uri:this.config.oidcConfiguration.redirectUri})}),i=await r.text(),o=be(i);if(!r.ok)throw new Error(Ue(o,i,r.status));if(!o||typeof o!="object")throw new Error("Token endpoint returned an invalid response");return o}async storeTokensInternal(e){return g(async()=>{let t=n.mapDeviceTokenResponseToOidcUser(e),r=n.mapOidcUserToDomainModel(t);return await this.userManager.storeUser(t),r},"AUTHENTICATION_ERROR")}async logoutInternal(){await g(async()=>{await this.userManager.revokeTokens(["refresh_token"]),this.logoutMode==="silent"?await this.userManager.signoutSilent():await this.userManager.signoutRedirect();},"LOGOUT_ERROR");}async getLogoutUrlInternal(){let e=this.userManager.settings?.metadata?.end_session_endpoint;return e||(y.warn("Failed to get logout URL"),null)}forceUserRefreshInBackgroundInternal(){this.refreshTokenAndUpdatePromise().catch(e=>{y.warn("Failed to refresh user token",e);});}async forceUserRefreshInternal(){return this.refreshTokenAndUpdatePromise().catch(e=>(y.warn("Failed to refresh user token",e),null))}async refreshTokenAndUpdatePromise(){return this.refreshingPromise?this.refreshingPromise:(this.refreshingPromise=new Promise((e,t)=>{(async()=>{try{let r=await this.userManager.signinSilent();if(r){e(n.mapOidcUserToDomainModel(r));return}e(null);}catch(r){let i="AUTHENTICATION_ERROR",o="Failed to refresh token",l=!0;if(r instanceof ErrorTimeout?(i="SILENT_LOGIN_ERROR",o=`${o}: ${r.message}`,l=!1):r instanceof ErrorResponse?(i="NOT_LOGGED_IN_ERROR",o=`${o}: ${r.message||r.error_description}`):r instanceof Error?o=`${o}: ${r.message}`:typeof r=="string"&&(o=`${o}: ${r}`),l)try{await this.userManager.removeUser();}catch(d){d instanceof Error&&(o=`${o}: Failed to remove user: ${d.message}`);}t(new p(o,i));}finally{this.refreshingPromise=null;}})();}),this.refreshingPromise)}async getUserInternal(e=t=>!0){if(this.refreshingPromise){let r=await this.refreshingPromise;return r&&e(r)?r:null}let t=await this.userManager.getUser();if(!t)return null;if(!q(t)){let r=n.mapOidcUserToDomainModel(t);if(r&&e(r))return r}if(t.refresh_token){let r=await this.refreshTokenAndUpdatePromise();if(r&&e(r))return r}return null}async getUserZkEvmInternal(){let e=await this.getUserInternal(U);if(!e)throw new Error("Failed to obtain a User with the required ZkEvm attributes");return e}};
|
|
391
388
|
|
|
392
|
-
export {
|
|
389
|
+
export { N as Auth, _ as AuthConfiguration, x as AuthEvents, F as MarketingConsentStatus, p as PassportError, P as PassportErrorType, D as RollupType, C as TypedEventEmitter, f as decodeJwtPayload, b as isAPIError, U as isUserZkEvm, g as withPassportError };
|
package/dist/types/Auth.d.ts
CHANGED
|
@@ -145,12 +145,12 @@ export declare class Auth {
|
|
|
145
145
|
* @returns Promise that resolves with the client ID string
|
|
146
146
|
*/
|
|
147
147
|
getClientId(): Promise<string>;
|
|
148
|
+
private handleSuccessfulLogin;
|
|
148
149
|
private buildExtraQueryParams;
|
|
149
150
|
private loginWithRedirectInternal;
|
|
150
151
|
private loginWithPopup;
|
|
151
152
|
private static mapOidcUserToDomainModel;
|
|
152
153
|
private static mapDeviceTokenResponseToOidcUser;
|
|
153
|
-
private static shouldUseSigninPopupCallback;
|
|
154
154
|
private loginCallbackInternal;
|
|
155
155
|
private getPKCEAuthorizationUrl;
|
|
156
156
|
private loginWithPKCEFlowCallbackInternal;
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ export declare enum PassportErrorType {
|
|
|
20
20
|
LINK_WALLET_VALIDATION_ERROR = "LINK_WALLET_VALIDATION_ERROR",
|
|
21
21
|
LINK_WALLET_DUPLICATE_NONCE_ERROR = "LINK_WALLET_DUPLICATE_NONCE_ERROR",
|
|
22
22
|
LINK_WALLET_GENERIC_ERROR = "LINK_WALLET_GENERIC_ERROR",
|
|
23
|
-
SERVICE_UNAVAILABLE_ERROR = "SERVICE_UNAVAILABLE_ERROR"
|
|
23
|
+
SERVICE_UNAVAILABLE_ERROR = "SERVICE_UNAVAILABLE_ERROR",
|
|
24
|
+
TRANSACTION_REJECTED = "TRANSACTION_REJECTED"
|
|
24
25
|
}
|
|
25
26
|
export declare function isAPIError(error: any): error is imx.APIError;
|
|
26
27
|
export declare class PassportError extends Error {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { AuthConfiguration, type IAuthConfiguration } from './config';
|
|
|
3
3
|
export type { User, UserProfile, UserZkEvm, DirectLoginMethod, DirectLoginOptions, LoginOptions, DeviceTokenResponse, OidcConfiguration, AuthModuleConfiguration, PopupOverlayOptions, PassportMetadata, IdTokenPayload, PKCEData, AuthEventMap, } from './types';
|
|
4
4
|
export { isUserZkEvm, RollupType, MarketingConsentStatus, AuthEvents, } from './types';
|
|
5
5
|
export { default as TypedEventEmitter } from './utils/typedEventEmitter';
|
|
6
|
-
export { PassportError, PassportErrorType, withPassportError } from './errors';
|
|
6
|
+
export { PassportError, PassportErrorType, withPassportError, isAPIError, } from './errors';
|
|
7
|
+
export { decodeJwtPayload } from './utils/jwt';
|
package/dist/types/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type UserProfile = {
|
|
|
8
8
|
email?: string;
|
|
9
9
|
nickname?: string;
|
|
10
10
|
sub: string;
|
|
11
|
+
username?: string;
|
|
11
12
|
};
|
|
12
13
|
export declare enum RollupType {
|
|
13
14
|
ZKEVM = "zkEvm"
|
|
@@ -77,6 +78,7 @@ export type TokenPayload = {
|
|
|
77
78
|
};
|
|
78
79
|
export type IdTokenPayload = {
|
|
79
80
|
passport?: PassportMetadata;
|
|
81
|
+
username?: string;
|
|
80
82
|
email: string;
|
|
81
83
|
nickname: string;
|
|
82
84
|
aud: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const decodeJwtPayload: <T>(token: string) => T;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
type StringEventKey<T> = Extract<keyof T, string>;
|
|
2
|
+
type EventArgs<TEvents, TEventName extends keyof TEvents> = TEvents[TEventName] extends readonly [...infer A] ? [...A] : TEvents[TEventName] extends readonly any[] ? [...TEvents[TEventName]] : [TEvents[TEventName]];
|
|
1
3
|
export default class TypedEventEmitter<TEvents extends Record<string, any>> {
|
|
2
|
-
private
|
|
3
|
-
emit<TEventName extends
|
|
4
|
-
on<TEventName extends
|
|
5
|
-
removeListener<TEventName extends
|
|
4
|
+
private listeners;
|
|
5
|
+
emit<TEventName extends StringEventKey<TEvents>>(eventName: TEventName, ...eventArg: EventArgs<TEvents, TEventName>): void;
|
|
6
|
+
on<TEventName extends StringEventKey<TEvents>>(eventName: TEventName, handler: (...eventArg: EventArgs<TEvents, TEventName>) => void): void;
|
|
7
|
+
removeListener<TEventName extends StringEventKey<TEvents>>(eventName: TEventName, handler: (...eventArg: EventArgs<TEvents, TEventName>) => void): void;
|
|
6
8
|
}
|
|
9
|
+
export {};
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Config } from 'jest';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { name } from './package.json';
|
|
4
|
+
|
|
5
|
+
const rootDirs = execSync(`pnpm --filter ${name}... exec pwd`)
|
|
6
|
+
.toString()
|
|
7
|
+
.split('\n')
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
.map((dir) => `${dir}/dist`);
|
|
10
|
+
|
|
11
|
+
const config: Config = {
|
|
12
|
+
clearMocks: true,
|
|
13
|
+
roots: ['<rootDir>/src', ...rootDirs],
|
|
14
|
+
coverageProvider: 'v8',
|
|
15
|
+
moduleDirectories: ['node_modules', 'src'],
|
|
16
|
+
moduleNameMapper: { '^@imtbl/(.*)$': '<rootDir>/../../node_modules/@imtbl/$1/src' },
|
|
17
|
+
testEnvironment: 'jsdom',
|
|
18
|
+
transform: {
|
|
19
|
+
'^.+\\.(t|j)sx?$': '@swc/jest',
|
|
20
|
+
},
|
|
21
|
+
transformIgnorePatterns: [],
|
|
22
|
+
restoreMocks: true,
|
|
23
|
+
setupFiles: ['<rootDir>/jest.setup.js'],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default config;
|
|
27
|
+
|
package/jest.setup.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtbl/auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.1-alpha.1",
|
|
4
4
|
"description": "Authentication SDK for Immutable",
|
|
5
5
|
"author": "Immutable",
|
|
6
6
|
"bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
|
|
@@ -25,17 +25,19 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@imtbl/
|
|
29
|
-
"@imtbl/metrics": "2.10.7-alpha.6",
|
|
30
|
-
"axios": "^1.6.5",
|
|
31
|
-
"jwt-decode": "^3.1.2",
|
|
28
|
+
"@imtbl/metrics": "2.11.1-alpha.1",
|
|
32
29
|
"localforage": "^1.10.0",
|
|
33
|
-
"oidc-client-ts": "3.
|
|
34
|
-
"uuid": "^9.0.1"
|
|
30
|
+
"oidc-client-ts": "3.4.1"
|
|
35
31
|
},
|
|
36
32
|
"devDependencies": {
|
|
37
|
-
"@
|
|
33
|
+
"@swc/core": "^1.3.36",
|
|
34
|
+
"@swc/jest": "^0.2.37",
|
|
35
|
+
"@types/jest": "^29.5.12",
|
|
38
36
|
"@types/node": "^18.14.2",
|
|
37
|
+
"@jest/test-sequencer": "^29.7.0",
|
|
38
|
+
"jest": "^29.4.3",
|
|
39
|
+
"jest-environment-jsdom": "^29.4.3",
|
|
40
|
+
"ts-node": "^10.9.1",
|
|
39
41
|
"tsup": "^8.3.0",
|
|
40
42
|
"typescript": "^5.6.2"
|
|
41
43
|
},
|
|
@@ -50,6 +52,7 @@
|
|
|
50
52
|
"typegen": "tsc --customConditions default --emitDeclarationOnly --outDir dist/types",
|
|
51
53
|
"pack:root": "pnpm pack --pack-destination $(dirname $(pnpm root -w))",
|
|
52
54
|
"lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0",
|
|
53
|
-
"typecheck": "tsc --customConditions default --noEmit --jsx preserve"
|
|
55
|
+
"typecheck": "tsc --customConditions default --noEmit --jsx preserve",
|
|
56
|
+
"test": "jest"
|
|
54
57
|
}
|
|
55
58
|
}
|
package/src/Auth.test.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Auth } from './Auth';
|
|
2
|
+
import { AuthEvents, User } from './types';
|
|
3
|
+
import { withMetricsAsync } from './utils/metrics';
|
|
4
|
+
import { decodeJwtPayload } from './utils/jwt';
|
|
5
|
+
|
|
6
|
+
const trackFlowMock = jest.fn();
|
|
7
|
+
const trackErrorMock = jest.fn();
|
|
8
|
+
const identifyMock = jest.fn();
|
|
9
|
+
const trackMock = jest.fn();
|
|
10
|
+
const getDetailMock = jest.fn();
|
|
11
|
+
|
|
12
|
+
jest.mock('@imtbl/metrics', () => ({
|
|
13
|
+
Detail: { RUNTIME_ID: 'runtime-id' },
|
|
14
|
+
trackFlow: (...args: any[]) => trackFlowMock(...args),
|
|
15
|
+
trackError: (...args: any[]) => trackErrorMock(...args),
|
|
16
|
+
identify: (...args: any[]) => identifyMock(...args),
|
|
17
|
+
track: (...args: any[]) => trackMock(...args),
|
|
18
|
+
getDetail: (...args: any[]) => getDetailMock(...args),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
jest.mock('./utils/jwt', () => ({
|
|
22
|
+
decodeJwtPayload: jest.fn(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
trackFlowMock.mockReset();
|
|
27
|
+
trackErrorMock.mockReset();
|
|
28
|
+
identifyMock.mockReset();
|
|
29
|
+
trackMock.mockReset();
|
|
30
|
+
getDetailMock.mockReset();
|
|
31
|
+
(decodeJwtPayload as jest.Mock).mockReset();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('withMetricsAsync', () => {
|
|
35
|
+
it('resolves with function result and tracks flow', async () => {
|
|
36
|
+
const flow = {
|
|
37
|
+
addEvent: jest.fn(),
|
|
38
|
+
details: { flowId: 'flow-id' },
|
|
39
|
+
};
|
|
40
|
+
trackFlowMock.mockReturnValue(flow);
|
|
41
|
+
|
|
42
|
+
const result = await withMetricsAsync(async () => 'done', 'login');
|
|
43
|
+
|
|
44
|
+
expect(result).toEqual('done');
|
|
45
|
+
expect(trackFlowMock).toHaveBeenCalledWith('passport', 'login', true);
|
|
46
|
+
expect(flow.addEvent).toHaveBeenCalledWith('End');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('tracks error when function throws', async () => {
|
|
50
|
+
const flow = {
|
|
51
|
+
addEvent: jest.fn(),
|
|
52
|
+
details: { flowId: 'flow-id' },
|
|
53
|
+
};
|
|
54
|
+
trackFlowMock.mockReturnValue(flow);
|
|
55
|
+
const error = new Error('boom');
|
|
56
|
+
|
|
57
|
+
await expect(withMetricsAsync(async () => {
|
|
58
|
+
throw error;
|
|
59
|
+
}, 'login')).rejects.toThrow(error);
|
|
60
|
+
|
|
61
|
+
expect(trackErrorMock).toHaveBeenCalledWith('passport', 'login', error, { flowId: 'flow-id' });
|
|
62
|
+
expect(flow.addEvent).toHaveBeenCalledWith('End');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('does not fail when non-error is thrown', async () => {
|
|
66
|
+
const flow = {
|
|
67
|
+
addEvent: jest.fn(),
|
|
68
|
+
details: { flowId: 'flow-id' },
|
|
69
|
+
};
|
|
70
|
+
trackFlowMock.mockReturnValue(flow);
|
|
71
|
+
|
|
72
|
+
const nonError = { message: 'failure' };
|
|
73
|
+
await expect(withMetricsAsync(async () => {
|
|
74
|
+
throw nonError as unknown as Error;
|
|
75
|
+
}, 'login')).rejects.toBe(nonError);
|
|
76
|
+
|
|
77
|
+
expect(flow.addEvent).toHaveBeenCalledWith('errored');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('Auth', () => {
|
|
82
|
+
describe('getUserOrLogin', () => {
|
|
83
|
+
const createMockUser = (): User => ({
|
|
84
|
+
accessToken: 'access',
|
|
85
|
+
idToken: 'id',
|
|
86
|
+
refreshToken: 'refresh',
|
|
87
|
+
expired: false,
|
|
88
|
+
profile: {
|
|
89
|
+
sub: 'user-123',
|
|
90
|
+
email: 'test@example.com',
|
|
91
|
+
nickname: 'tester',
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('emits LOGGED_IN event and identifies user when login is required', async () => {
|
|
96
|
+
const auth = Object.create(Auth.prototype) as Auth;
|
|
97
|
+
const loginWithPopup = jest.fn().mockResolvedValue(createMockUser());
|
|
98
|
+
|
|
99
|
+
(auth as any).eventEmitter = { emit: jest.fn() };
|
|
100
|
+
(auth as any).getUserInternal = jest.fn().mockResolvedValue(null);
|
|
101
|
+
(auth as any).loginWithPopup = loginWithPopup;
|
|
102
|
+
|
|
103
|
+
const user = await auth.getUserOrLogin();
|
|
104
|
+
|
|
105
|
+
expect(loginWithPopup).toHaveBeenCalledTimes(1);
|
|
106
|
+
expect((auth as any).eventEmitter.emit).toHaveBeenCalledWith(AuthEvents.LOGGED_IN, user);
|
|
107
|
+
expect(identifyMock).toHaveBeenCalledWith({ passportId: user.profile.sub });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('returns cached user without triggering login', async () => {
|
|
111
|
+
const auth = Object.create(Auth.prototype) as Auth;
|
|
112
|
+
const cachedUser = createMockUser();
|
|
113
|
+
|
|
114
|
+
(auth as any).eventEmitter = { emit: jest.fn() };
|
|
115
|
+
(auth as any).getUserInternal = jest.fn().mockResolvedValue(cachedUser);
|
|
116
|
+
(auth as any).loginWithPopup = jest.fn();
|
|
117
|
+
|
|
118
|
+
const user = await auth.getUserOrLogin();
|
|
119
|
+
|
|
120
|
+
expect(user).toBe(cachedUser);
|
|
121
|
+
expect((auth as any).loginWithPopup).not.toHaveBeenCalled();
|
|
122
|
+
expect((auth as any).eventEmitter.emit).not.toHaveBeenCalled();
|
|
123
|
+
expect(identifyMock).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('buildExtraQueryParams', () => {
|
|
128
|
+
it('omits third_party_a_id when no anonymous id is provided', () => {
|
|
129
|
+
const auth = Object.create(Auth.prototype) as Auth;
|
|
130
|
+
(auth as any).userManager = { settings: { extraQueryParams: {} } };
|
|
131
|
+
getDetailMock.mockReturnValue('runtime-id-value');
|
|
132
|
+
|
|
133
|
+
const params = (auth as any).buildExtraQueryParams();
|
|
134
|
+
|
|
135
|
+
expect(params.third_party_a_id).toBeUndefined();
|
|
136
|
+
expect(params.rid).toEqual('runtime-id-value');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('username extraction', () => {
|
|
141
|
+
it('extracts username from id token when present', () => {
|
|
142
|
+
const mockOidcUser = {
|
|
143
|
+
id_token: 'token',
|
|
144
|
+
access_token: 'access',
|
|
145
|
+
refresh_token: 'refresh',
|
|
146
|
+
expired: false,
|
|
147
|
+
profile: { sub: 'user-123', email: 'test@example.com', nickname: 'tester' },
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
151
|
+
username: 'username123',
|
|
152
|
+
passport: undefined,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const result = (Auth as any).mapOidcUserToDomainModel(mockOidcUser);
|
|
156
|
+
|
|
157
|
+
expect(decodeJwtPayload).toHaveBeenCalledWith('token');
|
|
158
|
+
expect(result.profile.username).toEqual('username123');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('maps username when creating OIDC user from device tokens', () => {
|
|
162
|
+
const tokenResponse = {
|
|
163
|
+
id_token: 'token',
|
|
164
|
+
access_token: 'access',
|
|
165
|
+
refresh_token: 'refresh',
|
|
166
|
+
token_type: 'Bearer',
|
|
167
|
+
expires_in: 3600,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
171
|
+
sub: 'user-123',
|
|
172
|
+
iss: 'issuer',
|
|
173
|
+
aud: 'audience',
|
|
174
|
+
exp: 1,
|
|
175
|
+
iat: 0,
|
|
176
|
+
email: 'test@example.com',
|
|
177
|
+
nickname: 'tester',
|
|
178
|
+
username: 'username123',
|
|
179
|
+
passport: undefined,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const oidcUser = (Auth as any).mapDeviceTokenResponseToOidcUser(tokenResponse);
|
|
183
|
+
|
|
184
|
+
expect(decodeJwtPayload).toHaveBeenCalledWith('token');
|
|
185
|
+
expect(oidcUser.profile.username).toEqual('username123');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|