@witsauth/react-client 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,6 +75,7 @@ interface OAuth2Config {
75
75
  redirectRoute?: string; // Route to redirect to after login (optional)
76
76
  logoutRedirectRoute?: string; // Route to redirect to after logout (optional)
77
77
  oAuthProvider?: 'witsauth' | string; // OAuth provider identifier
78
+ theme?: 'light' | 'dark' | 'system' | (() => 'light' | 'dark' | 'system' | string); // Theme type for hosted pages
78
79
  }
79
80
  ```
80
81
 
@@ -12,6 +12,6 @@ export declare class AuthClient {
12
12
  getAccessToken(): string | null;
13
13
  getExpiresAt(): number | null;
14
14
  isAuthenticated(): boolean;
15
- navigateToAccountManagement(): void;
15
+ navigateToAccountManagement(): Promise<void>;
16
16
  logout(): Promise<void>;
17
17
  }
package/dist/index.es.js CHANGED
@@ -99,6 +99,10 @@ var f = class {
99
99
  state: n,
100
100
  nonce: r
101
101
  });
102
+ if (this.config.theme) {
103
+ let e = typeof this.config.theme == "function" ? this.config.theme() : this.config.theme;
104
+ e && i.append("theme", e);
105
+ }
102
106
  this.config.scope && i.append("scope", this.config.scope), this.config.audience && i.append("audience", this.config.audience);
103
107
  let a = `${this.config.authorizationEndpoint}?${i.toString()}`;
104
108
  if (typeof window > "u" || !window.location) throw Error("startAuthorization requires a browser environment to redirect the user.");
@@ -169,17 +173,25 @@ var f = class {
169
173
  let e = this.getExpiresAt();
170
174
  return e !== null && Date.now() < e - 3e4 && !!this.getAccessToken();
171
175
  }
172
- navigateToAccountManagement() {
176
+ async navigateToAccountManagement() {
173
177
  let e = this.storage.getItem(c.REFRESH_TOKEN);
174
178
  if (!e) throw Error("No refresh token available to access account management.");
175
179
  if (typeof window > "u" || !document) throw Error("navigateToAccountManagement requires a browser environment.");
176
180
  let t = document.createElement("form");
177
- t.method = "POST", t.action = `${this.config.authorizationEndpoint.replace(/\/authorize\/?$/, "")}/account/session`;
178
- let n = (e, n) => {
181
+ t.method = "POST";
182
+ let n = this.config.authorizationEndpoint.replace(/\/authorize\/?$/, "");
183
+ t.action = `${n}/account/session`;
184
+ let r = (e, n) => {
179
185
  let r = document.createElement("input");
180
186
  r.type = "hidden", r.name = e, r.value = n, t.appendChild(r);
181
187
  };
182
- n("refreshToken", e), n("clientId", this.config.clientId), n("redirectUri", this.config.redirectUri), document.body.appendChild(t), t.submit();
188
+ try {
189
+ let e = await (await fetch(`${n}/csrf-token`, { credentials: "include" })).json();
190
+ e.csrfToken && r("_csrf", e.csrfToken);
191
+ } catch (e) {
192
+ this.log("warn", "Failed to fetch CSRF token for Account Management navigation", e);
193
+ }
194
+ r("refreshToken", e), r("clientId", this.config.clientId), r("redirectUri", this.config.redirectUri), document.body.appendChild(t), t.submit();
183
195
  }
184
196
  async logout() {
185
197
  let e = this.getAccessToken();
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WitsAuthReactClient={},e.React,e.jsxRuntime))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);var u={ACCESS_TOKEN:`witsauth_access_token`,REFRESH_TOKEN:`witsauth_refresh_token`,TOKEN_EXPIRES_AT:`witsauth_expires_at`,TOKEN_TYPE:`witsauth_token_type`,SCOPE:`witsauth_scope`,CODE_VERIFIER:`witsauth_code_verifier`,STATE:`witsauth_state`,NONCE:`witsauth_nonce`};function d(){let e=new Uint8Array(32);return crypto.getRandomValues(e),btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}function f(){let e=new Uint8Array(32);return crypto.getRandomValues(e),btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}async function p(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest(`SHA-256`,t),r=new Uint8Array(n);return btoa(String.fromCharCode(...r)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}var m=class{storageType;customStorage;memoryStorage=new Map;constructor(e,t){this.storageType=e,this.customStorage=t}getStorage(){if(this.storageType===`custom`&&this.customStorage)return this.customStorage;if(typeof window<`u`){if(this.storageType===`sessionStorage`&&window.sessionStorage)return window.sessionStorage;if((!this.storageType||this.storageType===`localStorage`)&&window.localStorage)return window.localStorage}let e=this.memoryStorage;return{getItem:t=>e.has(t)?e.get(t):null,setItem:(t,n)=>{e.set(t,n)},removeItem:t=>{e.delete(t)},clear:()=>{e.clear()}}}setItem(e,t){this.getStorage().setItem(e,t)}getItem(e){return this.getStorage().getItem(e)}removeItem(e){this.getStorage().removeItem(e)}clearTokens(){let e=this.getStorage();e.removeItem(u.ACCESS_TOKEN),e.removeItem(u.REFRESH_TOKEN),e.removeItem(u.TOKEN_EXPIRES_AT),e.removeItem(u.TOKEN_TYPE),e.removeItem(u.SCOPE)}clearTemporary(){let e=this.getStorage();e.removeItem(u.CODE_VERIFIER),e.removeItem(u.STATE),e.removeItem(u.NONCE)}},h=class{config;storage;constructor(e,t){this.config=e,this.storage=t||new m(e.storage,e.customStorage)}log(e,...t){let n={none:0,error:1,warn:2,info:3,debug:4},r=n[this.config.logLevel||`warn`]||0,i=n[e]||0;i>0&&r>=i&&console[e](...t)}async startAuthorization(){let e=d(),t=await p(e),n=f(),r=this.config.nonce||f();this.storage.setItem(u.CODE_VERIFIER,e),this.storage.setItem(u.STATE,n),this.storage.setItem(u.NONCE,r);let i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:this.config.responseType||`code`,code_challenge_method:this.config.codeChallengeMethod||`S256`,code_challenge:t,state:n,nonce:r});this.config.scope&&i.append(`scope`,this.config.scope),this.config.audience&&i.append(`audience`,this.config.audience);let a=`${this.config.authorizationEndpoint}?${i.toString()}`;if(typeof window>`u`||!window.location)throw Error(`startAuthorization requires a browser environment to redirect the user.`);window.location.href=a}async exchangeCodeForTokens(e,t){let n=this.storage.getItem(u.STATE),r=this.storage.getItem(u.CODE_VERIFIER);if(t!==n)throw Error(`Invalid state parameter. Possible CSRF attack.`);if(!r)throw Error(`Code verifier missing from storage.`);let i=new URLSearchParams({grant_type:`authorization_code`,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,code:e,code_verifier:r}),a=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:i.toString()});if(!a.ok)throw Error(`Failed to exchange code for tokens`);let o=await a.json(),s={accessToken:o.access_token,refreshToken:o.refresh_token,tokenType:o.token_type||`Bearer`,expiresAt:Date.now()+o.expires_in*1e3,scope:o.scope,idToken:o.id_token};return this.saveTokenInfo(s),this.storage.clearTemporary(),s}async refreshAccessToken(){let e=this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available`);let t=new URLSearchParams({grant_type:`refresh_token`,client_id:this.config.clientId,refresh_token:e}),n=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t.toString()});if(!n.ok)throw this.storage.clearTokens(),Error(`Failed to refresh token`);let r=await n.json(),i={accessToken:r.access_token,refreshToken:r.refresh_token||e,tokenType:r.token_type||`Bearer`,expiresAt:Date.now()+r.expires_in*1e3,scope:r.scope,idToken:r.id_token};return this.saveTokenInfo(i),i}saveTokenInfo(e){this.storage.setItem(u.ACCESS_TOKEN,e.accessToken),this.storage.setItem(u.TOKEN_EXPIRES_AT,e.expiresAt.toString()),this.storage.setItem(u.TOKEN_TYPE,e.tokenType),e.refreshToken&&this.storage.setItem(u.REFRESH_TOKEN,e.refreshToken),e.scope&&this.storage.setItem(u.SCOPE,e.scope)}getAccessToken(){return this.storage.getItem(u.ACCESS_TOKEN)}getExpiresAt(){let e=this.storage.getItem(u.TOKEN_EXPIRES_AT);if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}isAuthenticated(){let e=this.getExpiresAt();return e!==null&&Date.now()<e-3e4&&!!this.getAccessToken()}navigateToAccountManagement(){let e=this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available to access account management.`);if(typeof window>`u`||!document)throw Error(`navigateToAccountManagement requires a browser environment.`);let t=document.createElement(`form`);t.method=`POST`,t.action=`${this.config.authorizationEndpoint.replace(/\/authorize\/?$/,``)}/account/session`;let n=(e,n)=>{let r=document.createElement(`input`);r.type=`hidden`,r.name=e,r.value=n,t.appendChild(r)};n(`refreshToken`,e),n(`clientId`,this.config.clientId),n(`redirectUri`,this.config.redirectUri),document.body.appendChild(t),t.submit()}async logout(){let e=this.getAccessToken();if(this.storage.clearTokens(),this.storage.clearTemporary(),this.config.revokeEndpoint&&e)try{await fetch(this.config.revokeEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:e}).toString()})}catch(e){this.log(`error`,`Failed to revoke token during logout`,e)}}},g=(0,t.createContext)(null),_=({config:e,children:r})=>{let[i,a]=(0,t.useState)({isAuthenticated:!1,isLoading:!0}),o=(0,t.useMemo)(()=>new h(e),[e]);(0,t.useEffect)(()=>{(async()=>{try{if(o.isAuthenticated())a({isAuthenticated:!0,isLoading:!1});else try{await o.refreshAccessToken(),a({isAuthenticated:!0,isLoading:!1})}catch{a({isAuthenticated:!1,isLoading:!1})}}catch(e){a({isAuthenticated:!1,isLoading:!1,error:e instanceof Error?e.message:String(e)})}})()},[o]),(0,t.useEffect)(()=>{if(e.autoRefresh===!1||!i.isAuthenticated)return;let t=!1,n=setInterval(async()=>{if(!t)try{let n=o.getExpiresAt();if(!n)return;let r=(e.refreshThreshold??60)*1e3;if(Date.now()>=n-r){t=!0;try{let e=await o.refreshAccessToken();a(t=>({...t,tokenInfo:e}))}catch{c()}finally{t=!1}}}catch(e){o.log(`error`,`Auto-refresh error:`,e)}},1e4);return()=>clearInterval(n)},[e.autoRefresh,e.refreshThreshold,i.isAuthenticated,o]);let s=async()=>{a(e=>({...e,isLoading:!0})),await o.startAuthorization()},c=async()=>{a(e=>({...e,isLoading:!0}));try{await o.logout()}catch(e){o.log(`error`,`Logout error:`,e)}finally{a({isAuthenticated:!1,isLoading:!1}),e.logoutRedirectRoute&&typeof window<`u`&&window.location.assign(e.logoutRedirectRoute)}},l=async(t,n)=>{a(e=>({...e,isLoading:!0}));try{let r=await o.exchangeCodeForTokens(t,n);return a({isAuthenticated:!0,isLoading:!1,tokenInfo:r}),e.redirectRoute&&typeof window<`u`&&setTimeout(()=>{window.location.assign(e.redirectRoute)},0),r}catch(e){throw a({isAuthenticated:!1,isLoading:!1,error:e instanceof Error?e.message:String(e)}),e}},u={...i,login:s,logout:c,navigateToAccountManagement:()=>o.navigateToAccountManagement(),handleCallback:l,client:o};return(0,n.jsx)(g.Provider,{value:u,children:r})},v=()=>{let e=(0,t.useContext)(g);if(!e)throw Error(`useAuth must be used within an AuthProvider`);return e},y=({onSuccess:e,onError:r,loadingComponent:i=(0,n.jsx)(`div`,{children:`Processing authentication...`})})=>{let{handleCallback:a,client:o}=v(),s=(0,t.useRef)(!1);return(0,t.useEffect)(()=>{s.current||(s.current=!0,(async()=>{let t=new URLSearchParams(window.location.search),n=new URLSearchParams(window.location.hash.substring(1)),i=t.get(`code`)||n.get(`code`),s=t.get(`state`)||n.get(`state`),c=t.get(`error`)||n.get(`error`);if(c){let e=Error(`OAuth Error: ${c}`);r&&r(e);return}if(i&&s)try{await a(i,s),e&&e()}catch(e){r&&r(e instanceof Error?e:Error(String(e)))}else if(!i){let e=o.config;window.location.assign(e.redirectRoute||`/`)}})())},[a,o,e,r]),(0,n.jsx)(n.Fragment,{children:i})};function b(e,t){e.interceptors.request.use(e=>{let n=t.getAccessToken();return n&&e.headers&&(e.headers.Authorization=`Bearer ${n}`),e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async n=>{let r=n.config;if(n.response?.status===401&&r&&!r._retry){r._retry=!0;try{let n=await t.refreshAccessToken();return r.headers&&(r.headers.Authorization=`Bearer ${n.accessToken}`),e(r)}catch(e){return t.logout(),Promise.reject(e)}}return Promise.reject(n)})}e.AuthCallback=y,e.AuthClient=h,e.AuthProvider=_,e.STORAGE_KEYS=u,e.TokenStorage=m,e.createCodeChallenge=p,e.generateCodeVerifier=d,e.generateState=f,e.setupAxiosInterceptors=b,e.useAuth=v});
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WitsAuthReactClient={},e.React,e.jsxRuntime))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);var u={ACCESS_TOKEN:`witsauth_access_token`,REFRESH_TOKEN:`witsauth_refresh_token`,TOKEN_EXPIRES_AT:`witsauth_expires_at`,TOKEN_TYPE:`witsauth_token_type`,SCOPE:`witsauth_scope`,CODE_VERIFIER:`witsauth_code_verifier`,STATE:`witsauth_state`,NONCE:`witsauth_nonce`};function d(){let e=new Uint8Array(32);return crypto.getRandomValues(e),btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}function f(){let e=new Uint8Array(32);return crypto.getRandomValues(e),btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}async function p(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest(`SHA-256`,t),r=new Uint8Array(n);return btoa(String.fromCharCode(...r)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=/g,``)}var m=class{storageType;customStorage;memoryStorage=new Map;constructor(e,t){this.storageType=e,this.customStorage=t}getStorage(){if(this.storageType===`custom`&&this.customStorage)return this.customStorage;if(typeof window<`u`){if(this.storageType===`sessionStorage`&&window.sessionStorage)return window.sessionStorage;if((!this.storageType||this.storageType===`localStorage`)&&window.localStorage)return window.localStorage}let e=this.memoryStorage;return{getItem:t=>e.has(t)?e.get(t):null,setItem:(t,n)=>{e.set(t,n)},removeItem:t=>{e.delete(t)},clear:()=>{e.clear()}}}setItem(e,t){this.getStorage().setItem(e,t)}getItem(e){return this.getStorage().getItem(e)}removeItem(e){this.getStorage().removeItem(e)}clearTokens(){let e=this.getStorage();e.removeItem(u.ACCESS_TOKEN),e.removeItem(u.REFRESH_TOKEN),e.removeItem(u.TOKEN_EXPIRES_AT),e.removeItem(u.TOKEN_TYPE),e.removeItem(u.SCOPE)}clearTemporary(){let e=this.getStorage();e.removeItem(u.CODE_VERIFIER),e.removeItem(u.STATE),e.removeItem(u.NONCE)}},h=class{config;storage;constructor(e,t){this.config=e,this.storage=t||new m(e.storage,e.customStorage)}log(e,...t){let n={none:0,error:1,warn:2,info:3,debug:4},r=n[this.config.logLevel||`warn`]||0,i=n[e]||0;i>0&&r>=i&&console[e](...t)}async startAuthorization(){let e=d(),t=await p(e),n=f(),r=this.config.nonce||f();this.storage.setItem(u.CODE_VERIFIER,e),this.storage.setItem(u.STATE,n),this.storage.setItem(u.NONCE,r);let i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:this.config.responseType||`code`,code_challenge_method:this.config.codeChallengeMethod||`S256`,code_challenge:t,state:n,nonce:r});if(this.config.theme){let e=typeof this.config.theme==`function`?this.config.theme():this.config.theme;e&&i.append(`theme`,e)}this.config.scope&&i.append(`scope`,this.config.scope),this.config.audience&&i.append(`audience`,this.config.audience);let a=`${this.config.authorizationEndpoint}?${i.toString()}`;if(typeof window>`u`||!window.location)throw Error(`startAuthorization requires a browser environment to redirect the user.`);window.location.href=a}async exchangeCodeForTokens(e,t){let n=this.storage.getItem(u.STATE),r=this.storage.getItem(u.CODE_VERIFIER);if(t!==n)throw Error(`Invalid state parameter. Possible CSRF attack.`);if(!r)throw Error(`Code verifier missing from storage.`);let i=new URLSearchParams({grant_type:`authorization_code`,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,code:e,code_verifier:r}),a=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:i.toString()});if(!a.ok)throw Error(`Failed to exchange code for tokens`);let o=await a.json(),s={accessToken:o.access_token,refreshToken:o.refresh_token,tokenType:o.token_type||`Bearer`,expiresAt:Date.now()+o.expires_in*1e3,scope:o.scope,idToken:o.id_token};return this.saveTokenInfo(s),this.storage.clearTemporary(),s}async refreshAccessToken(){let e=this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available`);let t=new URLSearchParams({grant_type:`refresh_token`,client_id:this.config.clientId,refresh_token:e}),n=await fetch(this.config.tokenEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:t.toString()});if(!n.ok)throw this.storage.clearTokens(),Error(`Failed to refresh token`);let r=await n.json(),i={accessToken:r.access_token,refreshToken:r.refresh_token||e,tokenType:r.token_type||`Bearer`,expiresAt:Date.now()+r.expires_in*1e3,scope:r.scope,idToken:r.id_token};return this.saveTokenInfo(i),i}saveTokenInfo(e){this.storage.setItem(u.ACCESS_TOKEN,e.accessToken),this.storage.setItem(u.TOKEN_EXPIRES_AT,e.expiresAt.toString()),this.storage.setItem(u.TOKEN_TYPE,e.tokenType),e.refreshToken&&this.storage.setItem(u.REFRESH_TOKEN,e.refreshToken),e.scope&&this.storage.setItem(u.SCOPE,e.scope)}getAccessToken(){return this.storage.getItem(u.ACCESS_TOKEN)}getExpiresAt(){let e=this.storage.getItem(u.TOKEN_EXPIRES_AT);if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}isAuthenticated(){let e=this.getExpiresAt();return e!==null&&Date.now()<e-3e4&&!!this.getAccessToken()}async navigateToAccountManagement(){let e=this.storage.getItem(u.REFRESH_TOKEN);if(!e)throw Error(`No refresh token available to access account management.`);if(typeof window>`u`||!document)throw Error(`navigateToAccountManagement requires a browser environment.`);let t=document.createElement(`form`);t.method=`POST`;let n=this.config.authorizationEndpoint.replace(/\/authorize\/?$/,``);t.action=`${n}/account/session`;let r=(e,n)=>{let r=document.createElement(`input`);r.type=`hidden`,r.name=e,r.value=n,t.appendChild(r)};try{let e=await(await fetch(`${n}/csrf-token`,{credentials:`include`})).json();e.csrfToken&&r(`_csrf`,e.csrfToken)}catch(e){this.log(`warn`,`Failed to fetch CSRF token for Account Management navigation`,e)}r(`refreshToken`,e),r(`clientId`,this.config.clientId),r(`redirectUri`,this.config.redirectUri),document.body.appendChild(t),t.submit()}async logout(){let e=this.getAccessToken();if(this.storage.clearTokens(),this.storage.clearTemporary(),this.config.revokeEndpoint&&e)try{await fetch(this.config.revokeEndpoint,{method:`POST`,headers:{"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({token:e}).toString()})}catch(e){this.log(`error`,`Failed to revoke token during logout`,e)}}},g=(0,t.createContext)(null),_=({config:e,children:r})=>{let[i,a]=(0,t.useState)({isAuthenticated:!1,isLoading:!0}),o=(0,t.useMemo)(()=>new h(e),[e]);(0,t.useEffect)(()=>{(async()=>{try{if(o.isAuthenticated())a({isAuthenticated:!0,isLoading:!1});else try{await o.refreshAccessToken(),a({isAuthenticated:!0,isLoading:!1})}catch{a({isAuthenticated:!1,isLoading:!1})}}catch(e){a({isAuthenticated:!1,isLoading:!1,error:e instanceof Error?e.message:String(e)})}})()},[o]),(0,t.useEffect)(()=>{if(e.autoRefresh===!1||!i.isAuthenticated)return;let t=!1,n=setInterval(async()=>{if(!t)try{let n=o.getExpiresAt();if(!n)return;let r=(e.refreshThreshold??60)*1e3;if(Date.now()>=n-r){t=!0;try{let e=await o.refreshAccessToken();a(t=>({...t,tokenInfo:e}))}catch{c()}finally{t=!1}}}catch(e){o.log(`error`,`Auto-refresh error:`,e)}},1e4);return()=>clearInterval(n)},[e.autoRefresh,e.refreshThreshold,i.isAuthenticated,o]);let s=async()=>{a(e=>({...e,isLoading:!0})),await o.startAuthorization()},c=async()=>{a(e=>({...e,isLoading:!0}));try{await o.logout()}catch(e){o.log(`error`,`Logout error:`,e)}finally{a({isAuthenticated:!1,isLoading:!1}),e.logoutRedirectRoute&&typeof window<`u`&&window.location.assign(e.logoutRedirectRoute)}},l=async(t,n)=>{a(e=>({...e,isLoading:!0}));try{let r=await o.exchangeCodeForTokens(t,n);return a({isAuthenticated:!0,isLoading:!1,tokenInfo:r}),e.redirectRoute&&typeof window<`u`&&setTimeout(()=>{window.location.assign(e.redirectRoute)},0),r}catch(e){throw a({isAuthenticated:!1,isLoading:!1,error:e instanceof Error?e.message:String(e)}),e}},u={...i,login:s,logout:c,navigateToAccountManagement:()=>o.navigateToAccountManagement(),handleCallback:l,client:o};return(0,n.jsx)(g.Provider,{value:u,children:r})},v=()=>{let e=(0,t.useContext)(g);if(!e)throw Error(`useAuth must be used within an AuthProvider`);return e},y=({onSuccess:e,onError:r,loadingComponent:i=(0,n.jsx)(`div`,{children:`Processing authentication...`})})=>{let{handleCallback:a,client:o}=v(),s=(0,t.useRef)(!1);return(0,t.useEffect)(()=>{s.current||(s.current=!0,(async()=>{let t=new URLSearchParams(window.location.search),n=new URLSearchParams(window.location.hash.substring(1)),i=t.get(`code`)||n.get(`code`),s=t.get(`state`)||n.get(`state`),c=t.get(`error`)||n.get(`error`);if(c){let e=Error(`OAuth Error: ${c}`);r&&r(e);return}if(i&&s)try{await a(i,s),e&&e()}catch(e){r&&r(e instanceof Error?e:Error(String(e)))}else if(!i){let e=o.config;window.location.assign(e.redirectRoute||`/`)}})())},[a,o,e,r]),(0,n.jsx)(n.Fragment,{children:i})};function b(e,t){e.interceptors.request.use(e=>{let n=t.getAccessToken();return n&&e.headers&&(e.headers.Authorization=`Bearer ${n}`),e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async n=>{let r=n.config;if(n.response?.status===401&&r&&!r._retry){r._retry=!0;try{let n=await t.refreshAccessToken();return r.headers&&(r.headers.Authorization=`Bearer ${n.accessToken}`),e(r)}catch(e){return t.logout(),Promise.reject(e)}}return Promise.reject(n)})}e.AuthCallback=y,e.AuthClient=h,e.AuthProvider=_,e.STORAGE_KEYS=u,e.TokenStorage=m,e.createCodeChallenge=p,e.generateCodeVerifier=d,e.generateState=f,e.setupAxiosInterceptors=b,e.useAuth=v});
@@ -24,6 +24,7 @@ export interface OAuth2Config {
24
24
  redirectRoute?: string;
25
25
  logoutRedirectRoute?: string;
26
26
  oAuthProvider?: 'witsauth' | string;
27
+ theme?: 'light' | 'dark' | 'system' | (() => 'light' | 'dark' | 'system' | string);
27
28
  }
28
29
  export interface TokenInfo {
29
30
  accessToken: string;
@@ -4,7 +4,7 @@ import { AuthClient } from '../core/AuthClient';
4
4
  interface AuthContextValue extends AuthState {
5
5
  login: () => Promise<void>;
6
6
  logout: () => Promise<void>;
7
- navigateToAccountManagement: () => void;
7
+ navigateToAccountManagement: () => Promise<void>;
8
8
  client: AuthClient;
9
9
  handleCallback: (code: string, state: string) => Promise<TokenInfo>;
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@witsauth/react-client",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Official React client library for integrating Witsauth Single Sign-On (SSO) authentication or similar services into your React applications.",
5
5
  "main": "./dist/index.umd.js",
6
6
  "module": "./dist/index.es.js",