@imtbl/auth 2.11.1-alpha.0 → 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/dist/browser/index.js +28 -30
- package/dist/node/index.cjs +30 -34
- package/dist/node/index.js +21 -24
- package/dist/types/index.d.ts +1 -0
- package/dist/types/utils/jwt.d.ts +1 -0
- package/dist/types/utils/typedEventEmitter.d.ts +7 -4
- package/package.json +3 -8
- package/src/Auth.test.ts +9 -7
- package/src/Auth.ts +63 -16
- package/src/errors.ts +28 -3
- package/src/index.ts +2 -0
- package/src/storage/device_credentials_manager.ts +2 -2
- package/src/utils/jwt.ts +78 -0
- package/src/utils/token.ts +2 -2
- package/src/utils/typedEventEmitter.ts +36 -11
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 X from 'jwt-decode';
|
|
4
|
-
import Ee from 'localforage';
|
|
2
|
+
import he from 'localforage';
|
|
5
3
|
import { track, identify, getDetail, Detail, trackFlow, trackError } from '@imtbl/metrics';
|
|
6
|
-
import { EventEmitter } from 'events';
|
|
7
4
|
|
|
8
|
-
var
|
|
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"
|
|
@@ -17,7 +14,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
17
14
|
fill="#F3F3F3"
|
|
18
15
|
/>
|
|
19
16
|
</svg>
|
|
20
|
-
`,
|
|
17
|
+
`,G=`
|
|
21
18
|
<svg
|
|
22
19
|
viewBox="0 0 17 16"
|
|
23
20
|
fill="none"
|
|
@@ -31,7 +28,7 @@ var L=(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;
|
|
@@ -216,7 +213,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
216
213
|
</svg>
|
|
217
214
|
`;var te=()=>`
|
|
218
215
|
<button
|
|
219
|
-
id="${
|
|
216
|
+
id="${I}"
|
|
220
217
|
style="
|
|
221
218
|
background: #f3f3f326 !important;
|
|
222
219
|
border: none !important;
|
|
@@ -232,11 +229,11 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
232
229
|
justify-content: center !important;
|
|
233
230
|
"
|
|
234
231
|
>
|
|
235
|
-
${
|
|
232
|
+
${V}
|
|
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;
|
|
@@ -252,7 +249,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
252
249
|
Try again
|
|
253
250
|
</button>
|
|
254
251
|
`,re=()=>`
|
|
255
|
-
${
|
|
252
|
+
${S}
|
|
256
253
|
<div
|
|
257
254
|
style="
|
|
258
255
|
color: #e01a3d !important;
|
|
@@ -262,7 +259,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
262
259
|
margin-bottom: 10px !important;
|
|
263
260
|
"
|
|
264
261
|
>
|
|
265
|
-
${
|
|
262
|
+
${G}
|
|
266
263
|
Pop-up blocked
|
|
267
264
|
</div>
|
|
268
265
|
<p style="
|
|
@@ -275,9 +272,9 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
275
272
|
If the problem continues, adjust your<br />
|
|
276
273
|
browser settings.
|
|
277
274
|
</p>
|
|
278
|
-
${
|
|
275
|
+
${B()}
|
|
279
276
|
`,ne=()=>`
|
|
280
|
-
${
|
|
277
|
+
${S}
|
|
281
278
|
<p style="
|
|
282
279
|
color: #b6b6b6 !important;
|
|
283
280
|
text-align: center !important;
|
|
@@ -286,10 +283,10 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
286
283
|
>
|
|
287
284
|
Secure pop-up not showing?<br />We'll help you re-launch
|
|
288
285
|
</p>
|
|
289
|
-
${
|
|
290
|
-
`,
|
|
286
|
+
${B()}
|
|
287
|
+
`,K=n=>`
|
|
291
288
|
<div
|
|
292
|
-
id="${
|
|
289
|
+
id="${h}"
|
|
293
290
|
style="
|
|
294
291
|
position: fixed !important;
|
|
295
292
|
top: 0 !important;
|
|
@@ -322,12 +319,12 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
322
319
|
max-width: 400px !important;
|
|
323
320
|
"
|
|
324
321
|
>
|
|
325
|
-
${
|
|
322
|
+
${n??""}
|
|
326
323
|
</div>
|
|
327
324
|
</div>
|
|
328
325
|
`,Z=()=>`
|
|
329
326
|
<div
|
|
330
|
-
id="${
|
|
327
|
+
id="${h}"
|
|
331
328
|
style="
|
|
332
329
|
position: fixed;
|
|
333
330
|
top: 0;
|
|
@@ -354,7 +351,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
354
351
|
"
|
|
355
352
|
/>
|
|
356
353
|
</div>
|
|
357
|
-
`;function A({id:
|
|
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;
|
|
@@ -373,7 +370,7 @@ var L=(s=>(s.AUTHENTICATION_ERROR="AUTHENTICATION_ERROR",s.INVALID_CONFIGURATION
|
|
|
373
370
|
}
|
|
374
371
|
|
|
375
372
|
@media (max-height: 400px) {
|
|
376
|
-
#${
|
|
373
|
+
#${z} {
|
|
377
374
|
width: 100% !important;
|
|
378
375
|
max-width: none !important;
|
|
379
376
|
}
|
|
@@ -387,6 +384,6 @@ var L=(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=Y,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",o.appendIFrameStylesIfNeeded(),e};displayEmbeddedLoginPrompt(){return new Promise((e,t)=>{let r=this.getEmbeddedLoginIFrame(),n=({data:i,origin:l})=>{if(!(l!==this.config.authenticationDomain||i.eventType!==F))switch(i.messageType){case"login_method_selected":{let d=i.payload;window.removeEventListener("message",n),m.remove(),e(d);break}case"login_prompt_error":{window.removeEventListener("message",n),m.remove(),t(new Error("Error during embedded login prompt",{cause:i.payload}));break}case"login_prompt_closed":{window.removeEventListener("message",n),m.remove(),t(new Error("Popup closed by user"));break}default:window.removeEventListener("message",n),m.remove(),t(new Error(`Unsupported message type: ${i.messageType}`));break}};window.addEventListener("message",n),m.appendOverlay(r,()=>{window.removeEventListener("message",n),m.remove(),t(new Error("Popup closed by user"));});})}};var h=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 c=async(o,e,t=!0,r=!0)=>{let n=trackFlow("passport",e,t);try{return await o(n)}catch(i){throw i instanceof Error?trackError("passport",e,i,{flowId:n.details.flowId}):n.addEvent("errored"),i}finally{r&&n.addEvent("End");}};var z="pkce_state",j="pkce_verifier",ue=3600,y=class{isTokenValid(e){try{let r=X(e).exp??0,n=Date.now()/1e3+ue;return r>n}catch{return !1}}savePKCEData(e){localStorage.setItem(z,e.state),localStorage.setItem(j,e.verifier);}getPKCEData(){let e=localStorage.getItem(z),t=localStorage.getItem(j);return e&&t?{state:e,verifier:t}:null}};var ge=(...o)=>{if(typeof process>"u")return;process?.env?.JEST_WORKER_ID===void 0&&console.warn(...o);},C={warn:ge};function Q(o){try{let e=X(o),t=Math.floor(Date.now()/1e3);return e.exp?e.exp<=t+30:!0}catch{return !0}}function J(o){let{id_token:e,access_token:t}=o;return !t||!e?!0:Q(t)||Q(e)}var v=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?K():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(P);t&&(this.onCloseListener&&t.removeEventListener("click",this.onCloseListener),this.onCloseListener=e,t.addEventListener("click",e));}};var k=class{storage;constructor(e,t){this.storage=Ee.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 we={headers:{"Content-Type":"application/x-www-form-urlencoded"}},Ue="/v2/logout",be="/im-logged-out",xe="/authorize",Me=o=>o?be:Ue,De=o=>{let{authenticationDomain:e,oidcConfiguration:t}=o,r;o.crossSdkBridgeEnabled?r=new k("ImmutableSDKPassport",Ee.INDEXEDDB):typeof window<"u"?r=window.localStorage:r=new InMemoryWebStorage;let n=new WebStorageStateStore({store:r}),i=new URL(Me(o.crossSdkBridgeEnabled),e.replace(/^(?:https?:\/\/)?(.*)/,"https://$1"));return i.searchParams.set("client_id",t.clientId),t.logoutRedirectUri&&i.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:i.toString(),revocation_endpoint:`${e}/oauth/revoke`},automaticSilentRenew:!1,scope:t.scope,userStore:n,revokeTokenTypes:["refresh_token"],extraQueryParams:{...t.audience?{audience:t.audience}:{}}}};function x(o){return btoa(String.fromCharCode(...new Uint8Array(o))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function Ne(o){let t=new TextEncoder().encode(o);return window.crypto.subtle.digest("SHA-256",t)}var M=class o{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 y,this.logoutMode=this.config.oidcConfiguration.logoutMode||"redirect",this.eventEmitter=new h,track("passport","initialise");}async login(e){return c(async()=>{let{useCachedSession:t=!1,useSilentLogin:r}=e||{},n=null;try{n=await this.getUserInternal();}catch(i){if(i instanceof Error&&!i.message.includes("Unknown or invalid refresh token")&&trackError("passport","login",i),t)throw i;C.warn("Failed to retrieve a cached user session",i);}if(!n&&r)n=await this.forceUserRefreshInternal();else if(!n&&!t){if(e?.useRedirectFlow)return await this.loginWithRedirectInternal(e?.directLoginOptions),null;n=await this.loginWithPopup(e?.directLoginOptions);}return n&&this.handleSuccessfulLogin(n),n},"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){C.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 n=e.email;n&&(r.direct=e.directLoginMethod,r.email=n);}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 n=window.crypto.randomUUID(),i=async()=>{let l=this.buildExtraQueryParams(t,r);return this.userManager.signinPopup({extraQueryParams:l,popupWindowFeatures:{width:410,height:450},popupWindowTarget:n})};return new Promise((l,d)=>{i().then(u=>l(o.mapOidcUserToDomainModel(u))).catch(u=>{if(!(u instanceof Error)||u.message!=="Attempted to navigate on a disposed window"){d(u);return}let T=!1,a=new v(this.config.popupOverlayOptions||{},!0);a.append(async()=>{try{if(T)window.open("",n);else {T=!0;let f=await i();a.remove(),l(o.mapOidcUserToDomainModel(f));}}catch(f){a.remove(),d(f);}},()=>{a.remove(),d(new Error("Popup closed by user"));});});})},"AUTHENTICATION_ERROR")}static mapOidcUserToDomainModel=e=>{let t,r;if(e.id_token){let i=X(e.id_token);t=i?.passport,i?.username&&(r=i?.username);}let n={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&&(n.zkEvm={ethAddress:t.zkevm_eth_address,userAdminAddress:t.zkevm_user_admin_address}),n};static mapDeviceTokenResponseToOidcUser=e=>{let t=X(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 o.mapOidcUserToDomainModel(e)},"AUTHENTICATION_ERROR")}async getPKCEAuthorizationUrl(e,t){let r=x(window.crypto.getRandomValues(new Uint8Array(32))),n=x(await Ne(r)),i=x(window.crypto.getRandomValues(new Uint8Array(32))),{redirectUri:l,scope:d,audience:u,clientId:T}=this.config.oidcConfiguration;this.deviceCredentialsManager.savePKCEData({state:i,verifier:r});let a=new URL(xe,this.config.authenticationDomain);if(a.searchParams.set("response_type","code"),a.searchParams.set("code_challenge",n),a.searchParams.set("code_challenge_method","S256"),a.searchParams.set("client_id",T),a.searchParams.set("redirect_uri",l),a.searchParams.set("state",i),d&&a.searchParams.set("scope",d),u&&a.searchParams.set("audience",u),e){if(e.directLoginMethod==="email"){let f=e.email;f&&(a.searchParams.set("direct",e.directLoginMethod),a.searchParams.set("email",f));}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 n=await this.getPKCEToken(e,r.verifier),i=o.mapDeviceTokenResponseToOidcUser(n),l=o.mapOidcUserToDomainModel(i);return await this.userManager.storeUser(i),l},"AUTHENTICATION_ERROR")}async getPKCEToken(e,t){return (await ve.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},we)).data}async storeTokensInternal(e){return g(async()=>{let t=o.mapDeviceTokenResponseToOidcUser(e),r=o.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||(C.warn("Failed to get logout URL"),null)}forceUserRefreshInBackgroundInternal(){this.refreshTokenAndUpdatePromise().catch(e=>{C.warn("Failed to refresh user token",e);});}async forceUserRefreshInternal(){return this.refreshTokenAndUpdatePromise().catch(e=>(C.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(o.mapOidcUserToDomainModel(r));return}e(null);}catch(r){let n="AUTHENTICATION_ERROR",i="Failed to refresh token",l=!0;if(r instanceof ErrorTimeout?(n="SILENT_LOGIN_ERROR",i=`${i}: ${r.message}`,l=!1):r instanceof ErrorResponse?(n="NOT_LOGGED_IN_ERROR",i=`${i}: ${r.message||r.error_description}`):r instanceof Error?i=`${i}: ${r.message}`:typeof r=="string"&&(i=`${i}: ${r}`),l)try{await this.userManager.removeUser();}catch(d){d instanceof Error&&(i=`${i}: Failed to remove user: ${d.message}`);}t(new p(i,n));}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(!J(t)){let r=o.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(w);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/index.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export type { User, UserProfile, UserZkEvm, DirectLoginMethod, DirectLoginOption
|
|
|
4
4
|
export { isUserZkEvm, RollupType, MarketingConsentStatus, AuthEvents, } from './types';
|
|
5
5
|
export { default as TypedEventEmitter } from './utils/typedEventEmitter';
|
|
6
6
|
export { PassportError, PassportErrorType, withPassportError, isAPIError, } from './errors';
|
|
7
|
+
export { decodeJwtPayload } from './utils/jwt';
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtbl/auth",
|
|
3
|
-
"version": "2.11.1-alpha.
|
|
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,16 +25,11 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@imtbl/
|
|
29
|
-
"@imtbl/metrics": "2.11.1-alpha.0",
|
|
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.4.1"
|
|
34
|
-
"uuid": "^9.0.1"
|
|
30
|
+
"oidc-client-ts": "3.4.1"
|
|
35
31
|
},
|
|
36
32
|
"devDependencies": {
|
|
37
|
-
"@imtbl/toolkit": "2.11.1-alpha.0",
|
|
38
33
|
"@swc/core": "^1.3.36",
|
|
39
34
|
"@swc/jest": "^0.2.37",
|
|
40
35
|
"@types/jest": "^29.5.12",
|
package/src/Auth.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Auth } from './Auth';
|
|
2
2
|
import { AuthEvents, User } from './types';
|
|
3
3
|
import { withMetricsAsync } from './utils/metrics';
|
|
4
|
-
import
|
|
4
|
+
import { decodeJwtPayload } from './utils/jwt';
|
|
5
5
|
|
|
6
6
|
const trackFlowMock = jest.fn();
|
|
7
7
|
const trackErrorMock = jest.fn();
|
|
@@ -18,7 +18,9 @@ jest.mock('@imtbl/metrics', () => ({
|
|
|
18
18
|
getDetail: (...args: any[]) => getDetailMock(...args),
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
|
-
jest.mock('jwt
|
|
21
|
+
jest.mock('./utils/jwt', () => ({
|
|
22
|
+
decodeJwtPayload: jest.fn(),
|
|
23
|
+
}));
|
|
22
24
|
|
|
23
25
|
beforeEach(() => {
|
|
24
26
|
trackFlowMock.mockReset();
|
|
@@ -26,7 +28,7 @@ beforeEach(() => {
|
|
|
26
28
|
identifyMock.mockReset();
|
|
27
29
|
trackMock.mockReset();
|
|
28
30
|
getDetailMock.mockReset();
|
|
29
|
-
(
|
|
31
|
+
(decodeJwtPayload as jest.Mock).mockReset();
|
|
30
32
|
});
|
|
31
33
|
|
|
32
34
|
describe('withMetricsAsync', () => {
|
|
@@ -145,14 +147,14 @@ describe('Auth', () => {
|
|
|
145
147
|
profile: { sub: 'user-123', email: 'test@example.com', nickname: 'tester' },
|
|
146
148
|
};
|
|
147
149
|
|
|
148
|
-
(
|
|
150
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
149
151
|
username: 'username123',
|
|
150
152
|
passport: undefined,
|
|
151
153
|
});
|
|
152
154
|
|
|
153
155
|
const result = (Auth as any).mapOidcUserToDomainModel(mockOidcUser);
|
|
154
156
|
|
|
155
|
-
expect(
|
|
157
|
+
expect(decodeJwtPayload).toHaveBeenCalledWith('token');
|
|
156
158
|
expect(result.profile.username).toEqual('username123');
|
|
157
159
|
});
|
|
158
160
|
|
|
@@ -165,7 +167,7 @@ describe('Auth', () => {
|
|
|
165
167
|
expires_in: 3600,
|
|
166
168
|
};
|
|
167
169
|
|
|
168
|
-
(
|
|
170
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
169
171
|
sub: 'user-123',
|
|
170
172
|
iss: 'issuer',
|
|
171
173
|
aud: 'audience',
|
|
@@ -179,7 +181,7 @@ describe('Auth', () => {
|
|
|
179
181
|
|
|
180
182
|
const oidcUser = (Auth as any).mapDeviceTokenResponseToOidcUser(tokenResponse);
|
|
181
183
|
|
|
182
|
-
expect(
|
|
184
|
+
expect(decodeJwtPayload).toHaveBeenCalledWith('token');
|
|
183
185
|
expect(oidcUser.profile.username).toEqual('username123');
|
|
184
186
|
});
|
|
185
187
|
});
|
package/src/Auth.ts
CHANGED
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
UserManagerSettings,
|
|
8
8
|
WebStorageStateStore,
|
|
9
9
|
} from 'oidc-client-ts';
|
|
10
|
-
import axios from 'axios';
|
|
11
|
-
import jwt_decode from 'jwt-decode';
|
|
12
10
|
import localForage from 'localforage';
|
|
13
11
|
import {
|
|
14
12
|
Detail,
|
|
@@ -35,6 +33,7 @@ import {
|
|
|
35
33
|
import EmbeddedLoginPrompt from './login/embeddedLoginPrompt';
|
|
36
34
|
import TypedEventEmitter from './utils/typedEventEmitter';
|
|
37
35
|
import { withMetricsAsync } from './utils/metrics';
|
|
36
|
+
import { decodeJwtPayload } from './utils/jwt';
|
|
38
37
|
import DeviceCredentialsManager from './storage/device_credentials_manager';
|
|
39
38
|
import { PassportError, PassportErrorType, withPassportError } from './errors';
|
|
40
39
|
import logger from './utils/logger';
|
|
@@ -42,10 +41,37 @@ import { isAccessTokenExpiredOrExpiring } from './utils/token';
|
|
|
42
41
|
import LoginPopupOverlay from './overlay/loginPopupOverlay';
|
|
43
42
|
import { LocalForageAsyncStorage } from './storage/LocalForageAsyncStorage';
|
|
44
43
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
const formUrlEncodedHeaders = {
|
|
45
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const parseJsonSafely = (text: string): unknown => {
|
|
49
|
+
if (!text) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(text);
|
|
54
|
+
} catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const extractTokenErrorMessage = (
|
|
60
|
+
payload: unknown,
|
|
61
|
+
fallbackText: string,
|
|
62
|
+
status: number,
|
|
63
|
+
): string => {
|
|
64
|
+
if (payload && typeof payload === 'object') {
|
|
65
|
+
const data = payload as Record<string, unknown>;
|
|
66
|
+
const description = data.error_description ?? data.message ?? data.error;
|
|
67
|
+
if (typeof description === 'string' && description.trim().length > 0) {
|
|
68
|
+
return description;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (fallbackText.trim().length > 0) {
|
|
72
|
+
return fallbackText;
|
|
73
|
+
}
|
|
74
|
+
return `Token request failed with status ${status}`;
|
|
49
75
|
};
|
|
50
76
|
|
|
51
77
|
const logoutEndpoint = '/v2/logout';
|
|
@@ -523,7 +549,7 @@ export class Auth {
|
|
|
523
549
|
let passport: PassportMetadata | undefined;
|
|
524
550
|
let username: string | undefined;
|
|
525
551
|
if (oidcUser.id_token) {
|
|
526
|
-
const idTokenPayload =
|
|
552
|
+
const idTokenPayload = decodeJwtPayload<IdTokenPayload>(oidcUser.id_token);
|
|
527
553
|
passport = idTokenPayload?.passport;
|
|
528
554
|
if (idTokenPayload?.username) {
|
|
529
555
|
username = idTokenPayload?.username;
|
|
@@ -552,7 +578,7 @@ export class Auth {
|
|
|
552
578
|
};
|
|
553
579
|
|
|
554
580
|
private static mapDeviceTokenResponseToOidcUser = (tokenResponse: DeviceTokenResponse): OidcUser => {
|
|
555
|
-
const idTokenPayload: IdTokenPayload =
|
|
581
|
+
const idTokenPayload: IdTokenPayload = decodeJwtPayload(tokenResponse.id_token);
|
|
556
582
|
return new OidcUser({
|
|
557
583
|
id_token: tokenResponse.id_token,
|
|
558
584
|
access_token: tokenResponse.access_token,
|
|
@@ -650,18 +676,39 @@ export class Auth {
|
|
|
650
676
|
}
|
|
651
677
|
|
|
652
678
|
private async getPKCEToken(authorizationCode: string, codeVerifier: string): Promise<DeviceTokenResponse> {
|
|
653
|
-
const response = await
|
|
679
|
+
const response = await fetch(
|
|
654
680
|
`${this.config.authenticationDomain}/oauth/token`,
|
|
655
681
|
{
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
682
|
+
method: 'POST',
|
|
683
|
+
headers: formUrlEncodedHeaders,
|
|
684
|
+
body: new URLSearchParams({
|
|
685
|
+
client_id: this.config.oidcConfiguration.clientId,
|
|
686
|
+
grant_type: 'authorization_code',
|
|
687
|
+
code_verifier: codeVerifier,
|
|
688
|
+
code: authorizationCode,
|
|
689
|
+
redirect_uri: this.config.oidcConfiguration.redirectUri,
|
|
690
|
+
}),
|
|
661
691
|
},
|
|
662
|
-
formUrlEncodedHeader,
|
|
663
692
|
);
|
|
664
|
-
|
|
693
|
+
|
|
694
|
+
const responseText = await response.text();
|
|
695
|
+
const parsedBody = parseJsonSafely(responseText);
|
|
696
|
+
|
|
697
|
+
if (!response.ok) {
|
|
698
|
+
throw new Error(
|
|
699
|
+
extractTokenErrorMessage(
|
|
700
|
+
parsedBody,
|
|
701
|
+
responseText,
|
|
702
|
+
response.status,
|
|
703
|
+
),
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (!parsedBody || typeof parsedBody !== 'object') {
|
|
708
|
+
throw new Error('Token endpoint returned an invalid response');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return parsedBody as DeviceTokenResponse;
|
|
665
712
|
}
|
|
666
713
|
|
|
667
714
|
private async storeTokensInternal(tokenResponse: DeviceTokenResponse): Promise<User> {
|
package/src/errors.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isAxiosError } from 'axios';
|
|
2
1
|
import { imx } from '@imtbl/generated-clients';
|
|
3
2
|
|
|
4
3
|
export enum PassportErrorType {
|
|
@@ -35,6 +34,31 @@ export function isAPIError(error: any): error is imx.APIError {
|
|
|
35
34
|
);
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
type AxiosLikeError = {
|
|
38
|
+
response?: {
|
|
39
|
+
data?: unknown;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const extractApiError = (error: unknown): imx.APIError | undefined => {
|
|
44
|
+
if (isAPIError(error)) {
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
typeof error === 'object'
|
|
50
|
+
&& error !== null
|
|
51
|
+
&& 'response' in error
|
|
52
|
+
) {
|
|
53
|
+
const { response } = error as AxiosLikeError;
|
|
54
|
+
if (response?.data && isAPIError(response.data)) {
|
|
55
|
+
return response.data;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
|
|
38
62
|
export class PassportError extends Error {
|
|
39
63
|
public type: PassportErrorType;
|
|
40
64
|
|
|
@@ -57,8 +81,9 @@ export const withPassportError = async <T>(
|
|
|
57
81
|
throw new PassportError(error.message, error.type);
|
|
58
82
|
}
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
const apiError = extractApiError(error);
|
|
85
|
+
if (apiError) {
|
|
86
|
+
errorMessage = apiError.message;
|
|
62
87
|
} else {
|
|
63
88
|
errorMessage = (error as Error).message;
|
|
64
89
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable class-methods-use-this */
|
|
2
|
-
import jwt_decode from 'jwt-decode';
|
|
3
2
|
import { TokenPayload, PKCEData } from '../types';
|
|
3
|
+
import { decodeJwtPayload } from '../utils/jwt';
|
|
4
4
|
|
|
5
5
|
const KEY_PKCE_STATE = 'pkce_state';
|
|
6
6
|
const KEY_PKCE_VERIFIER = 'pkce_verifier';
|
|
@@ -9,7 +9,7 @@ const validCredentialsMinTtlSec = 3600; // 1 hour
|
|
|
9
9
|
export default class DeviceCredentialsManager {
|
|
10
10
|
private isTokenValid(jwt: string): boolean {
|
|
11
11
|
try {
|
|
12
|
-
const tokenPayload: TokenPayload =
|
|
12
|
+
const tokenPayload: TokenPayload = decodeJwtPayload(jwt);
|
|
13
13
|
const expiresAt = tokenPayload.exp ?? 0;
|
|
14
14
|
const now = (Date.now() / 1000) + validCredentialsMinTtlSec;
|
|
15
15
|
return expiresAt > now;
|
package/src/utils/jwt.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */
|
|
2
|
+
const getGlobal = (): typeof globalThis => {
|
|
3
|
+
if (typeof globalThis !== 'undefined') {
|
|
4
|
+
return globalThis;
|
|
5
|
+
}
|
|
6
|
+
if (typeof self !== 'undefined') {
|
|
7
|
+
return self;
|
|
8
|
+
}
|
|
9
|
+
if (typeof window !== 'undefined') {
|
|
10
|
+
return window;
|
|
11
|
+
}
|
|
12
|
+
if (typeof global !== 'undefined') {
|
|
13
|
+
return global;
|
|
14
|
+
}
|
|
15
|
+
return {} as typeof globalThis;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const base64UrlToBase64 = (input: string): string => {
|
|
19
|
+
const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
20
|
+
const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));
|
|
21
|
+
return normalized + padding;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const decodeWithAtob = (value: string): string | null => {
|
|
25
|
+
const globalRef = getGlobal();
|
|
26
|
+
if (typeof globalRef.atob !== 'function') {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const binary = globalRef.atob(value);
|
|
31
|
+
const bytes = new Uint8Array(binary.length);
|
|
32
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
33
|
+
bytes[i] = binary.charCodeAt(i);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof globalRef.TextDecoder === 'function') {
|
|
37
|
+
return new globalRef.TextDecoder('utf-8').decode(bytes);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let result = '';
|
|
41
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
42
|
+
result += String.fromCharCode(bytes[i]);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const base64Decode = (value: string): string => {
|
|
48
|
+
if (typeof Buffer !== 'undefined') {
|
|
49
|
+
return Buffer.from(value, 'base64').toString('utf-8');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const decoded = decodeWithAtob(value);
|
|
53
|
+
if (decoded === null) {
|
|
54
|
+
throw new Error('Base64 decoding is not supported in this environment');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return decoded;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const decodeJwtPayload = <T>(token: string): T => {
|
|
61
|
+
if (typeof token !== 'string') {
|
|
62
|
+
throw new Error('JWT must be a string');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const segments = token.split('.');
|
|
66
|
+
if (segments.length < 2) {
|
|
67
|
+
throw new Error('Invalid JWT: payload segment is missing');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const payloadSegment = segments[1];
|
|
71
|
+
const json = base64Decode(base64UrlToBase64(payloadSegment));
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(json) as T;
|
|
75
|
+
} catch {
|
|
76
|
+
throw new Error('Invalid JWT payload: unable to parse JSON');
|
|
77
|
+
}
|
|
78
|
+
};
|
package/src/utils/token.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import jwt_decode from 'jwt-decode';
|
|
2
1
|
import {
|
|
3
2
|
User as OidcUser,
|
|
4
3
|
} from 'oidc-client-ts';
|
|
5
4
|
import { IdTokenPayload, TokenPayload } from '../types';
|
|
5
|
+
import { decodeJwtPayload } from './jwt';
|
|
6
6
|
|
|
7
7
|
function isTokenExpiredOrExpiring(token: string): boolean {
|
|
8
8
|
try {
|
|
9
9
|
// try to decode the token as access token payload or id token payload
|
|
10
|
-
const decodedToken =
|
|
10
|
+
const decodedToken = decodeJwtPayload<TokenPayload | IdTokenPayload>(token);
|
|
11
11
|
const now = Math.floor(Date.now() / 1000);
|
|
12
12
|
|
|
13
13
|
// Tokens without expiration claims are invalid (security vulnerability)
|