@axa-fr/react-oidc 7.29.3 → 7.29.5

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.
@@ -1 +1 @@
1
- (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@axa-fr/oidc-client`),require(`react`),require(`react/jsx-runtime`)):typeof define==`function`&&define.amd?define([`exports`,`@axa-fr/oidc-client`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e[`react-oidc`]={},e._axa_fr_oidc_client,e.React,e.react_jsx_runtime))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=s(t),c=0,u=i.length,d;c<u;c++)d=i[c],!l.call(e,d)&&d!==n&&a(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=o(t,d))||r.enumerable});return e};n=((e,t,n)=>(n=e==null?{}:i(c(e)),u(t||!e||!e.__esModule?a(n,`default`,{value:e,enumerable:!0}):n,e)))(n,1);var d=`default`,f=(e,t,n=!1)=>async(...r)=>await t().fetchWithTokens(e,n)(...r),p=(e=null,t=d,n=!1)=>i=>a=>{let{fetch:o}=m(e||a.fetch,t,n);return(0,r.jsx)(i,{...a,fetch:o})},m=(e=null,r=d,i=!1)=>{let a=e||window.fetch,o=t.OidcClient.getOrThrow;return{fetch:(0,n.useCallback)((e,t)=>f(a,()=>o(r),i)(e,t),[a,r,i])}},h=()=>(0,r.jsx)(`div`,{className:`oidc-authenticating`,children:(0,r.jsxs)(`div`,{className:`oidc-authenticating__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-authenticating__title`,children:`Error authentication`}),(0,r.jsx)(`p`,{className:`oidc-authenticating__content`,children:`An error occurred during authentication.`})]})}),g=()=>(0,r.jsx)(`div`,{className:`oidc-authenticating`,children:(0,r.jsxs)(`div`,{className:`oidc-authenticating__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-authenticating__title`,children:`Authentication in progress`}),(0,r.jsx)(`p`,{className:`oidc-authenticating__content`,children:`You will be redirected to the login page.`})]})}),_=()=>Math.random().toString(36).slice(2,8),v=(e,t)=>(n,r)=>{if(typeof e.CustomEvent==`function`)return new e.CustomEvent(n,r);let i=r||{bubbles:!1,cancelable:!1,detail:void 0},a=t.createEvent(`CustomEvent`);return a.initCustomEvent(n,i.bubbles,i.cancelable,i.detail),a.prototype=e.Event.prototype,a},y=(e,t,n)=>({replaceState:(r,i)=>{let a=n(),o=i||e.history.state;e.history.replaceState({key:a,state:o},null,r),e.dispatchEvent(t(`popstate`))}}),b=()=>y(window,v(window,document),_),x=()=>(0,r.jsx)(`div`,{className:`oidc-callback`,children:(0,r.jsxs)(`div`,{className:`oidc-callback__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-callback__title`,children:`Authentication complete`}),(0,r.jsx)(`p`,{className:`oidc-callback__content`,children:`You will be redirected to your application.`})]})}),S=200,C=(e,t=window)=>t.location.pathname===e||e===`/`,w=({callBackError:e,callBackSuccess:i,configurationName:a,withCustomHistory:o,navigateAfterCallback:s})=>{let[c,l]=(0,n.useState)(!1);(0,n.useEffect)(()=>{let e=!0;return(async()=>{let n=t.OidcClient.getOrThrow;try{let r=n(a),{callbackPath:i}=await r.loginCallbackAsync(),c=i||`/`;if(s)try{await s(c),r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigated,{configurationName:a,callbackPath:c})}catch(n){r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigation_error,{configurationName:a,callbackPath:c,error:n}),e&&(console.warn(n),l(!0))}else (o?o():b()).replaceState(c),await new Promise(e=>{setTimeout(()=>{e()},S)}),e&&(C(c)?r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigated,{configurationName:a,callbackPath:c}):(r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigation_error,{configurationName:a,callbackPath:c,error:Error(`Navigation did not commit: expected "${c}" but found "${window.location.pathname}"`)}),l(!0)))}catch(t){e&&(console.warn(t),l(!0))}})(),()=>{e=!1}},[]);let u=e||h,d=i||x;return c?(0,r.jsx)(u,{configurationName:a}):(0,r.jsx)(d,{configurationName:a})},T=()=>(0,r.jsx)(`span`,{className:`oidc-loading`,children:`Loading`}),E=()=>(0,r.jsx)(`div`,{className:`oidc-loading-timeout`,children:(0,r.jsxs)(`div`,{className:`oidc-loading-timeout__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-loading-timeout__title`,children:`Loading timeout`}),(0,r.jsx)(`p`,{className:`oidc-loading-timeout__content`,children:`Authentication is taking longer than expected. Please try refreshing the page.`})]})}),D=()=>(0,r.jsx)(`div`,{className:`oidc-serviceworker`,children:(0,r.jsxs)(`div`,{className:`oidc-serviceworker__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-serviceworker__title`,children:`Unable to authenticate on this browser`}),(0,r.jsx)(`p`,{className:`oidc-serviceworker__content`,children:`Your browser is not secure enough to make authentication work. Try updating your browser or use a newer browser.`})]})}),O=()=>(0,r.jsx)(`div`,{className:`oidc-session-lost`,children:(0,r.jsxs)(`div`,{className:`oidc-session-lost__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-session-lost__title`,children:`Session timed out`}),(0,r.jsx)(`p`,{className:`oidc-session-lost__content`,children:`Your session has expired. Please re-authenticate.`})]})}),k=({configurationName:e})=>((0,n.useEffect)(()=>{(async()=>{t.OidcClient.getOrThrow(e).silentLoginCallbackAsync()})().catch(e=>{console.error(`Error during silent login callback:`,e)})},[e]),null),A=({configurationName:e})=>{let i=(0,t.getParseQueryStringFromLocation)(window.location.href),a=t.OidcClient.getOrThrow,o=a(e),s=null;for(let[e,t]of Object.entries(i))e===`state`||e===`scope`||(s===null&&(s={}),s[e]=t);return(0,n.useEffect)(()=>{o.tokens||o.loginAsync(null,s,!0,i.scope)},[]),(0,r.jsx)(r.Fragment,{})},j=n.default.memo(({callbackErrorComponent:e,callbackSuccessComponent:i,redirect_uri:a,silent_redirect_uri:o,silent_login_uri:s,children:c,configurationName:l,withCustomHistory:u=null,navigateAfterCallback:d=null})=>{let[f,p]=(0,n.useState)(window?(0,t.getPath)(window.location.href):``);(0,n.useEffect)(()=>{let e=()=>p((0,t.getPath)(window.location.href));return e(),window.addEventListener(`popstate`,e,!1),()=>window.removeEventListener(`popstate`,e,!1)},[]);let m=(0,t.getPath)(a);if(o&&f===(0,t.getPath)(o))return(0,r.jsx)(k,{configurationName:l});if(s&&f===(0,t.getPath)(s))return(0,r.jsx)(A,{configurationName:l});switch(f){case m:return(0,r.jsx)(w,{callBackError:e,callBackSuccess:i,configurationName:l,withCustomHistory:u,navigateAfterCallback:d});default:return(0,r.jsx)(r.Fragment,{children:c})}}),M={name:``,data:null},N=({loadingComponent:e,children:i,configurationName:a})=>{let[o,s]=(0,n.useState)(!0),c=t.OidcClient.get,l=c(a);return(0,n.useEffect)(()=>{let e=!0;return l&&l.tryKeepExistingSessionAsync().then(()=>{e&&s(!1)}),()=>{e=!1}},[a]),(0,r.jsx)(r.Fragment,{children:o?(0,r.jsx)(e,{configurationName:a}):(0,r.jsx)(r.Fragment,{children:i})})},P=({isLoading:e,loadingComponent:t,children:n,configurationName:i})=>{let a=t;return e?(0,r.jsx)(a,{configurationName:i,children:n}):(0,r.jsx)(r.Fragment,{children:n})},F=3e4,I=e=>e===t.OidcClient.eventNames.token_acquired||e===t.OidcClient.eventNames.token_renewed||e===t.OidcClient.eventNames.loginCallbackAsync_end||e===t.OidcClient.eventNames.tryKeepExistingSessionAsync_end||e===t.OidcClient.eventNames.tryKeepExistingSessionAsync_error,L=({children:e,configuration:i,configurationName:a=`default`,callbackSuccessComponent:o=x,authenticatingComponent:s=g,loadingComponent:c=T,loadingTimeoutComponent:l=E,serviceWorkerNotSupportedComponent:u=D,authenticatingErrorComponent:d=h,sessionLostComponent:f=O,onSessionLost:p=null,onLogoutFromAnotherTab:m=null,onLogoutFromSameTab:_=null,withCustomHistory:v=null,navigateAfterCallback:y=null,onEvent:b=null,getFetch:S=null,location:C=null})=>{if(i&&i.redirect_uri&&i.silent_redirect_uri&&i.redirect_uri===i.silent_redirect_uri)throw Error(`redirect_uri and silent_redirect_uri must be different`);let w=(e=`default`)=>t.OidcClient.getOrCreate(S??t.getFetchDefault,C??new t.OidcLocation)(i,e),[k,A]=(0,n.useState)(M),[L,R]=(0,n.useState)(!0),[z,B]=(0,n.useState)(a);(0,n.useEffect)(()=>{let e=w(a).subscribeEvents((e,t)=>{b&&b(a,e,t)});return()=>{w(a).removeEventSubscription(e)}},[a,b]),(0,n.useEffect)(()=>{let e=w(a).subscribeEvents((e,n)=>{if(e===t.OidcClient.eventNames.loginAsync_begin?R(!0):I(e)&&R(!1),e===t.OidcClient.eventNames.refreshTokensAsync_error||e===t.OidcClient.eventNames.syncTokensAsync_error){if(p!=null){p();return}A({name:e,data:n})}else if(e===t.OidcClient.eventNames.logout_from_another_tab){if(m!=null){m();return}A({name:e,data:n})}else e===t.OidcClient.eventNames.logout_from_same_tab?_?.():(e===t.OidcClient.eventNames.loadingTimeout_error||e===t.OidcClient.eventNames.loginAsync_begin||e===t.OidcClient.eventNames.loginCallbackAsync_end||e===t.OidcClient.eventNames.loginAsync_error||e===t.OidcClient.eventNames.loginCallbackAsync_error||e===t.OidcClient.eventNames.service_worker_not_supported_by_browser&&i.service_worker_only===!0)&&A({name:e,data:n})});return queueMicrotask(()=>{B(a)}),()=>{w(a).removeEventSubscription(e),A(M),R(!0)}},[i,a]),(0,n.useEffect)(()=>{let e=i?.loading_timeout_ms??F;if(e<=0||!L||w(a)?.tokens!=null||!(k.name===``||k.name===t.OidcClient.eventNames.loginAsync_begin))return;let n=setTimeout(()=>{w(a).publishEvent(t.OidcClient.eventNames.loadingTimeout_error,{timeoutMs:e})},e);return()=>clearTimeout(n)},[k.name,L,a,i]);let V=f,H=s,U=c,W=l,G=u,K=d,q=z!==a,J=w(a);switch(k.name){case t.OidcClient.eventNames.service_worker_not_supported_by_browser:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(G,{configurationName:a})});case t.OidcClient.eventNames.loginAsync_begin:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(H,{configurationName:a})});case t.OidcClient.eventNames.loadingTimeout_error:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(W,{configurationName:a})});case t.OidcClient.eventNames.loginAsync_error:case t.OidcClient.eventNames.loginCallbackAsync_error:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(K,{configurationName:a})});case t.OidcClient.eventNames.refreshTokensAsync_error:case t.OidcClient.eventNames.syncTokensAsync_error:case t.OidcClient.eventNames.logout_from_another_tab:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(V,{configurationName:a})});default:return(0,r.jsx)(P,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(j,{redirect_uri:J.configuration.redirect_uri,silent_redirect_uri:J.configuration.silent_redirect_uri,silent_login_uri:J.configuration.silent_login_uri,callbackSuccessComponent:o,callbackErrorComponent:d,authenticatingComponent:s,configurationName:a,withCustomHistory:v,navigateAfterCallback:y,location:C??new t.OidcLocation,children:(0,r.jsx)(N,{loadingComponent:U,configurationName:a,children:e})})})}},R=({children:e,callbackPath:i=null,extras:a=null,configurationName:o=`default`})=>{let s=t.OidcClient.getOrThrow,c=s(o);return(0,n.useEffect)(()=>{!c.tokens&&!c.isLoggingOut&&c.loginAsync(i,a)},[o,i,a]),c.tokens?(0,r.jsx)(r.Fragment,{children:e}):null},z=(e,t=null,n=null,i=`default`)=>a=>(0,r.jsx)(R,{callbackPath:t,extras:n,configurationName:i,children:(0,r.jsx)(e,{...a})}),B=new Set,V=e=>{B.has(e)||(B.add(e),console.warn(`@axa-fr/react-oidc: no OIDC configuration found for "${e}". Make sure to wrap your component tree with <OidcProvider configurationName="${e}">. Hooks are returning safe default values (issue #1679).`))},H=`default`,U=(e,t)=>{let n=!1,r=e(t);return r&&(n=r.tokens!=null),n},W=(e=H)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>U(r,e));return(0,n.useEffect)(()=>{let n=!0,i=r(e);if(!i){V(e);return}let o=i.subscribeEvents((i,o)=>{(i===t.OidcClient.eventNames.logout_from_another_tab||i===t.OidcClient.eventNames.logout_from_same_tab||i===t.OidcClient.eventNames.token_acquired)&&n&&a(U(r,e))});return()=>{n=!1,i.removeEventSubscription(o)}},[e]),{login:(t=void 0,n=void 0,i=!1,a=void 0)=>{let o=r(e);return o?o.loginAsync(t,n,!1,a,i):(V(e),Promise.resolve())},logout:(t=void 0,n=void 0)=>{let i=r(e);return i?i.logoutAsync(t,n):(V(e),Promise.resolve())},renewTokens:async(t=void 0)=>{let n=r(e);if(!n)return V(e),{accessToken:null,accessTokenPayload:null,idToken:null,idTokenPayload:null};let i=await n.renewTokensAsync(t);return{accessToken:i.accessToken,accessTokenPayload:i.accessTokenPayload,idToken:i.idToken,idTokenPayload:i.idTokenPayload}},isAuthenticated:i}},G={accessToken:null,accessTokenPayload:null},K=e=>{let n=t.OidcClient.get,r=n(e);if(!r)return G;if(r.tokens){let e=r.tokens;return{accessToken:e.accessToken,accessTokenPayload:e.accessTokenPayload,generateDemonstrationOfProofOfPossessionAsync:r.configuration.demonstrating_proof_of_possession?(t,n)=>r.generateDemonstrationOfProofOfPossessionAsync(e.accessToken,t,n):null}}return G};function q(e,t){return e.configuration.demonstrating_proof_of_possession?(n,r,i={})=>e.generateDemonstrationOfProofOfPossessionAsync(t.accessToken,n,r,i):null}var J=(e=H)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>K(e));return(0,n.useEffect)(()=>{let n=!0,i=r(e);if(!i){V(e);return}let o=i.subscribeEvents((e,r)=>{if((e===t.OidcClient.eventNames.token_renewed||e===t.OidcClient.eventNames.token_acquired||e===t.OidcClient.eventNames.logout_from_another_tab||e===t.OidcClient.eventNames.logout_from_same_tab||e===t.OidcClient.eventNames.refreshTokensAsync_error||e===t.OidcClient.eventNames.syncTokensAsync_error)&&n){let e=i.tokens;a(e==null?G:{accessToken:e.accessToken,accessTokenPayload:e.accessTokenPayload,generateDemonstrationOfProofOfPossessionAsync:q(i,e)})}});return()=>{n=!1,i.removeEventSubscription(o)}},[e]),i},Y={idToken:null,idTokenPayload:null},X=e=>{let n=t.OidcClient.get,r=n(e);if(!r)return Y;if(r.tokens){let e=r.tokens;return{idToken:e.idToken,idTokenPayload:e.idTokenPayload}}return Y},Z=(e=H)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>X(e));return(0,n.useEffect)(()=>{let n=!0,i=r(e);if(!i){V(e);return}let o=i.subscribeEvents((e,r)=>{if((e===t.OidcClient.eventNames.token_renewed||e===t.OidcClient.eventNames.token_acquired||e===t.OidcClient.eventNames.logout_from_another_tab||e===t.OidcClient.eventNames.logout_from_same_tab||e===t.OidcClient.eventNames.refreshTokensAsync_error||e===t.OidcClient.eventNames.syncTokensAsync_error)&&n){let e=i.tokens;a(e==null?Y:{idToken:e.idToken,idTokenPayload:e.idTokenPayload})}});return()=>{n=!1,i.removeEventSubscription(o)}},[e]),i},Q=function(e){return e.Unauthenticated=`Unauthenticated`,e.Loading=`Loading user`,e.Loaded=`User loaded`,e.LoadingError=`Error loading user`,e}({});Object.defineProperty(e,`OidcClient`,{enumerable:!0,get:function(){return t.OidcClient}}),Object.defineProperty(e,`OidcError`,{enumerable:!0,get:function(){return t.OidcError}}),Object.defineProperty(e,`OidcErrorCode`,{enumerable:!0,get:function(){return t.OidcErrorCode}}),Object.defineProperty(e,`OidcLocation`,{enumerable:!0,get:function(){return t.OidcLocation}}),e.OidcProvider=L,e.OidcSecure=R,Object.defineProperty(e,`OidcStateError`,{enumerable:!0,get:function(){return t.OidcStateError}}),Object.defineProperty(e,`OidcStateErrorCode`,{enumerable:!0,get:function(){return t.OidcStateErrorCode}}),e.OidcUserStatus=Q,Object.defineProperty(e,`PushedAuthorizationRequestError`,{enumerable:!0,get:function(){return t.PushedAuthorizationRequestError}}),Object.defineProperty(e,`PushedAuthorizationRequestErrorCode`,{enumerable:!0,get:function(){return t.PushedAuthorizationRequestErrorCode}}),Object.defineProperty(e,`TokenAutomaticRenewMode`,{enumerable:!0,get:function(){return t.TokenAutomaticRenewMode}}),Object.defineProperty(e,`TokenRenewMode`,{enumerable:!0,get:function(){return t.TokenRenewMode}}),Object.defineProperty(e,`isOidcError`,{enumerable:!0,get:function(){return t.isOidcError}}),Object.defineProperty(e,`isOidcStateError`,{enumerable:!0,get:function(){return t.isOidcStateError}}),Object.defineProperty(e,`isPushedAuthorizationRequestError`,{enumerable:!0,get:function(){return t.isPushedAuthorizationRequestError}}),e.useOidc=W,e.useOidcAccessToken=J,e.useOidcFetch=m,e.useOidcIdToken=Z,e.useOidcUser=(e=`default`,r=!1)=>{let i=t.OidcClient.get(e),a=i?i.userInfo():null,[o,s]=(0,n.useState)({user:a,status:a?`User loaded`:`Unauthenticated`}),[c,l]=(0,n.useState)(+!!a),u=(0,n.useRef)(+!!a);return(0,n.useEffect)(()=>{let n=t.OidcClient.get(e),i=!0;if(!n){V(e);return}if(n.tokens){let e=c===u.current;if(e&&n.userInfo())return;u.current=c,queueMicrotask(()=>{i&&s({...o,status:`Loading user`})}),n.userInfoAsync(!e,r).then(e=>{i&&s({user:e,status:`User loaded`})}).catch(()=>s({...o,status:`Error loading user`}))}else queueMicrotask(()=>{i&&s({user:null,status:`Unauthenticated`})});let a=n.subscribeEvents(e=>{(e===t.OidcClient.eventNames.logout_from_another_tab||e===t.OidcClient.eventNames.logout_from_same_tab)&&i&&s({user:null,status:`Unauthenticated`})});return()=>{i=!1,n.removeEventSubscription(a)}},[c,e,r]),{oidcUser:o.user,oidcUserLoadingState:o.status,reloadOidcUser:()=>{l(c+1)}}},e.withOidcFetch=p,e.withOidcSecure=z});
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@axa-fr/oidc-client"),require("react"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`@axa-fr/oidc-client`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e[`react-oidc`]={},e._axa_fr_oidc_client,e.React,e.react_jsx_runtime))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var i=Object.create,a=Object.defineProperty,o=Object.getOwnPropertyDescriptor,s=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,u=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=s(t),c=0,u=i.length,d;c<u;c++)d=i[c],!l.call(e,d)&&d!==n&&a(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=o(t,d))||r.enumerable});return e};n=((e,t,n)=>(n=e==null?{}:i(c(e)),u(t||!e||!e.__esModule||!l.call(e,`default`)?a(n,`default`,{value:e,enumerable:!0}):n,e)))(n,1);var d=`default`,f=(e=null,t=d,n=!1)=>i=>a=>{let{fetch:o}=p(e||a.fetch,t,n);return(0,r.jsx)(i,{...a,fetch:o})},p=(e=null,r=d,i=!1)=>{let a=e||window.fetch,o=t.OidcClient.getOrThrow;return{fetch:(0,n.useCallback)(async(e,t)=>await o(r).fetchWithTokens(a,i)(e,t),[a,r,i])}},m=()=>(0,r.jsx)(`div`,{className:`oidc-authenticating`,children:(0,r.jsxs)(`div`,{className:`oidc-authenticating__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-authenticating__title`,children:`Error authentication`}),(0,r.jsx)(`p`,{className:`oidc-authenticating__content`,children:`An error occurred during authentication.`})]})}),h=()=>(0,r.jsx)(`div`,{className:`oidc-authenticating`,children:(0,r.jsxs)(`div`,{className:`oidc-authenticating__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-authenticating__title`,children:`Authentication in progress`}),(0,r.jsx)(`p`,{className:`oidc-authenticating__content`,children:`You will be redirected to the login page.`})]})}),g=()=>Math.random().toString(36).slice(2,8),_=(e,t)=>(n,r)=>{if(typeof e.CustomEvent==`function`)return new e.CustomEvent(n,r);let i=r||{bubbles:!1,cancelable:!1,detail:void 0},a=t.createEvent(`CustomEvent`);return a.initCustomEvent(n,i.bubbles,i.cancelable,i.detail),a.prototype=e.Event.prototype,a},v=(e,t,n)=>({replaceState:(r,i)=>{let a=n(),o=i||e.history.state;e.history.replaceState({key:a,state:o},null,r),e.dispatchEvent(t(`popstate`))}}),y=()=>v(window,_(window,document),g),b=()=>(0,r.jsx)(`div`,{className:`oidc-callback`,children:(0,r.jsxs)(`div`,{className:`oidc-callback__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-callback__title`,children:`Authentication complete`}),(0,r.jsx)(`p`,{className:`oidc-callback__content`,children:`You will be redirected to your application.`})]})}),x=200,S=(e,t=window)=>t.location.pathname===e||e===`/`,C=({callBackError:e,callBackSuccess:i,configurationName:a,withCustomHistory:o,navigateAfterCallback:s})=>{let[c,l]=(0,n.useState)(!1);(0,n.useEffect)(()=>{let e=!0;return(async()=>{let n=t.OidcClient.getOrThrow;try{let r=n(a),{callbackPath:i}=await r.loginCallbackAsync(),c=i||`/`;if(s)try{await s(c),r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigated,{configurationName:a,callbackPath:c})}catch(n){r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigation_error,{configurationName:a,callbackPath:c,error:n}),e&&(console.warn(n),l(!0))}else(o?o():y()).replaceState(c),await new Promise(e=>{setTimeout(()=>{e()},x)}),e&&(S(c)?r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigated,{configurationName:a,callbackPath:c}):(r.publishEvent(t.OidcClient.eventNames.loginCallbackAsync_navigation_error,{configurationName:a,callbackPath:c,error:Error(`Navigation did not commit: expected "${c}" but found "${window.location.pathname}"`)}),l(!0)))}catch(t){e&&(console.warn(t),l(!0))}})(),()=>{e=!1}},[]);let u=e||m,d=i||b;return c?(0,r.jsx)(u,{configurationName:a}):(0,r.jsx)(d,{configurationName:a})},w=()=>(0,r.jsx)(`span`,{className:`oidc-loading`,children:`Loading`}),T=()=>(0,r.jsx)(`div`,{className:`oidc-loading-timeout`,children:(0,r.jsxs)(`div`,{className:`oidc-loading-timeout__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-loading-timeout__title`,children:`Loading timeout`}),(0,r.jsx)(`p`,{className:`oidc-loading-timeout__content`,children:`Authentication is taking longer than expected. Please try refreshing the page.`})]})}),E=()=>(0,r.jsx)(`div`,{className:`oidc-serviceworker`,children:(0,r.jsxs)(`div`,{className:`oidc-serviceworker__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-serviceworker__title`,children:`Unable to authenticate on this browser`}),(0,r.jsx)(`p`,{className:`oidc-serviceworker__content`,children:`Your browser is not secure enough to make authentication work. Try updating your browser or use a newer browser.`})]})}),D=()=>(0,r.jsx)(`div`,{className:`oidc-session-lost`,children:(0,r.jsxs)(`div`,{className:`oidc-session-lost__container`,children:[(0,r.jsx)(`h1`,{className:`oidc-session-lost__title`,children:`Session timed out`}),(0,r.jsx)(`p`,{className:`oidc-session-lost__content`,children:`Your session has expired. Please re-authenticate.`})]})}),O=({configurationName:e})=>((0,n.useEffect)(()=>{(async()=>{t.OidcClient.getOrThrow(e).silentLoginCallbackAsync()})().catch(e=>{console.error(`Error during silent login callback:`,e)})},[e]),null),k=({configurationName:e})=>{let i=(0,t.getParseQueryStringFromLocation)(window.location.href),a=t.OidcClient.getOrThrow,o=a(e),s=null;for(let[e,t]of Object.entries(i))e!==`state`&&e!==`scope`&&(s??={},s[e]=t);return(0,n.useEffect)(()=>{o.tokens||o.loginAsync(null,s,!0,i.scope)},[]),(0,r.jsx)(r.Fragment,{})},A=n.default.memo(({callbackErrorComponent:e,callbackSuccessComponent:i,redirect_uri:a,silent_redirect_uri:o,silent_login_uri:s,children:c,configurationName:l,withCustomHistory:u=null,navigateAfterCallback:d=null})=>{let f=window?(0,t.getPath)(window.location.href):``,[p,m]=(0,n.useState)(f);(0,n.useEffect)(()=>{let e=()=>m((0,t.getPath)(window.location.href));return e(),window.addEventListener(`popstate`,e,!1),()=>window.removeEventListener(`popstate`,e,!1)},[]);let h=(0,t.getPath)(a);return o&&p===(0,t.getPath)(o)?(0,r.jsx)(O,{configurationName:l}):s&&p===(0,t.getPath)(s)?(0,r.jsx)(k,{configurationName:l}):p===h?(0,r.jsx)(C,{callBackError:e,callBackSuccess:i,configurationName:l,withCustomHistory:u,navigateAfterCallback:d}):(0,r.jsx)(r.Fragment,{children:c})}),j={name:``,data:null},M=({loadingComponent:e,children:i,configurationName:a})=>{let[o,s]=(0,n.useState)(!0),c=t.OidcClient.get,l=c(a);return(0,n.useEffect)(()=>{let e=!0;return l&&l.tryKeepExistingSessionAsync().then(()=>{e&&s(!1)}),()=>{e=!1}},[a]),(0,r.jsx)(r.Fragment,{children:o?(0,r.jsx)(e,{configurationName:a}):(0,r.jsx)(r.Fragment,{children:i})})},N=({isLoading:e,loadingComponent:t,children:n,configurationName:i})=>{let a=t;return e?(0,r.jsx)(a,{configurationName:i,children:n}):(0,r.jsx)(r.Fragment,{children:n})},P=3e4,F=e=>e===t.OidcClient.eventNames.token_acquired||e===t.OidcClient.eventNames.token_renewed||e===t.OidcClient.eventNames.loginCallbackAsync_end||e===t.OidcClient.eventNames.tryKeepExistingSessionAsync_end||e===t.OidcClient.eventNames.tryKeepExistingSessionAsync_error,I=({children:e,configuration:i,configurationName:a=`default`,callbackSuccessComponent:o=b,authenticatingComponent:s=h,loadingComponent:c=w,loadingTimeoutComponent:l=T,serviceWorkerNotSupportedComponent:u=E,authenticatingErrorComponent:d=m,sessionLostComponent:f=D,onSessionLost:p=null,onLogoutFromAnotherTab:g=null,onLogoutFromSameTab:_=null,withCustomHistory:v=null,navigateAfterCallback:y=null,onEvent:x=null,getFetch:S=null,location:C=null})=>{if(i&&i.redirect_uri&&i.silent_redirect_uri&&i.redirect_uri===i.silent_redirect_uri)throw Error(`redirect_uri and silent_redirect_uri must be different`);let O=(e=`default`)=>t.OidcClient.getOrCreate(S??t.getFetchDefault,C??new t.OidcLocation)(i,e),[k,I]=(0,n.useState)(j),[L,R]=(0,n.useState)(!0),[z,B]=(0,n.useState)(a);(0,n.useEffect)(()=>{let e=O(a).subscribeEvents((e,t)=>{x&&x(a,e,t)});return()=>{O(a).removeEventSubscription(e)}},[a,x]),(0,n.useEffect)(()=>{let e=O(a).subscribeEvents((e,n)=>{if(e===t.OidcClient.eventNames.loginAsync_begin?R(!0):F(e)&&R(!1),e===t.OidcClient.eventNames.refreshTokensAsync_error||e===t.OidcClient.eventNames.syncTokensAsync_error){if(p!=null){p();return}I({name:e,data:n})}else if(e===t.OidcClient.eventNames.logout_from_another_tab){if(g!=null){g();return}I({name:e,data:n})}else e===t.OidcClient.eventNames.logout_from_same_tab?_?.():(e===t.OidcClient.eventNames.loadingTimeout_error||e===t.OidcClient.eventNames.loginAsync_begin||e===t.OidcClient.eventNames.loginCallbackAsync_end||e===t.OidcClient.eventNames.loginAsync_error||e===t.OidcClient.eventNames.loginCallbackAsync_error||e===t.OidcClient.eventNames.service_worker_not_supported_by_browser&&i.service_worker_only===!0)&&I({name:e,data:n})});return queueMicrotask(()=>{B(a)}),()=>{O(a).removeEventSubscription(e),I(j),R(!0)}},[i,a]),(0,n.useEffect)(()=>{let e=i?.loading_timeout_ms??P;if(e<=0||!L||O(a)?.tokens!=null||k.name!==``&&k.name!==t.OidcClient.eventNames.loginAsync_begin)return;let n=setTimeout(()=>{O(a).publishEvent(t.OidcClient.eventNames.loadingTimeout_error,{timeoutMs:e})},e);return()=>clearTimeout(n)},[k.name,L,a,i]);let V=f,H=s,U=c,W=l,G=u,K=d,q=z!==a,J=O(a);switch(k.name){case t.OidcClient.eventNames.service_worker_not_supported_by_browser:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(G,{configurationName:a})});case t.OidcClient.eventNames.loginAsync_begin:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(H,{configurationName:a})});case t.OidcClient.eventNames.loadingTimeout_error:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(W,{configurationName:a})});case t.OidcClient.eventNames.loginAsync_error:case t.OidcClient.eventNames.loginCallbackAsync_error:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(K,{configurationName:a})});case t.OidcClient.eventNames.refreshTokensAsync_error:case t.OidcClient.eventNames.syncTokensAsync_error:case t.OidcClient.eventNames.logout_from_another_tab:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(V,{configurationName:a})});default:return(0,r.jsx)(N,{loadingComponent:U,isLoading:q,configurationName:a,children:(0,r.jsx)(A,{redirect_uri:J.configuration.redirect_uri,silent_redirect_uri:J.configuration.silent_redirect_uri,silent_login_uri:J.configuration.silent_login_uri,callbackSuccessComponent:o,callbackErrorComponent:d,authenticatingComponent:s,configurationName:a,withCustomHistory:v,navigateAfterCallback:y,location:C??new t.OidcLocation,children:(0,r.jsx)(M,{loadingComponent:U,configurationName:a,children:e})})})}},L=({children:e,callbackPath:i=null,extras:a=null,configurationName:o=`default`})=>{let s=t.OidcClient.getOrThrow,c=s(o);return(0,n.useEffect)(()=>{!c.tokens&&!c.isLoggingOut&&c.loginAsync(i,a)},[o,i,a]),c.tokens?(0,r.jsx)(r.Fragment,{children:e}):null},R=(e,t=null,n=null,i=`default`)=>a=>(0,r.jsx)(L,{callbackPath:t,extras:n,configurationName:i,children:(0,r.jsx)(e,{...a})}),z=new Set,B=e=>{z.has(e)||(z.add(e),console.warn(`@axa-fr/react-oidc: no OIDC configuration found for "${e}". Make sure to wrap your component tree with <OidcProvider configurationName="${e}">. Hooks are returning safe default values (issue #1679).`))},V=`default`,H=(e,t)=>{let n=e(t);return n?n.tokens!=null:!1},U=e=>e===t.OidcClient.eventNames.token_renewed||e===t.OidcClient.eventNames.token_acquired||e===t.OidcClient.eventNames.logout_from_another_tab||e===t.OidcClient.eventNames.logout_from_same_tab||e===t.OidcClient.eventNames.refreshTokensAsync_error||e===t.OidcClient.eventNames.syncTokensAsync_error,W=(e=V)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>H(r,e));return(0,n.useEffect)(()=>{let n=!0,i=r(e);if(!i){B(e);return}let o=i.subscribeEvents((i,o)=>{(i===t.OidcClient.eventNames.logout_from_another_tab||i===t.OidcClient.eventNames.logout_from_same_tab||i===t.OidcClient.eventNames.token_acquired)&&n&&a(H(r,e))});return()=>{n=!1,i.removeEventSubscription(o)}},[e]),{login:(t=void 0,n=void 0,i=!1,a=void 0)=>{let o=r(e);return o?o.loginAsync(t,n,!1,a,i):(B(e),Promise.resolve())},logout:(t=void 0,n=void 0)=>{let i=r(e);return i?i.logoutAsync(t,n):(B(e),Promise.resolve())},renewTokens:async(t=void 0)=>{let n=r(e);if(!n)return B(e),{accessToken:null,accessTokenPayload:null,idToken:null,idTokenPayload:null};let i=await n.renewTokensAsync(t);return{accessToken:i.accessToken,accessTokenPayload:i.accessTokenPayload,idToken:i.idToken,idTokenPayload:i.idTokenPayload}},isAuthenticated:i}},G={accessToken:null,accessTokenPayload:null},K=e=>{let n=t.OidcClient.get,r=n(e);if(!r)return G;if(r.tokens){let e=r.tokens;return{accessToken:e.accessToken,accessTokenPayload:e.accessTokenPayload,generateDemonstrationOfProofOfPossessionAsync:r.configuration.demonstrating_proof_of_possession?(t,n)=>r.generateDemonstrationOfProofOfPossessionAsync(e.accessToken,t,n):null}}return G};function q(e,t){return e.configuration.demonstrating_proof_of_possession?(n,r,i={})=>e.generateDemonstrationOfProofOfPossessionAsync(t.accessToken,n,r,i):null}var J=(e=V)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>K(e));return(0,n.useEffect)(()=>{let t=!0,n=r(e);if(!n){B(e);return}let i=n.subscribeEvents((e,r)=>{if(U(e)&&t){let e=n.tokens;a(e==null?G:{accessToken:e.accessToken,accessTokenPayload:e.accessTokenPayload,generateDemonstrationOfProofOfPossessionAsync:q(n,e)})}});return()=>{t=!1,n.removeEventSubscription(i)}},[e]),i},Y={idToken:null,idTokenPayload:null},X=e=>{let n=t.OidcClient.get,r=n(e);if(!r)return Y;if(r.tokens){let e=r.tokens;return{idToken:e.idToken,idTokenPayload:e.idTokenPayload}}return Y},Z=(e=V)=>{let r=t.OidcClient.get,[i,a]=(0,n.useState)(()=>X(e));return(0,n.useEffect)(()=>{let t=!0,n=r(e);if(!n){B(e);return}let i=n.subscribeEvents((e,r)=>{if(U(e)&&t){let e=n.tokens;a(e==null?Y:{idToken:e.idToken,idTokenPayload:e.idTokenPayload})}});return()=>{t=!1,n.removeEventSubscription(i)}},[e]),i},Q=function(e){return e.Unauthenticated=`Unauthenticated`,e.Loading=`Loading user`,e.Loaded=`User loaded`,e.LoadingError=`Error loading user`,e}({});Object.defineProperty(e,"OidcClient",{enumerable:!0,get:function(){return t.OidcClient}}),Object.defineProperty(e,"OidcError",{enumerable:!0,get:function(){return t.OidcError}}),Object.defineProperty(e,"OidcErrorCode",{enumerable:!0,get:function(){return t.OidcErrorCode}}),Object.defineProperty(e,"OidcLocation",{enumerable:!0,get:function(){return t.OidcLocation}}),e.OidcProvider=I,e.OidcSecure=L,Object.defineProperty(e,"OidcStateError",{enumerable:!0,get:function(){return t.OidcStateError}}),Object.defineProperty(e,"OidcStateErrorCode",{enumerable:!0,get:function(){return t.OidcStateErrorCode}}),e.OidcUserStatus=Q,Object.defineProperty(e,"PushedAuthorizationRequestError",{enumerable:!0,get:function(){return t.PushedAuthorizationRequestError}}),Object.defineProperty(e,"PushedAuthorizationRequestErrorCode",{enumerable:!0,get:function(){return t.PushedAuthorizationRequestErrorCode}}),Object.defineProperty(e,"TokenAutomaticRenewMode",{enumerable:!0,get:function(){return t.TokenAutomaticRenewMode}}),Object.defineProperty(e,"TokenRenewMode",{enumerable:!0,get:function(){return t.TokenRenewMode}}),Object.defineProperty(e,"isOidcError",{enumerable:!0,get:function(){return t.isOidcError}}),Object.defineProperty(e,"isOidcStateError",{enumerable:!0,get:function(){return t.isOidcStateError}}),Object.defineProperty(e,"isPushedAuthorizationRequestError",{enumerable:!0,get:function(){return t.isPushedAuthorizationRequestError}}),e.useOidc=W,e.useOidcAccessToken=J,e.useOidcFetch=p,e.useOidcIdToken=Z,e.useOidcUser=(e=`default`,r=!1)=>{let i=t.OidcClient.get(e),a=i?i.userInfo():null,[o,s]=(0,n.useState)({user:a,status:a?`User loaded`:`Unauthenticated`}),[c,l]=(0,n.useState)(+!!a),u=(0,n.useRef)(+!!a);return(0,n.useEffect)(()=>{let n=t.OidcClient.get(e),i=!0;if(!n){B(e);return}if(n.tokens){let e=c===u.current;if(e&&n.userInfo())return;u.current=c,queueMicrotask(()=>{i&&s({...o,status:`Loading user`})}),n.userInfoAsync(!e,r).then(e=>{i&&s({user:e,status:`User loaded`})}).catch(()=>s({...o,status:`Error loading user`}))}else queueMicrotask(()=>{i&&s({user:null,status:`Unauthenticated`})});let a=n.subscribeEvents(e=>{(e===t.OidcClient.eventNames.logout_from_another_tab||e===t.OidcClient.eventNames.logout_from_same_tab)&&i&&s({user:null,status:`Unauthenticated`})});return()=>{i=!1,n.removeEventSubscription(a)}},[c,e,r]),{oidcUser:o.user,oidcUserLoadingState:o.status,reloadOidcUser:()=>{l(c+1)}}},e.withOidcFetch=f,e.withOidcSecure=R});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axa-fr/react-oidc",
3
- "version": "7.29.3",
3
+ "version": "7.29.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.cjs",
@@ -39,31 +39,31 @@
39
39
  "lint": "eslint src"
40
40
  },
41
41
  "dependencies": {
42
- "@axa-fr/oidc-client": "7.29.3",
43
- "@axa-fr/oidc-client-service-worker": "7.29.3"
42
+ "@axa-fr/oidc-client": "7.29.5",
43
+ "@axa-fr/oidc-client-service-worker": "7.29.5"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@testing-library/jest-dom": "6.9.1",
50
- "@testing-library/react": "16.3.2",
51
- "@testing-library/user-event": "14.6.1",
52
- "@types/react": "19.2.14",
53
- "@vitejs/plugin-react": "6.0.2",
54
- "@vitest/coverage-v8": "4.1.6",
55
- "cpy": "13.2.2",
49
+ "@testing-library/jest-dom": "7.0.1",
50
+ "@testing-library/react": "16.3.3",
51
+ "@testing-library/user-event": "14.6.7",
52
+ "@types/react": "19.3.0",
53
+ "@vitejs/plugin-react": "6.1.1",
54
+ "@vitest/coverage-v8": "5.0.0",
55
+ "cpy": "13.2.3",
56
56
  "cpy-cli": "^7.0.0",
57
57
  "cross-env": "^10.1.0",
58
- "jsdom": "29.1.1",
59
- "msw": "2.14.6",
60
- "react": "^19.2.6",
61
- "react-dom": "^19.2.6",
58
+ "jsdom": "30.0.1",
59
+ "msw": "2.15.0",
60
+ "react": "^19.3.0",
61
+ "react-dom": "^19.3.0",
62
62
  "rimraf": "6.1.3",
63
- "typescript": "6.0.3",
64
- "vite": "8.0.13",
65
- "vite-plugin-dts": "5.0.0",
66
- "vitest": "4.1.6"
63
+ "typescript": "npm:@typescript/typescript6@6.0.2",
64
+ "vite": "8.3.0",
65
+ "vite-plugin-dts": "5.1.0",
66
+ "vitest": "5.0.0"
67
67
  },
68
68
  "license": "MIT",
69
69
  "publishConfig": {
@@ -0,0 +1,135 @@
1
+ import type { Fetch } from '@axa-fr/oidc-client';
2
+ import { renderHook } from '@testing-library/react';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+
5
+ import { useOidcFetch } from './FetchToken';
6
+
7
+ const { getOrThrow, fetchWithTokens } = vi.hoisted(() => ({
8
+ getOrThrow: vi.fn(),
9
+ fetchWithTokens: vi.fn(),
10
+ }));
11
+
12
+ vi.mock('@axa-fr/oidc-client', () => ({
13
+ OidcClient: { getOrThrow },
14
+ }));
15
+
16
+ describe('useOidcFetch', () => {
17
+ const originalFetch = vi.fn<Fetch>();
18
+ const authenticatedFetch = vi.fn<Fetch>();
19
+ const response = new Response('response');
20
+
21
+ beforeEach(() => {
22
+ vi.resetAllMocks();
23
+ getOrThrow.mockReturnValue({ fetchWithTokens });
24
+ fetchWithTokens.mockReturnValue(authenticatedFetch);
25
+ authenticatedFetch.mockResolvedValue(response);
26
+ });
27
+
28
+ afterEach(() => {
29
+ vi.unstubAllGlobals();
30
+ });
31
+
32
+ it('looks up the default configuration only when a request is made', async () => {
33
+ const { result } = renderHook(() => useOidcFetch(originalFetch));
34
+
35
+ expect(getOrThrow).not.toHaveBeenCalled();
36
+ await expect(result.current.fetch('/resource')).resolves.toBe(response);
37
+ expect(getOrThrow).toHaveBeenCalledExactlyOnceWith('default');
38
+ expect(fetchWithTokens).toHaveBeenCalledExactlyOnceWith(originalFetch, false);
39
+ expect(authenticatedFetch).toHaveBeenCalledExactlyOnceWith('/resource', undefined);
40
+ });
41
+
42
+ it('defaults to the browser fetch implementation', async () => {
43
+ vi.stubGlobal('fetch', originalFetch);
44
+ const { result } = renderHook(() => useOidcFetch());
45
+
46
+ await result.current.fetch('/resource');
47
+
48
+ expect(fetchWithTokens).toHaveBeenCalledExactlyOnceWith(originalFetch, false);
49
+ });
50
+
51
+ it.each([new URL('https://example.com/resource'), new Request('https://example.com/resource')])(
52
+ 'forwards request objects and options unchanged: %s',
53
+ async input => {
54
+ const init: RequestInit = { method: 'POST', headers: { 'X-Custom': 'value' }, body: 'body' };
55
+ const { result } = renderHook(() => useOidcFetch(originalFetch, 'custom', true));
56
+
57
+ await expect(result.current.fetch(input, init)).resolves.toBe(response);
58
+
59
+ expect(getOrThrow).toHaveBeenCalledWith('custom');
60
+ expect(fetchWithTokens).toHaveBeenCalledWith(originalFetch, true);
61
+ expect(authenticatedFetch.mock.calls[0][0]).toBe(input);
62
+ expect(authenticatedFetch.mock.calls[0][1]).toBe(init);
63
+ },
64
+ );
65
+
66
+ it('uses the current client for each request rather than caching it', async () => {
67
+ const nextFetchWithTokens = vi.fn().mockReturnValue(authenticatedFetch);
68
+ const { result } = renderHook(() => useOidcFetch(originalFetch));
69
+ await result.current.fetch('/first');
70
+
71
+ getOrThrow.mockReturnValue({ fetchWithTokens: nextFetchWithTokens });
72
+ await result.current.fetch('/second');
73
+
74
+ expect(getOrThrow).toHaveBeenCalledTimes(2);
75
+ expect(fetchWithTokens).toHaveBeenCalledTimes(1);
76
+ expect(nextFetchWithTokens).toHaveBeenCalledExactlyOnceWith(originalFetch, false);
77
+ });
78
+
79
+ it('keeps the callback stable when its dependencies are unchanged', () => {
80
+ const { result, rerender } = renderHook(() => useOidcFetch(originalFetch));
81
+ const callback = result.current.fetch;
82
+
83
+ rerender();
84
+
85
+ expect(result.current.fetch).toBe(callback);
86
+ });
87
+
88
+ it.each([
89
+ { fetch: vi.fn<Fetch>(), configurationName: 'default', isDpop: false },
90
+ { fetch: originalFetch, configurationName: 'custom', isDpop: false },
91
+ { fetch: originalFetch, configurationName: 'default', isDpop: true },
92
+ ])('updates the callback when a dependency changes: %s', async nextProps => {
93
+ const { result, rerender } = renderHook(
94
+ ({ fetch, configurationName, isDpop }) => useOidcFetch(fetch, configurationName, isDpop),
95
+ { initialProps: { fetch: originalFetch, configurationName: 'default', isDpop: false } },
96
+ );
97
+ const callback = result.current.fetch;
98
+
99
+ rerender(nextProps);
100
+ expect(result.current.fetch).not.toBe(callback);
101
+ await result.current.fetch('/resource');
102
+
103
+ expect(getOrThrow).toHaveBeenCalledWith(nextProps.configurationName);
104
+ expect(fetchWithTokens).toHaveBeenCalledWith(nextProps.fetch, nextProps.isDpop);
105
+ });
106
+
107
+ it('rejects the request rather than throwing synchronously when configuration is missing', async () => {
108
+ const error = new Error('Missing configuration');
109
+ getOrThrow.mockImplementation(() => {
110
+ throw error;
111
+ });
112
+ const { result } = renderHook(() => useOidcFetch(originalFetch));
113
+
114
+ await expect(result.current.fetch('/resource')).rejects.toBe(error);
115
+ expect(fetchWithTokens).not.toHaveBeenCalled();
116
+ });
117
+
118
+ it('preserves errors thrown while constructing the authenticated fetch', async () => {
119
+ const error = new Error('Cannot construct fetch');
120
+ fetchWithTokens.mockImplementation(() => {
121
+ throw error;
122
+ });
123
+ const { result } = renderHook(() => useOidcFetch(originalFetch));
124
+
125
+ await expect(result.current.fetch('/resource')).rejects.toBe(error);
126
+ });
127
+
128
+ it('preserves rejected fetch errors', async () => {
129
+ const error = new Error('Network failure');
130
+ authenticatedFetch.mockRejectedValue(error);
131
+ const { result } = renderHook(() => useOidcFetch(originalFetch));
132
+
133
+ await expect(result.current.fetch('/resource')).rejects.toBe(error);
134
+ });
135
+ });
@@ -7,18 +7,6 @@ export interface ComponentWithOidcFetchProps {
7
7
 
8
8
  const defaultConfigurationName = 'default';
9
9
 
10
- const fetchWithToken =
11
- (
12
- fetch: Fetch,
13
- getOidcWithConfigurationName: () => OidcClient | null,
14
- demonstratingProofOfPossession: boolean = false,
15
- ) =>
16
- async (...params: Parameters<Fetch>) => {
17
- const oidc = getOidcWithConfigurationName();
18
- const newFetch = oidc.fetchWithTokens(fetch, demonstratingProofOfPossession);
19
- return await newFetch(...params);
20
- };
21
-
22
10
  export const withOidcFetch =
23
11
  (
24
12
  fetch: Fetch = null,
@@ -46,14 +34,13 @@ export const useOidcFetch = (
46
34
  const getOidc = OidcClient.getOrThrow;
47
35
 
48
36
  const memoizedFetchCallback = useCallback(
49
- (input: RequestInfo | URL, init?: RequestInit) => {
50
- const getOidcWithConfigurationName = () => getOidc(configurationName);
51
- const newFetch = fetchWithToken(
37
+ async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
38
+ const oidc = getOidc(configurationName);
39
+ const authenticatedFetch = oidc.fetchWithTokens(
52
40
  previousFetch,
53
- getOidcWithConfigurationName,
54
41
  demonstratingProofOfPossession,
55
42
  );
56
- return newFetch(input, init);
43
+ return await authenticatedFetch(input, init);
57
44
  },
58
45
  [previousFetch, configurationName, demonstratingProofOfPossession],
59
46
  );
@@ -0,0 +1,84 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { OidcSecure, withOidcSecure } from './OidcSecure';
5
+
6
+ const { getOrThrow, loginAsync } = vi.hoisted(() => ({
7
+ getOrThrow: vi.fn(),
8
+ loginAsync: vi.fn(),
9
+ }));
10
+
11
+ vi.mock('@axa-fr/oidc-client', () => ({ OidcClient: { getOrThrow } }));
12
+
13
+ describe('OidcSecure', () => {
14
+ beforeEach(() => {
15
+ vi.resetAllMocks();
16
+ });
17
+
18
+ it('renders children without logging in when tokens are present', () => {
19
+ getOrThrow.mockReturnValue({ tokens: {}, loginAsync });
20
+
21
+ render(<OidcSecure>Protected content</OidcSecure>);
22
+
23
+ expect(screen.getByText('Protected content')).toBeDefined();
24
+ expect(getOrThrow).toHaveBeenCalledWith('default');
25
+ expect(loginAsync).not.toHaveBeenCalled();
26
+ });
27
+
28
+ it('hides children and starts login with the supplied configuration and arguments', () => {
29
+ getOrThrow.mockReturnValue({ tokens: null, loginAsync });
30
+ const extras = { prompt: 'login' };
31
+
32
+ render(
33
+ <OidcSecure configurationName="custom" callbackPath="/private" extras={extras}>
34
+ Protected content
35
+ </OidcSecure>,
36
+ );
37
+
38
+ expect(screen.queryByText('Protected content')).toBeNull();
39
+ expect(getOrThrow).toHaveBeenCalledWith('custom');
40
+ expect(loginAsync).toHaveBeenCalledExactlyOnceWith('/private', extras);
41
+ });
42
+
43
+ it('does not restart login while logout is in progress', () => {
44
+ getOrThrow.mockReturnValue({ tokens: null, isLoggingOut: true, loginAsync });
45
+
46
+ render(<OidcSecure>Protected content</OidcSecure>);
47
+
48
+ expect(screen.queryByText('Protected content')).toBeNull();
49
+ expect(loginAsync).not.toHaveBeenCalled();
50
+ });
51
+
52
+ it('does not repeat login on an unchanged render', () => {
53
+ getOrThrow.mockReturnValue({ tokens: null, loginAsync });
54
+ const { rerender } = render(<OidcSecure>Protected content</OidcSecure>);
55
+
56
+ rerender(<OidcSecure>Protected content</OidcSecure>);
57
+
58
+ expect(loginAsync).toHaveBeenCalledExactlyOnceWith(null, null);
59
+ });
60
+
61
+ it('preserves the fail-fast behavior for a missing configuration', () => {
62
+ const error = new Error('Missing configuration');
63
+ getOrThrow.mockImplementation(() => {
64
+ throw error;
65
+ });
66
+
67
+ expect(() => render(<OidcSecure>Protected content</OidcSecure>)).toThrow(error);
68
+ });
69
+
70
+ it('passes props through the higher-order component', () => {
71
+ getOrThrow.mockReturnValue({ tokens: {}, loginAsync });
72
+ const Component = withOidcSecure(
73
+ ({ children }) => <span>{children}</span>,
74
+ '/callback',
75
+ null,
76
+ 'custom',
77
+ );
78
+
79
+ render(<Component>Wrapped content</Component>);
80
+
81
+ expect(screen.getByText('Wrapped content')).toBeDefined();
82
+ expect(getOrThrow).toHaveBeenCalledWith('custom');
83
+ });
84
+ });
@@ -0,0 +1,222 @@
1
+ import type { Tokens } from '@axa-fr/oidc-client';
2
+ import { act, renderHook } from '@testing-library/react';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+
5
+ import { useOidc, useOidcAccessToken, useOidcIdToken } from './ReactOidc';
6
+
7
+ const { get, subscribeEvents, removeEventSubscription, generateProof } = vi.hoisted(() => ({
8
+ get: vi.fn(),
9
+ subscribeEvents: vi.fn<(listener: (name: string) => void) => string>(),
10
+ removeEventSubscription: vi.fn(),
11
+ generateProof: vi.fn(),
12
+ }));
13
+
14
+ vi.mock('@axa-fr/oidc-client', () => ({
15
+ OidcClient: {
16
+ get,
17
+ eventNames: {
18
+ token_renewed: 'token_renewed',
19
+ token_acquired: 'token_acquired',
20
+ logout_from_another_tab: 'logout_from_another_tab',
21
+ logout_from_same_tab: 'logout_from_same_tab',
22
+ refreshTokensAsync_error: 'refreshTokensAsync_error',
23
+ syncTokensAsync_error: 'syncTokensAsync_error',
24
+ },
25
+ },
26
+ }));
27
+
28
+ const initialTokens = {
29
+ accessToken: 'access-example',
30
+ accessTokenPayload: { sub: 'user' },
31
+ idToken: 'id-example',
32
+ idTokenPayload: { sub: 'user' },
33
+ };
34
+
35
+ const client = {
36
+ tokens: null as Pick<
37
+ Tokens,
38
+ 'accessToken' | 'accessTokenPayload' | 'idToken' | 'idTokenPayload'
39
+ > | null,
40
+ configuration: { demonstrating_proof_of_possession: false },
41
+ subscribeEvents,
42
+ removeEventSubscription,
43
+ generateDemonstrationOfProofOfPossessionAsync: generateProof,
44
+ loginAsync: vi.fn(),
45
+ logoutAsync: vi.fn(),
46
+ renewTokensAsync: vi.fn(),
47
+ };
48
+
49
+ beforeEach(() => {
50
+ vi.resetAllMocks();
51
+ client.tokens = { ...initialTokens };
52
+ client.configuration.demonstrating_proof_of_possession = false;
53
+ get.mockReturnValue(client);
54
+ subscribeEvents.mockReturnValue('subscription');
55
+ });
56
+
57
+ describe.each([
58
+ {
59
+ name: 'access token',
60
+ useToken: useOidcAccessToken,
61
+ initial: {
62
+ accessToken: initialTokens.accessToken,
63
+ accessTokenPayload: initialTokens.accessTokenPayload,
64
+ generateDemonstrationOfProofOfPossessionAsync: null,
65
+ },
66
+ empty: { accessToken: null, accessTokenPayload: null },
67
+ },
68
+ {
69
+ name: 'ID token',
70
+ useToken: useOidcIdToken,
71
+ initial: {
72
+ idToken: initialTokens.idToken,
73
+ idTokenPayload: initialTokens.idTokenPayload,
74
+ },
75
+ empty: { idToken: null, idTokenPayload: null },
76
+ },
77
+ ])('$name hook', ({ useToken, initial, empty }) => {
78
+ it('initializes from the configured client', () => {
79
+ const { result } = renderHook(() => useToken('custom'));
80
+
81
+ expect(result.current).toEqual(initial);
82
+ expect(get).toHaveBeenCalledWith('custom');
83
+ });
84
+
85
+ it('uses the empty state when the client has no tokens', () => {
86
+ client.tokens = null;
87
+ const { result } = renderHook(() => useToken());
88
+
89
+ expect(result.current).toEqual(empty);
90
+ });
91
+
92
+ it.each([
93
+ 'token_renewed',
94
+ 'token_acquired',
95
+ 'logout_from_another_tab',
96
+ 'logout_from_same_tab',
97
+ 'refreshTokensAsync_error',
98
+ 'syncTokensAsync_error',
99
+ ])('refreshes token state on %s', eventName => {
100
+ const { result } = renderHook(() => useToken());
101
+ const listener = subscribeEvents.mock.calls[0][0];
102
+
103
+ client.tokens = null;
104
+ act(() => listener(eventName));
105
+ expect(result.current).toEqual(empty);
106
+
107
+ client.tokens = { ...initialTokens };
108
+ act(() => listener(eventName));
109
+ expect(result.current).toEqual(initial);
110
+ });
111
+
112
+ it('ignores unrelated events', () => {
113
+ const { result } = renderHook(() => useToken());
114
+ client.tokens = null;
115
+
116
+ act(() => subscribeEvents.mock.calls[0][0]('unrelated'));
117
+
118
+ expect(result.current).toEqual(initial);
119
+ });
120
+
121
+ it('cleans up subscriptions on configuration changes and unmount', () => {
122
+ subscribeEvents.mockReturnValueOnce('first').mockReturnValueOnce('second');
123
+ const { rerender, unmount } = renderHook(({ name }) => useToken(name), {
124
+ initialProps: { name: 'first' },
125
+ });
126
+
127
+ rerender({ name: 'second' });
128
+ expect(removeEventSubscription).toHaveBeenCalledExactlyOnceWith('first');
129
+ expect(subscribeEvents).toHaveBeenCalledTimes(2);
130
+ expect(get).toHaveBeenLastCalledWith('second');
131
+
132
+ unmount();
133
+ expect(removeEventSubscription.mock.calls).toEqual([['first'], ['second']]);
134
+ });
135
+ });
136
+
137
+ describe('access token proof generation', () => {
138
+ it('uses the initial access token when generating a proof', async () => {
139
+ client.configuration.demonstrating_proof_of_possession = true;
140
+ generateProof.mockResolvedValue('proof');
141
+ const { result } = renderHook(() => useOidcAccessToken());
142
+
143
+ await expect(
144
+ result.current.generateDemonstrationOfProofOfPossessionAsync(
145
+ 'https://api.example.com',
146
+ 'GET',
147
+ ),
148
+ ).resolves.toBe('proof');
149
+ expect(generateProof).toHaveBeenCalledExactlyOnceWith(
150
+ 'access-example',
151
+ 'https://api.example.com',
152
+ 'GET',
153
+ );
154
+ });
155
+
156
+ it('uses refreshed tokens and forwards proof extras after a token event', async () => {
157
+ client.configuration.demonstrating_proof_of_possession = true;
158
+ const { result } = renderHook(() => useOidcAccessToken());
159
+ client.tokens = { ...initialTokens, accessToken: 'renewed-example' };
160
+ act(() => subscribeEvents.mock.calls[0][0]('token_renewed'));
161
+ const extras = { nonce: 'example-nonce' };
162
+
163
+ await result.current.generateDemonstrationOfProofOfPossessionAsync(
164
+ 'https://api.example.com',
165
+ 'POST',
166
+ extras,
167
+ );
168
+
169
+ expect(generateProof).toHaveBeenCalledExactlyOnceWith(
170
+ 'renewed-example',
171
+ 'https://api.example.com',
172
+ 'POST',
173
+ extras,
174
+ );
175
+ });
176
+ });
177
+
178
+ describe('useOidc', () => {
179
+ it('tracks authentication on login and logout events', () => {
180
+ client.tokens = null;
181
+ const { result } = renderHook(() => useOidc('custom'));
182
+ const listener = subscribeEvents.mock.calls[0][0];
183
+ expect(result.current.isAuthenticated).toBe(false);
184
+
185
+ client.tokens = { ...initialTokens };
186
+ act(() => listener('token_acquired'));
187
+ expect(result.current.isAuthenticated).toBe(true);
188
+
189
+ client.tokens = null;
190
+ act(() => listener('logout_from_same_tab'));
191
+ expect(result.current.isAuthenticated).toBe(false);
192
+ });
193
+
194
+ it('forwards login and logout arguments and returns their promises', () => {
195
+ const loginPromise = Promise.resolve();
196
+ const logoutPromise = Promise.resolve();
197
+ client.loginAsync.mockReturnValue(loginPromise);
198
+ client.logoutAsync.mockReturnValue(logoutPromise);
199
+ const { result } = renderHook(() => useOidc('custom'));
200
+ const extras = { prompt: 'login' };
201
+
202
+ expect(result.current.login('/callback', extras, true, 'openid')).toBe(loginPromise);
203
+ expect(client.loginAsync).toHaveBeenCalledExactlyOnceWith(
204
+ '/callback',
205
+ extras,
206
+ false,
207
+ 'openid',
208
+ true,
209
+ );
210
+ expect(result.current.logout('/logout', extras)).toBe(logoutPromise);
211
+ expect(client.logoutAsync).toHaveBeenCalledExactlyOnceWith('/logout', extras);
212
+ });
213
+
214
+ it('returns renewed token data without adding internal fields', async () => {
215
+ client.renewTokensAsync.mockResolvedValue({ ...initialTokens, internal: 'not exposed' });
216
+ const { result } = renderHook(() => useOidc());
217
+ const extras = { scope: 'openid' };
218
+
219
+ await expect(result.current.renewTokens(extras)).resolves.toEqual(initialTokens);
220
+ expect(client.renewTokensAsync).toHaveBeenCalledExactlyOnceWith(extras);
221
+ });
222
+ });