UNPKG

@trimble-oss/trimble-id-react

Version:

> **Important Notice:** > > As of version 1.0.0, `PersistentOptions` have been removed. By default, the SDK now supports in-memory token storage. > > When you upgrade to version 1.x, storage options will no longer be available, resulting in a breaking

2 lines (1 loc) 12.8 kB
(function(o,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@trimble-oss/trimble-id"),require("es-cookie"),require("jwt-decode"),require("react"),require("react/jsx-runtime")):typeof define=="function"&&define.amd?define(["exports","@trimble-oss/trimble-id","es-cookie","jwt-decode","react","react/jsx-runtime"],l):(o=typeof globalThis<"u"?globalThis:o||self,l(o.ReactTID={},o.trimbleId,o.esCookie,o.jwt_decode,o.React,o.jsxRuntime))})(this,function(o,l,p,W,d,E){"use strict";var de=Object.defineProperty;var he=(o,l,p)=>l in o?de(o,l,{enumerable:!0,configurable:!0,writable:!0,value:p}):o[l]=p;var u=(o,l,p)=>(he(o,typeof l!="symbol"?l+"":l,p),p);function G(i){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const t in i)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(i,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>i[t]})}}return e.default=i,Object.freeze(e)}const S=G(p);class F{constructor(){u(this,"generateCache",function(){const e={token:void 0,user:void 0};return{async getToken(){return e.token},async getUser(){return e.user},async storeToken(t){e.token=t},async storeUser(t){e.user=t},clear(){return e.token=void 0,e.user=void 0,Promise.resolve(void 0)}}}())}}class j{constructor(){u(this,"cacheStorage");this.cacheStorage=new F().generateCache}async setToken(e){await this.cacheStorage.storeToken(e)}async setUser(e){await this.cacheStorage.storeUser(e)}async getUser(){return this.cacheStorage.getUser()}async getToken(){return this.cacheStorage.getToken()}async clear(){await this.cacheStorage.clear()}}const C=5*6e4,$=["code","state"],_=10*60*1e3,I=i=>i.split("?")[0],q=i=>i.split("?")[1],M=i=>{let e=i;if(!i.startsWith("?"))try{e=new URL(i).search}catch{}return new URLSearchParams(e)},B=(i=window.location.href,e)=>{if(e!=null){const n=I(e),r=I(i??"");if(n!==r)return!1}const t=M(i);for(const n of $)if(!t.has(n))return!1;return!0},J=i=>({id:i.sub,name:`${i.given_name} ${i.family_name}`,given_name:i.given_name,family_name:i.family_name,picture:i.picture,email:i.email,email_verified:i.email_verified}),H="@TID_COOKIE";class z{get(e){const t=S.get(e);if(t==null)throw new Error("Cookie not found");return JSON.parse(t)}set(e,t,n){let r={};window.location.protocol==="https:"&&(r={secure:!0,sameSite:"none"}),n!=null&&n.expires&&(r.expires=n.expires),n!=null&&n.domain&&(r.domain=n.domain),S.set(e,JSON.stringify(t),r)}remove(e,t){const n={};t!=null&&t.domain&&(n.domain=t.domain),S.remove(e,n)}}class Q{constructor(e){u(this,"cookieKey");u(this,"cookiesStorage");this.cookieKey=`${H}.${e.clientId}`,this.cookiesStorage=new z}save(e){const n={...this.get()||{},...e};this.cookiesStorage.set(this.cookieKey,n,{expires:1})}getStatePayload(){const e=this.get();return e==null?void 0:e.state_payload}clearStatePayload(){const e=this.get();e&&(delete e.state_payload,this.cookiesStorage.set(this.cookieKey,e,{expires:1}))}get(){try{return this.cookiesStorage.get(this.cookieKey)}catch{return}}clear(){this.cookiesStorage.remove(this.cookieKey)}}class R extends Error{}class x extends Error{}class X extends Error{}const g="@trimble-oss/trimble-id-react",f="1.0.1",Y={configurationEndpoint:"",clientId:"",redirectUrl:"",logoutRedirectUrl:"",scopes:[]};class A{constructor(e){u(this,"tokenProvider");u(this,"cacheManager");u(this,"cookiesManager");u(this,"clientId");u(this,"redirectUrl");u(this,"analyticshttpclient");const{config:t=Y}=e;if(this.redirectUrl=t.redirectUrl,t.configurationEndpoint==null||t.configurationEndpoint=="")throw new Error("Configuration endpoint not defined");if(t.clientId==null||t.clientId=="")throw new Error("Consumer key is not defined");this.cookiesManager=new Q({clientId:t.clientId});const n=this.cookiesManager.get(),r=new l.OpenIdEndpointProvider(t.configurationEndpoint);let c=new l.AuthorizationCodeGrantTokenProvider(r,t.clientId,t.redirectUrl).WithScopes(t.scopes);t.logoutRedirectUrl&&(c=c.WithLogoutRedirect(t.logoutRedirectUrl)),(n==null?void 0:n.code_verifier)!=null&&(c=c.WithProofKeyForCodeExchange(n.code_verifier)),this.tokenProvider=c,this.cacheManager=new j,this.clientId=t.clientId,this.analyticshttpclient=l.AnalyticsHttpClient,this.analyticshttpclient.sendInitEvent("TIDClient",this.clientId,g,f),this.cleanupExpiredState()}cleanupExpiredState(){try{const e=this.cookiesManager.getStatePayload();if(e){const t=atob(e),n=JSON.parse(t);Date.now()-n.timestamp>_&&this.cookiesManager.clearStatePayload()}}catch{this.cookiesManager.clearStatePayload()}}async loginWithRedirect(e){this.analyticshttpclient.sendMethodEvent(this.loginWithRedirect.name,this.clientId,g,f);const{onRedirect:t}=e||{},n=window.location.pathname+window.location.search,r=this.createStatePayload(n);this.cookiesManager.save({state_payload:r});const c=l.AuthorizationCodeGrantTokenProvider.GenerateCodeVerifier();this.cookiesManager.save({code_verifier:c}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(c);const h=await this.tokenProvider.GetOAuthRedirect(r);t!=null?t(h):window.location.assign(h)}async handleCallback(e=window.location.href){const t=this.cookiesManager.get();if(t==null||(t==null?void 0:t.code_verifier)==null)throw new X("Code verifier not available");const n=q(e),r=M(e),c=r.get("identity_provider")??"",h=r.get("state")??"",y=this.validateStatePayload(h);if(!y.isValid)throw new Error(`State validation failed: ${y.error}`);return await this.tokenProvider.ValidateQuery(n),await this.generateToken(c),{authState:h,returnTo:y.redirectTo}}async generateToken(e){var a,v;const t=await this.tokenProvider.RetrieveToken(),n=await this.tokenProvider.RetrieveRefreshToken(),r=await this.tokenProvider.RetrieveIdToken(),c=await this.tokenProvider.RetrieveTokenExpiry(),y=new Date(c).getTime(),T=(v=(a=this.tokenProvider)==null?void 0:a._scopes)==null?void 0:v.join(" ");await this.cacheManager.setToken({scope:T,state:"",session_state:"",identity_provider:e,token_type:"bearer",access_token:t,refresh_token:n,id_token:r,expires_at:y});const U=W(r),P=J(U);await this.cacheManager.setUser(P),await this.reloadCodeVerifier()}async reloadCodeVerifier(){const e=await this.tokenProvider.RetrieveCodeVerifier();this.cookiesManager.save({code_verifier:e}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(e)}createStatePayload(e){const t=this.generateNonce(),n=Date.now();return btoa(JSON.stringify({redirectTo:e,timestamp:n,nonce:t}))}generateNonce(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}validateStatePayload(e){try{if(!e||e.trim()==="")return{isValid:!1,error:"Empty state parameter"};const t=this.cookiesManager.getStatePayload();if(!t)return{isValid:!1,error:"No stored state found"};const n=atob(e),r=JSON.parse(n),c=atob(t),h=JSON.parse(c);if(!r.nonce||!r.timestamp||!r.redirectTo)return{isValid:!1,error:"Invalid state payload structure"};if(r.nonce!==h.nonce)return{isValid:!1,error:"State nonce mismatch"};const T=Date.now()-r.timestamp;return T>_?{isValid:!1,error:"State expired - possible replay attack"}:T<0?{isValid:!1,error:"State timestamp is in the future - possible replay attack"}:(this.cookiesManager.clearStatePayload(),{isValid:!0,redirectTo:r.redirectTo})}catch{return{isValid:!1,error:"Invalid state format"}}}async getUser(){return this.analyticshttpclient.sendMethodEvent(this.getUser.name,this.clientId,g,f),await this.cacheManager.getUser()}async getAccessTokenSilently(){this.analyticshttpclient.sendMethodEvent(this.getAccessTokenSilently.name,this.clientId,g,f);let e=await this.cacheManager.getToken();if(e==null)throw this.analyticshttpclient.sendExceptionEvent(this.getAccessTokenSilently.name,"No token available",this.clientId,g,f),new x("No token available");const t=new Date(new Date().getTime()+C);if((e==null?void 0:e.expires_at)==null||(e==null?void 0:e.expires_at)<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(n){throw this.analyticshttpclient.sendExceptionEvent(this.getAccessTokenSilently.name,n.message,this.clientId,g,f),new R(n.message)}await this.generateToken((e==null?void 0:e.identity_provider)??""),e=await this.cacheManager.getToken()}return(e==null?void 0:e.access_token)||""}async getTokens(){this.analyticshttpclient.sendMethodEvent(this.getTokens.name,this.clientId,g,f);let e=await this.cacheManager.getToken();if(e==null)throw this.analyticshttpclient.sendExceptionEvent(this.getTokens.name,"No token available",this.clientId,g,f),new x("No token available");const t=new Date(new Date().getTime()+C);if((e==null?void 0:e.expires_at)==null||(e==null?void 0:e.expires_at)<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(n){throw this.analyticshttpclient.sendExceptionEvent(this.getTokens.name,n.message,this.clientId,g,f),new R(n.message)}await this.generateToken((e==null?void 0:e.identity_provider)??""),e=await this.cacheManager.getToken()}return{access_token:(e==null?void 0:e.access_token)||"",expires_at:(e==null?void 0:e.expires_at)||0,id_token:(e==null?void 0:e.id_token)||""}}async logout(e){this.analyticshttpclient.sendMethodEvent(this.logout.name,this.clientId,g,f);const{onRedirect:t,disabledAutoRedirect:n}=e||{};this.cacheManager&&await this.cacheManager.clear(),this.cookiesManager&&this.cookiesManager.clear();const r=await this.tokenProvider.GetOAuthLogoutRedirect("state");if(t!=null)return t(r);n||window.location.assign(r)}async checkSession(){try{await this.getAccessTokenSilently()}catch{return!1}return!0}async loadUserSession(){await this.loadCacheSessionIntoSDK(),await this.checkSession()||await this.cacheManager.clear()}async loadCacheSessionIntoSDK(){const e=await this.cacheManager.getToken();if(e==null)return;const t=e.access_token,n=e.refresh_token||"",r=e.id_token,c=e.expires_at;this.tokenProvider=this.tokenProvider.WithAccessToken(t,c).WithRefreshToken(n).WithIdToken(r)}getBearerTokenHttpClient(e){return new l.BearerTokenHttpClientProvider(this.tokenProvider,e)}getRedirectUrl(){return this.redirectUrl}}const w=d.createContext(null),O=()=>d.useContext(w)??{},Z=(i,e)=>{switch(e.type){case"INIT":return{...i,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"GET_TOKENS_COMPLETE":case"HANDLE_CALLBACK_COMPLETE":case"GET_ACCESS_TOKEN_COMPLETE":return{...i,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"LOGOUT":return{...i,isAuthenticated:!1,user:void 0};case"ERROR":return{...i,isLoading:!1,error:e.error}}},ee={isLoading:!0,isAuthenticated:!1},te=i=>{if(i==null||Object.keys(i).length<=0)return!0;const e=Object.keys(i);for(const t of e)if(i[t]!=null&&i[t]!=="")return!1;return!0},ie=i=>!te(i.config),ne=i=>{const{children:e,configurationEndpoint:t,clientId:n,redirectUrl:r,logoutRedirectUrl:c,scopes:h,onRedirectCallback:y,checkRedirectUrlMatch:T}=i;if(d.useContext(w)!=null)throw new Error("TID Provider already defined");const P={config:{configurationEndpoint:t??"",clientId:n??"",redirectUrl:r??"",logoutRedirectUrl:c??"",scopes:h??[""]}},[a]=d.useState(i.tidClient??new A(P)),[v,m]=d.useReducer(Z,ee),D=d.useRef(!1),se=d.useMemo(()=>T?a.getRedirectUrl():void 0,[T,a]);d.useEffect(()=>{D.current||(i.tidClient!=null&&ie({config:{configurationEndpoint:t,clientId:n,redirectUrl:r,logoutRedirectUrl:c,scopes:h}})&&console.warn("When TID client is pass as prop, any client configuration property sent directly to the TID Provider component will be ignored"),D.current=!0,(async()=>{try{let s;if(B(void 0,se)){const k=await a.handleCallback();s=await a.getUser(),y!=null&&y(k)}else await a.loadUserSession(),s=await a.getUser();m({type:"INIT",user:s})}catch(s){let k=s;typeof s=="string"&&(k=new Error(s)),m({type:"ERROR",error:k})}})())},[a,y]);const b=d.useCallback(async()=>{const s=await a.getAccessTokenSilently(),k=await a.getUser();return m({type:"GET_ACCESS_TOKEN_COMPLETE",user:k}),s},[a]),L=d.useCallback(async()=>{const s=await a.getTokens(),k=await a.getUser();return m({type:"GET_TOKENS_COMPLETE",user:k}),s},[a]),N=d.useCallback(async s=>{await a.loginWithRedirect(s)},[a]),V=d.useCallback(async s=>{await a.logout(s),(s==null?void 0:s.disabledAutoRedirect)!=null&&s.disabledAutoRedirect&&m({type:"LOGOUT"})},[a]),K=d.useCallback(async s=>{const k=await a.handleCallback(s),le=await a.getUser();return m({type:"HANDLE_CALLBACK_COMPLETE",user:le}),k},[a]),ce=d.useMemo(()=>({...v,getAccessTokenSilently:b,getTokens:L,loginWithRedirect:N,handleCallback:K,logout:V}),[v,b,L,N,K,V]);return E.jsx(w.Provider,{value:ce,children:e})},re=O,ae=ne,oe=({renderComponent:i,loader:e})=>{const{isAuthenticated:t,isLoading:n,loginWithRedirect:r}=O();return d.useEffect(()=>{!n&&!t&&(async()=>await r())()},[n,t,r]),t?i:e||E.jsx(E.Fragment,{})};o.AuthenticationGuard=oe,o.TIDClient=A,o.TIDContext=w,o.TIDProvider=ae,o.useAuth=re,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});