@jasperoosthoek/zustand-auth-registry 0.1.0 → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ##### Version 0.1.1
4
+ - Fix: `tokenType` is now persisted and restored for non-`Bearer` schemes
5
+
6
+ ##### Version 0.1.0
7
+ - **BREAKING**: Generic `D` is now data-agnostic — store any shape, not just a user
8
+ - Rename `state.user` → `state.data`, `setUser` → `setData`, `unsetUser` → `reset`
9
+ - Rename `extractUser` → `extractData`, `getUserUrl` → `dataUrl`, `userKey` → `dataKey`
10
+ - Rename `getCurrentUser()` → `fetchData()`
11
+ - Default persistence key changed from `'user'` to `'data'`
12
+
13
+ ##### Version 0.0.3
14
+ - Automatic `CSRF` token injection via `axios` interceptor for `POST`/`PUT`/`PATCH`/`DELETE` requests
15
+
16
+ ##### Version 0.0.2
17
+ - **BREAKING**: Remove `token` and `setToken` - use `tokens?.accessToken` and `setBearerToken`/`setTokens` instead
18
+ - New `setBearerToken(token)` convenience method for simple Bearer token auth
19
+ - Cookie-based authentication with CSRF support
20
+ - Persistence to localStorage is now opt-in via `persistence: { enabled: true }`
21
+
22
+ ##### Version 0.0.1
23
+ - Initial release
24
+ - `createAuthRegistry` for type-safe auth store management
25
+ - `useAuth` hook with `login`, `logout`, `getCurrentUser`, `refresh`, `checkAuth`
26
+ - `AuthError` class with typed error codes (`AuthErrorCode`)
27
+ - `createAuthError()` helper for converting axios errors
28
+ - `extractTokens` for structured token data (access token, refresh token, expiry)
29
+ - `extractUser` option (function or string key) to extract user from responses
30
+ - Persistence to localStorage/sessionStorage
31
+ - Auto-refresh with configurable threshold (`autoRefresh`, `refreshThreshold`)
32
+ - Full TypeScript support with generics
@@ -31,6 +31,7 @@ export type AuthConfig<D> = {
31
31
  storage?: Storage;
32
32
  tokenKey?: string;
33
33
  refreshTokenKey?: string;
34
+ tokenTypeKey?: string;
34
35
  dataKey?: string;
35
36
  expiryKey?: string;
36
37
  };
@@ -63,6 +64,7 @@ export type ValidatedAuthConfig<D> = {
63
64
  storage: Storage;
64
65
  tokenKey: string;
65
66
  refreshTokenKey: string;
67
+ tokenTypeKey: string;
66
68
  dataKey: string;
67
69
  expiryKey: string;
68
70
  };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r(require("zustand"),require("react")):"function"==typeof define&&define.amd?define(["zustand","react"],r):"object"==typeof exports?exports["@jasperoosthoek/zustand-auth-registry"]=r(require("zustand"),require("react")):e["@jasperoosthoek/zustand-auth-registry"]=r(e.zustand,e.react)}(this,(e,r)=>(()=>{"use strict";var t={155:e=>{e.exports=r},287:r=>{r.exports=e}},n={};function o(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,o),a.exports}o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};o.r(a),o.d(a,{AuthError:()=>b,AuthErrorCode:()=>g,createAuthError:()=>A,createAuthRegistry:()=>d,createAuthStore:()=>c,useAuth:()=>k,validateAuthConfig:()=>i});var i=function(e){var r,t,n,o,a,i,s,l,c,d,f,v,h,p,k,y,T,g,E,b,A,x,N;if(!e.axios)throw new Error("AuthConfig: axios instance is required");if(!e.loginUrl)throw new Error("AuthConfig: loginUrl is required");var w,m,I=(null===(r=e.cookieAuth)||void 0===r?void 0:r.enabled)?{enabled:!0,csrf:{enabled:null!==(n=null===(t=e.cookieAuth.csrf)||void 0===t?void 0:t.enabled)&&void 0!==n&&n,headerName:(null===(o=e.cookieAuth.csrf)||void 0===o?void 0:o.headerName)||"X-CSRFToken",getToken:null!==(i=null===(a=e.cookieAuth.csrf)||void 0===a?void 0:a.getToken)&&void 0!==i?i:(w=(null===(s=e.cookieAuth.csrf)||void 0===s?void 0:s.cookieName)||"csrftoken",function(){if("undefined"==typeof document)return null;var e=document.cookie.match(new RegExp("".concat(w,"=([^;]+)")));return e?e[1]:null})}}:void 0,_={enabled:null!==(c=null===(l=e.persistence)||void 0===l?void 0:l.enabled)&&void 0!==c&&c,storage:null!==(f=null===(d=e.persistence)||void 0===d?void 0:d.storage)&&void 0!==f?f:"undefined"!=typeof window&&window.localStorage?window.localStorage:{},tokenKey:null!==(h=null===(v=e.persistence)||void 0===v?void 0:v.tokenKey)&&void 0!==h?h:"token",refreshTokenKey:null!==(k=null===(p=e.persistence)||void 0===p?void 0:p.refreshTokenKey)&&void 0!==k?k:"refresh_token",dataKey:null!==(T=null===(y=e.persistence)||void 0===y?void 0:y.dataKey)&&void 0!==T?T:"data",expiryKey:null!==(E=null===(g=e.persistence)||void 0===g?void 0:g.expiryKey)&&void 0!==E?E:"expires_at"};return{axios:e.axios,loginUrl:e.loginUrl,logoutUrl:e.logoutUrl,refreshUrl:e.refreshUrl,dataUrl:e.dataUrl,authCheckUrl:e.authCheckUrl,extractTokens:null!==(b=e.extractTokens)&&void 0!==b?b:u,extractData:(m=e.extractData,"string"==typeof m?function(e){var r;return null!==(r=e[m])&&void 0!==r?r:null}:m),formatAuthHeader:null!==(A=e.formatAuthHeader)&&void 0!==A?A:function(e,r){return void 0===r&&(r="Bearer"),"".concat(r," ").concat(e)},autoRefresh:null===(x=e.autoRefresh)||void 0===x||x,refreshThreshold:null!==(N=e.refreshThreshold)&&void 0!==N?N:3e5,cookieAuth:I,persistence:_,onError:e.onError,onLogin:e.onLogin,onLogout:e.onLogout}};function u(e){if(e.access_token)return{accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:e.expires_in?Date.now()+1e3*e.expires_in:void 0,tokenType:e.token_type||"Bearer"};var r=e.token||e.auth_token;if(r)return{accessToken:r,tokenType:"Bearer"};throw new Error("No token found in response. Provide extractTokens or ensure response contains access_token/token field.")}var s=o(287),l=["post","put","patch","delete"],c=function(e){var r=e.persistence,t=e.cookieAuth;!function(e){var r;if(!(null===(r=e.cookieAuth)||void 0===r?void 0:r.csrf.enabled))return null;var t=e.cookieAuth.csrf,n=t.headerName,o=t.getToken;e.axios.interceptors.request.use(function(e){var r,t=null===(r=e.method)||void 0===r?void 0:r.toLowerCase();if(t&&l.includes(t)){var a=o();a&&(e.headers[n]=a)}return e})}(e);var n=function(){if(null==t?void 0:t.enabled)return null;if(!r.enabled)return null;try{var e=r.storage.getItem(r.tokenKey);if(!e)return null;var n=r.storage.getItem(r.refreshTokenKey),o=r.storage.getItem(r.expiryKey),a=o?parseInt(o,10):void 0;return{accessToken:e,refreshToken:n||void 0,expiresAt:a&&!isNaN(a)?a:void 0,tokenType:"Bearer"}}catch(e){return null}}(),o=function(){if(!r.enabled)return null;try{var e=r.storage.getItem(r.dataKey);return e?JSON.parse(e):null}catch(e){return null}}(),a=(null==t?void 0:t.enabled)?null:!!(null==n?void 0:n.accessToken),i=(0,s.create)(function(i,u){return{tokens:n,data:o,isAuthenticated:a,setTokens:function(n){var o;if(i({tokens:n,isAuthenticated:!0}),!(null==t?void 0:t.enabled)&&r.enabled)try{r.storage.setItem(r.tokenKey,n.accessToken),n.refreshToken?r.storage.setItem(r.refreshTokenKey,n.refreshToken):r.storage.removeItem(r.refreshTokenKey),n.expiresAt?r.storage.setItem(r.expiryKey,n.expiresAt.toString()):r.storage.removeItem(r.expiryKey)}catch(r){null===(o=e.onError)||void 0===o||o.call(e,r)}},setBearerToken:function(e){u().setTokens({accessToken:e,tokenType:"Bearer"})},setAuthenticated:function(e){i({isAuthenticated:e})},setData:function(t){var n;if(i({data:t}),r.enabled)try{r.storage.setItem(r.dataKey,JSON.stringify(t))}catch(r){null===(n=e.onError)||void 0===n||n.call(e,r)}},reset:function(){var t;if(i({data:null,tokens:null,isAuthenticated:!1}),r.enabled)try{r.storage.removeItem(r.tokenKey),r.storage.removeItem(r.refreshTokenKey),r.storage.removeItem(r.dataKey),r.storage.removeItem(r.expiryKey)}catch(r){null===(t=e.onError)||void 0===t||t.call(e,r)}},isTokenExpired:function(){var e=u().tokens;return!!(null==e?void 0:e.expiresAt)&&Date.now()>=e.expiresAt}}});return Object.assign(i,{config:e})};function d(){var e={};return function(r,t){var n=String(r);if(!e[n]){var o=i(t);e[n]=c(o)}return e[n]}}var f=o(155),v=function(e,r,t,n){return new(t||(t=Promise))(function(o,a){function i(e){try{s(n.next(e))}catch(e){a(e)}}function u(e){try{s(n.throw(e))}catch(e){a(e)}}function s(e){var r;e.done?o(e.value):(r=e.value,r instanceof t?r:new t(function(e){e(r)})).then(i,u)}s((n=n.apply(e,r||[])).next())})},h=function(e,r){var t,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(u){return function(s){return function(u){if(t)throw new TypeError("Generator is already executing.");for(;a&&(a=0,u[0]&&(i=0)),i;)try{if(t=1,n&&(o=2&u[0]?n.return:u[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,u[1])).done)return o;switch(n=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,n=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){i=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){i.label=u[1];break}if(6===u[0]&&i.label<o[1]){i.label=o[1],o=u;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(u);break}o[2]&&i.ops.pop(),i.trys.pop();continue}u=r.call(e,i)}catch(e){u=[6,e],n=0}finally{t=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,s])}}},p=new WeakMap;function k(e){var r=this,t=e(),n=t.setTokens,o=t.setAuthenticated,a=t.setData,i=t.reset,u=t.tokens,s=t.data,l=t.isAuthenticated,c=t.isTokenExpired,d=e.config,k=(0,f.useCallback)(function(e,r){var t;(null===(t=d.cookieAuth)||void 0===t?void 0:t.enabled)||(e?d.axios.defaults.headers.common.Authorization=d.formatAuthHeader(e,r):delete d.axios.defaults.headers.common.Authorization)},[d]),T=(0,f.useCallback)(function(){return v(r,void 0,void 0,function(){var e,r,t,o,a,s;return h(this,function(l){switch(l.label){case 0:if(!d.refreshUrl)return[2,!1];l.label=1;case 1:return l.trys.push([1,5,,6]),(null===(a=d.cookieAuth)||void 0===a?void 0:a.enabled)?(e=y(d),[4,d.axios.post(d.refreshUrl,{},{headers:e})]):[3,3];case 2:return l.sent(),[2,!0];case 3:return(null==u?void 0:u.refreshToken)?[4,d.axios.post(d.refreshUrl,{refresh_token:u.refreshToken})]:[2,!1];case 4:return r=l.sent(),t=d.extractTokens(r.data),n(t),k(t.accessToken,t.tokenType),[2,!0];case 5:return o=l.sent(),i(),k(),null===(s=d.onError)||void 0===s||s.call(d,o),[2,!1];case 6:return[2]}})})},[u,d,n,i,k]),g=(0,f.useCallback)(function(){return v(r,void 0,void 0,function(){var r,t,n,u,s,l=this;return h(this,function(c){return r=d.authCheckUrl,(null===(s=d.cookieAuth)||void 0===s?void 0:s.enabled)&&r?(t=p.get(e))?[2,t]:(n=function(){return v(l,void 0,void 0,function(){var t,n,u,s,l,c;return h(this,function(f){switch(f.label){case 0:return f.trys.push([0,6,7,8]),t=y(d),[4,d.axios.get(r,{headers:t})];case 1:return(n=f.sent()).data.authenticated?(o(!0),(u=null===(l=d.extractData)||void 0===l?void 0:l.call(d,n.data))?(a(u),[3,4]):[3,2]):[3,5];case 2:return d.dataUrl?[4,E()]:[3,4];case 3:f.sent(),f.label=4;case 4:return[2,!0];case 5:return i(),[2,!1];case 6:return s=f.sent(),i(),null===(c=d.onError)||void 0===c||c.call(d,s),[2,!1];case 7:return p.delete(e),[7];case 8:return[2]}})})},u=n(),p.set(e,u),[2,u]):[2,!1]})})},[e,d,o,a]),E=(0,f.useCallback)(function(){return v(r,void 0,void 0,function(){var e,r,t;return h(this,function(n){switch(n.label){case 0:if(!d.dataUrl)return[2];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,d.axios.get(d.dataUrl)];case 2:return e=n.sent(),a(e.data),[3,4];case 3:throw r=n.sent(),i(),k(),null===(t=d.onError)||void 0===t||t.call(d,r),r;case 4:return[2]}})})},[d,a,i,k]);return(0,f.useEffect)(function(){var e;if(null===(e=d.cookieAuth)||void 0===e?void 0:e.enabled)null===l&&g();else if(null==u?void 0:u.accessToken){if(k(u.accessToken,u.tokenType),c())return void(u.refreshToken&&d.autoRefresh?T():i());if(u.expiresAt&&u.refreshToken&&d.autoRefresh){var r=u.expiresAt-Date.now(),t=Math.max(r-d.refreshThreshold,0),n=setTimeout(T,t);return function(){return clearTimeout(n)}}!s&&d.dataUrl&&E().catch(function(){})}},[u,s,l,d,c,T,g,E,k,i]),{login:function(t,u){return v(r,void 0,void 0,function(){var r,s,l,c,f,v,p,T,g,b,A,x;return h(this,function(h){switch(h.label){case 0:return h.trys.push([0,5,,6]),r=(null===(p=d.cookieAuth)||void 0===p?void 0:p.enabled)?y(d):{},[4,d.axios.post(d.loginUrl,t,{headers:r})];case 1:return s=h.sent(),(null===(T=d.cookieAuth)||void 0===T?void 0:T.enabled)?o(!0):(l=d.extractTokens(s.data),n(l),k(l.accessToken,l.tokenType)),(c=null===(g=d.extractData)||void 0===g?void 0:g.call(d,s.data))?(a(c),null===(b=d.onLogin)||void 0===b||b.call(d,c),[3,4]):[3,2];case 2:return d.dataUrl?[4,E()]:[3,4];case 3:h.sent(),(f=e.getState().data)&&(null===(A=d.onLogin)||void 0===A||A.call(d,f)),h.label=4;case 4:return null==u||u(),[3,6];case 5:throw v=h.sent(),i(),k(),null===(x=d.onError)||void 0===x||x.call(d,v),v;case 6:return[2]}})})},logout:function(){return v(r,void 0,void 0,function(){var e,r,t,n,o;return h(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,4,5]),d.logoutUrl?(e=(null===(t=d.cookieAuth)||void 0===t?void 0:t.enabled)?y(d):{},[4,d.axios.post(d.logoutUrl,{},{headers:e})]):[3,2];case 1:a.sent(),a.label=2;case 2:return[3,5];case 3:return r=a.sent(),null===(n=d.onError)||void 0===n||n.call(d,r),[3,5];case 4:return i(),k(),null===(o=d.onLogout)||void 0===o||o.call(d),[7];case 5:return[2]}})})},refresh:T,checkAuth:g,fetchData:E}}function y(e){var r,t,n={};if(null===(t=null===(r=e.cookieAuth)||void 0===r?void 0:r.csrf)||void 0===t?void 0:t.enabled){var o=e.cookieAuth.csrf.getToken();o&&(n[e.cookieAuth.csrf.headerName]=o)}return n}var T,g,E=(T=function(e,r){return T=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)Object.prototype.hasOwnProperty.call(r,t)&&(e[t]=r[t])},T(e,r)},function(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function t(){this.constructor=e}T(e,r),e.prototype=null===r?Object.create(r):(t.prototype=r.prototype,new t)});!function(e){e.INVALID_CREDENTIALS="INVALID_CREDENTIALS",e.TOKEN_EXPIRED="TOKEN_EXPIRED",e.TOKEN_INVALID="TOKEN_INVALID",e.REFRESH_FAILED="REFRESH_FAILED",e.NETWORK_ERROR="NETWORK_ERROR",e.USER_NOT_FOUND="USER_NOT_FOUND",e.UNAUTHORIZED="UNAUTHORIZED",e.CSRF_TOKEN_MISSING="CSRF_TOKEN_MISSING",e.FORBIDDEN="FORBIDDEN",e.UNKNOWN="UNKNOWN"}(g||(g={}));var b=function(e){function r(t,n,o){var a=e.call(this,o||t)||this;return a.code=t,a.originalError=n,a.name="AuthError",Error.captureStackTrace&&Error.captureStackTrace(a,r),a}return E(r,e),r.prototype.toJSON=function(){return{code:this.code,message:this.message,name:this.name}},r.isAuthError=function(e){return e instanceof r},r}(Error);function A(e){var r,t,n,o,a,i;if(b.isAuthError(e))return e;if(e.response){var u=e.response.status,s=e.response.data;switch(u){case 401:return(null===(r=null==s?void 0:s.detail)||void 0===r?void 0:r.toLowerCase().includes("expired"))||(null===(t=null==s?void 0:s.message)||void 0===t?void 0:t.toLowerCase().includes("expired"))?new b(g.TOKEN_EXPIRED,e,"Token has expired"):(null===(n=null==s?void 0:s.detail)||void 0===n?void 0:n.toLowerCase().includes("invalid"))||(null===(o=null==s?void 0:s.detail)||void 0===o?void 0:o.toLowerCase().includes("credentials"))?new b(g.INVALID_CREDENTIALS,e,"Invalid credentials"):new b(g.UNAUTHORIZED,e,"Unauthorized");case 403:return(null===(a=null==s?void 0:s.detail)||void 0===a?void 0:a.toLowerCase().includes("csrf"))?new b(g.CSRF_TOKEN_MISSING,e,"CSRF token missing or invalid"):new b(g.FORBIDDEN,e,"Access forbidden");case 404:return(null===(i=null==s?void 0:s.detail)||void 0===i?void 0:i.toLowerCase().includes("user"))?new b(g.USER_NOT_FOUND,e,"User not found"):new b(g.UNKNOWN,e,"Resource not found");default:return new b(g.UNKNOWN,e,"HTTP ".concat(u," error"))}}return e.request?new b(g.NETWORK_ERROR,e,"Network error - no response received"):new b(g.UNKNOWN,e,e.message||"Unknown error")}return a})());
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("zustand"),require("react")):"function"==typeof define&&define.amd?define(["zustand","react"],t):"object"==typeof exports?exports["@jasperoosthoek/zustand-auth-registry"]=t(require("zustand"),require("react")):e["@jasperoosthoek/zustand-auth-registry"]=t(e.zustand,e.react)}(this,(e,t)=>(()=>{"use strict";var r={155:e=>{e.exports=t},287:t=>{t.exports=e}},n={};function o(e){var t=n[e];if(void 0!==t)return t.exports;var a=n[e]={exports:{}};return r[e](a,a.exports,o),a.exports}o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};o.r(a),o.d(a,{AuthError:()=>b,AuthErrorCode:()=>g,createAuthError:()=>A,createAuthRegistry:()=>d,createAuthStore:()=>c,useAuth:()=>k,validateAuthConfig:()=>i});var i=function(e){var t,r,n,o,a,i,s,l,c,d,f,v,h,p,k,y,T,g,E,b,A,m,x,N,w;if(!e.axios)throw new Error("AuthConfig: axios instance is required");if(!e.loginUrl)throw new Error("AuthConfig: loginUrl is required");var I,_,O=(null===(t=e.cookieAuth)||void 0===t?void 0:t.enabled)?{enabled:!0,csrf:{enabled:null!==(n=null===(r=e.cookieAuth.csrf)||void 0===r?void 0:r.enabled)&&void 0!==n&&n,headerName:(null===(o=e.cookieAuth.csrf)||void 0===o?void 0:o.headerName)||"X-CSRFToken",getToken:null!==(i=null===(a=e.cookieAuth.csrf)||void 0===a?void 0:a.getToken)&&void 0!==i?i:(I=(null===(s=e.cookieAuth.csrf)||void 0===s?void 0:s.cookieName)||"csrftoken",function(){if("undefined"==typeof document)return null;var e=document.cookie.match(new RegExp("".concat(I,"=([^;]+)")));return e?e[1]:null})}}:void 0,K={enabled:null!==(c=null===(l=e.persistence)||void 0===l?void 0:l.enabled)&&void 0!==c&&c,storage:null!==(f=null===(d=e.persistence)||void 0===d?void 0:d.storage)&&void 0!==f?f:"undefined"!=typeof window&&window.localStorage?window.localStorage:{},tokenKey:null!==(h=null===(v=e.persistence)||void 0===v?void 0:v.tokenKey)&&void 0!==h?h:"token",refreshTokenKey:null!==(k=null===(p=e.persistence)||void 0===p?void 0:p.refreshTokenKey)&&void 0!==k?k:"refresh_token",tokenTypeKey:null!==(T=null===(y=e.persistence)||void 0===y?void 0:y.tokenTypeKey)&&void 0!==T?T:"token_type",dataKey:null!==(E=null===(g=e.persistence)||void 0===g?void 0:g.dataKey)&&void 0!==E?E:"data",expiryKey:null!==(A=null===(b=e.persistence)||void 0===b?void 0:b.expiryKey)&&void 0!==A?A:"expires_at"};return{axios:e.axios,loginUrl:e.loginUrl,logoutUrl:e.logoutUrl,refreshUrl:e.refreshUrl,dataUrl:e.dataUrl,authCheckUrl:e.authCheckUrl,extractTokens:null!==(m=e.extractTokens)&&void 0!==m?m:u,extractData:(_=e.extractData,"string"==typeof _?function(e){var t;return null!==(t=e[_])&&void 0!==t?t:null}:_),formatAuthHeader:null!==(x=e.formatAuthHeader)&&void 0!==x?x:function(e,t){return void 0===t&&(t="Bearer"),"".concat(t," ").concat(e)},autoRefresh:null===(N=e.autoRefresh)||void 0===N||N,refreshThreshold:null!==(w=e.refreshThreshold)&&void 0!==w?w:3e5,cookieAuth:O,persistence:K,onError:e.onError,onLogin:e.onLogin,onLogout:e.onLogout}};function u(e){if(e.access_token)return{accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:e.expires_in?Date.now()+1e3*e.expires_in:void 0,tokenType:e.token_type||"Bearer"};var t=e.token||e.auth_token;if(t)return{accessToken:t,tokenType:"Bearer"};throw new Error("No token found in response. Provide extractTokens or ensure response contains access_token/token field.")}var s=o(287),l=["post","put","patch","delete"],c=function(e){var t=e.persistence,r=e.cookieAuth;!function(e){var t;if(!(null===(t=e.cookieAuth)||void 0===t?void 0:t.csrf.enabled))return null;var r=e.cookieAuth.csrf,n=r.headerName,o=r.getToken;e.axios.interceptors.request.use(function(e){var t,r=null===(t=e.method)||void 0===t?void 0:t.toLowerCase();if(r&&l.includes(r)){var a=o();a&&(e.headers[n]=a)}return e})}(e);var n=function(){if(null==r?void 0:r.enabled)return null;if(!t.enabled)return null;try{var e=t.storage.getItem(t.tokenKey);if(!e)return null;var n=t.storage.getItem(t.refreshTokenKey),o=t.storage.getItem(t.expiryKey),a=o?parseInt(o,10):void 0,i=t.storage.getItem(t.tokenTypeKey);return{accessToken:e,refreshToken:n||void 0,expiresAt:a&&!isNaN(a)?a:void 0,tokenType:i||"Bearer"}}catch(e){return null}}(),o=function(){if(!t.enabled)return null;try{var e=t.storage.getItem(t.dataKey);return e?JSON.parse(e):null}catch(e){return null}}(),a=(null==r?void 0:r.enabled)?null:!!(null==n?void 0:n.accessToken),i=(0,s.create)(function(i,u){return{tokens:n,data:o,isAuthenticated:a,setTokens:function(n){var o;if(i({tokens:n,isAuthenticated:!0}),!(null==r?void 0:r.enabled)&&t.enabled)try{t.storage.setItem(t.tokenKey,n.accessToken),n.refreshToken?t.storage.setItem(t.refreshTokenKey,n.refreshToken):t.storage.removeItem(t.refreshTokenKey),n.expiresAt?t.storage.setItem(t.expiryKey,n.expiresAt.toString()):t.storage.removeItem(t.expiryKey),n.tokenType?t.storage.setItem(t.tokenTypeKey,n.tokenType):t.storage.removeItem(t.tokenTypeKey)}catch(t){null===(o=e.onError)||void 0===o||o.call(e,t)}},setBearerToken:function(e){u().setTokens({accessToken:e,tokenType:"Bearer"})},setAuthenticated:function(e){i({isAuthenticated:e})},setData:function(r){var n;if(i({data:r}),t.enabled)try{t.storage.setItem(t.dataKey,JSON.stringify(r))}catch(t){null===(n=e.onError)||void 0===n||n.call(e,t)}},reset:function(){var r;if(i({data:null,tokens:null,isAuthenticated:!1}),t.enabled)try{t.storage.removeItem(t.tokenKey),t.storage.removeItem(t.refreshTokenKey),t.storage.removeItem(t.tokenTypeKey),t.storage.removeItem(t.dataKey),t.storage.removeItem(t.expiryKey)}catch(t){null===(r=e.onError)||void 0===r||r.call(e,t)}},isTokenExpired:function(){var e=u().tokens;return!!(null==e?void 0:e.expiresAt)&&Date.now()>=e.expiresAt}}});return Object.assign(i,{config:e})};function d(){var e={};return function(t,r){var n=String(t);if(!e[n]){var o=i(r);e[n]=c(o)}return e[n]}}var f=o(155),v=function(e,t,r,n){return new(r||(r=Promise))(function(o,a){function i(e){try{s(n.next(e))}catch(e){a(e)}}function u(e){try{s(n.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(i,u)}s((n=n.apply(e,t||[])).next())})},h=function(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(u){return function(s){return function(u){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,u[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&u[0]?n.return:u[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,u[1])).done)return o;switch(n=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,n=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){i=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]<o[3])){i.label=u[1];break}if(6===u[0]&&i.label<o[1]){i.label=o[1],o=u;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(u);break}o[2]&&i.ops.pop(),i.trys.pop();continue}u=t.call(e,i)}catch(e){u=[6,e],n=0}finally{r=o=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}([u,s])}}},p=new WeakMap;function k(e){var t=this,r=e(),n=r.setTokens,o=r.setAuthenticated,a=r.setData,i=r.reset,u=r.tokens,s=r.data,l=r.isAuthenticated,c=r.isTokenExpired,d=e.config,k=(0,f.useCallback)(function(e,t){var r;(null===(r=d.cookieAuth)||void 0===r?void 0:r.enabled)||(e?d.axios.defaults.headers.common.Authorization=d.formatAuthHeader(e,t):delete d.axios.defaults.headers.common.Authorization)},[d]),T=(0,f.useCallback)(function(){return v(t,void 0,void 0,function(){var e,t,r,o,a,s;return h(this,function(l){switch(l.label){case 0:if(!d.refreshUrl)return[2,!1];l.label=1;case 1:return l.trys.push([1,5,,6]),(null===(a=d.cookieAuth)||void 0===a?void 0:a.enabled)?(e=y(d),[4,d.axios.post(d.refreshUrl,{},{headers:e})]):[3,3];case 2:return l.sent(),[2,!0];case 3:return(null==u?void 0:u.refreshToken)?[4,d.axios.post(d.refreshUrl,{refresh_token:u.refreshToken})]:[2,!1];case 4:return t=l.sent(),r=d.extractTokens(t.data),n(r),k(r.accessToken,r.tokenType),[2,!0];case 5:return o=l.sent(),i(),k(),null===(s=d.onError)||void 0===s||s.call(d,o),[2,!1];case 6:return[2]}})})},[u,d,n,i,k]),g=(0,f.useCallback)(function(){return v(t,void 0,void 0,function(){var t,r,n,u,s,l=this;return h(this,function(c){return t=d.authCheckUrl,(null===(s=d.cookieAuth)||void 0===s?void 0:s.enabled)&&t?(r=p.get(e))?[2,r]:(n=function(){return v(l,void 0,void 0,function(){var r,n,u,s,l,c;return h(this,function(f){switch(f.label){case 0:return f.trys.push([0,6,7,8]),r=y(d),[4,d.axios.get(t,{headers:r})];case 1:return(n=f.sent()).data.authenticated?(o(!0),(u=null===(l=d.extractData)||void 0===l?void 0:l.call(d,n.data))?(a(u),[3,4]):[3,2]):[3,5];case 2:return d.dataUrl?[4,E()]:[3,4];case 3:f.sent(),f.label=4;case 4:return[2,!0];case 5:return i(),[2,!1];case 6:return s=f.sent(),i(),null===(c=d.onError)||void 0===c||c.call(d,s),[2,!1];case 7:return p.delete(e),[7];case 8:return[2]}})})},u=n(),p.set(e,u),[2,u]):[2,!1]})})},[e,d,o,a]),E=(0,f.useCallback)(function(){return v(t,void 0,void 0,function(){var e,t,r;return h(this,function(n){switch(n.label){case 0:if(!d.dataUrl)return[2];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,d.axios.get(d.dataUrl)];case 2:return e=n.sent(),a(e.data),[3,4];case 3:throw t=n.sent(),i(),k(),null===(r=d.onError)||void 0===r||r.call(d,t),t;case 4:return[2]}})})},[d,a,i,k]);return(0,f.useEffect)(function(){var e;if(null===(e=d.cookieAuth)||void 0===e?void 0:e.enabled)null===l&&g();else if(null==u?void 0:u.accessToken){if(k(u.accessToken,u.tokenType),c())return void(u.refreshToken&&d.autoRefresh?T():i());if(u.expiresAt&&u.refreshToken&&d.autoRefresh){var t=u.expiresAt-Date.now(),r=Math.max(t-d.refreshThreshold,0),n=setTimeout(T,r);return function(){return clearTimeout(n)}}!s&&d.dataUrl&&E().catch(function(){})}},[u,s,l,d,c,T,g,E,k,i]),{login:function(r,u){return v(t,void 0,void 0,function(){var t,s,l,c,f,v,p,T,g,b,A,m;return h(this,function(h){switch(h.label){case 0:return h.trys.push([0,5,,6]),t=(null===(p=d.cookieAuth)||void 0===p?void 0:p.enabled)?y(d):{},[4,d.axios.post(d.loginUrl,r,{headers:t})];case 1:return s=h.sent(),(null===(T=d.cookieAuth)||void 0===T?void 0:T.enabled)?o(!0):(l=d.extractTokens(s.data),n(l),k(l.accessToken,l.tokenType)),(c=null===(g=d.extractData)||void 0===g?void 0:g.call(d,s.data))?(a(c),null===(b=d.onLogin)||void 0===b||b.call(d,c),[3,4]):[3,2];case 2:return d.dataUrl?[4,E()]:[3,4];case 3:h.sent(),(f=e.getState().data)&&(null===(A=d.onLogin)||void 0===A||A.call(d,f)),h.label=4;case 4:return null==u||u(),[3,6];case 5:throw v=h.sent(),i(),k(),null===(m=d.onError)||void 0===m||m.call(d,v),v;case 6:return[2]}})})},logout:function(){return v(t,void 0,void 0,function(){var e,t,r,n,o;return h(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,4,5]),d.logoutUrl?(e=(null===(r=d.cookieAuth)||void 0===r?void 0:r.enabled)?y(d):{},[4,d.axios.post(d.logoutUrl,{},{headers:e})]):[3,2];case 1:a.sent(),a.label=2;case 2:return[3,5];case 3:return t=a.sent(),null===(n=d.onError)||void 0===n||n.call(d,t),[3,5];case 4:return i(),k(),null===(o=d.onLogout)||void 0===o||o.call(d),[7];case 5:return[2]}})})},refresh:T,checkAuth:g,fetchData:E}}function y(e){var t,r,n={};if(null===(r=null===(t=e.cookieAuth)||void 0===t?void 0:t.csrf)||void 0===r?void 0:r.enabled){var o=e.cookieAuth.csrf.getToken();o&&(n[e.cookieAuth.csrf.headerName]=o)}return n}var T,g,E=(T=function(e,t){return T=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},T(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}T(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});!function(e){e.INVALID_CREDENTIALS="INVALID_CREDENTIALS",e.TOKEN_EXPIRED="TOKEN_EXPIRED",e.TOKEN_INVALID="TOKEN_INVALID",e.REFRESH_FAILED="REFRESH_FAILED",e.NETWORK_ERROR="NETWORK_ERROR",e.USER_NOT_FOUND="USER_NOT_FOUND",e.UNAUTHORIZED="UNAUTHORIZED",e.CSRF_TOKEN_MISSING="CSRF_TOKEN_MISSING",e.FORBIDDEN="FORBIDDEN",e.UNKNOWN="UNKNOWN"}(g||(g={}));var b=function(e){function t(r,n,o){var a=e.call(this,o||r)||this;return a.code=r,a.originalError=n,a.name="AuthError",Error.captureStackTrace&&Error.captureStackTrace(a,t),a}return E(t,e),t.prototype.toJSON=function(){return{code:this.code,message:this.message,name:this.name}},t.isAuthError=function(e){return e instanceof t},t}(Error);function A(e){var t,r,n,o,a,i;if(b.isAuthError(e))return e;if(e.response){var u=e.response.status,s=e.response.data;switch(u){case 401:return(null===(t=null==s?void 0:s.detail)||void 0===t?void 0:t.toLowerCase().includes("expired"))||(null===(r=null==s?void 0:s.message)||void 0===r?void 0:r.toLowerCase().includes("expired"))?new b(g.TOKEN_EXPIRED,e,"Token has expired"):(null===(n=null==s?void 0:s.detail)||void 0===n?void 0:n.toLowerCase().includes("invalid"))||(null===(o=null==s?void 0:s.detail)||void 0===o?void 0:o.toLowerCase().includes("credentials"))?new b(g.INVALID_CREDENTIALS,e,"Invalid credentials"):new b(g.UNAUTHORIZED,e,"Unauthorized");case 403:return(null===(a=null==s?void 0:s.detail)||void 0===a?void 0:a.toLowerCase().includes("csrf"))?new b(g.CSRF_TOKEN_MISSING,e,"CSRF token missing or invalid"):new b(g.FORBIDDEN,e,"Access forbidden");case 404:return(null===(i=null==s?void 0:s.detail)||void 0===i?void 0:i.toLowerCase().includes("user"))?new b(g.USER_NOT_FOUND,e,"User not found"):new b(g.UNKNOWN,e,"Resource not found");default:return new b(g.UNKNOWN,e,"HTTP ".concat(u," error"))}}return e.request?new b(g.NETWORK_ERROR,e,"Network error - no response received"):new b(g.UNKNOWN,e,e.message||"Unknown error")}return a})());
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","mappings":"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","sources":["webpack://@jasperoosthoek/zustand-auth-registry/webpack/universalModuleDefinition","webpack://@jasperoosthoek/zustand-auth-registry/external umd \"react\"","webpack://@jasperoosthoek/zustand-auth-registry/external umd \"zustand\"","webpack://@jasperoosthoek/zustand-auth-registry/webpack/bootstrap","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/define property getters","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/hasOwnProperty shorthand","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/make namespace object","webpack://@jasperoosthoek/zustand-auth-registry/./src/authConfig.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/authStore.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/createAuthRegistry.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/useAuth.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/errors.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"zustand\"), require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"zustand\", \"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"@jasperoosthoek/zustand-auth-registry\"] = factory(require(\"zustand\"), require(\"react\"));\n\telse\n\t\troot[\"@jasperoosthoek/zustand-auth-registry\"] = factory(root[\"zustand\"], root[\"react\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__287__, __WEBPACK_EXTERNAL_MODULE__155__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__155__;","module.exports = __WEBPACK_EXTERNAL_MODULE__287__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { AxiosInstance } from 'axios';\n\n// Token data structure\nexport type TokenData = {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n};\n\nexport type AuthConfig<D> = {\n axios: AxiosInstance;\n\n // Endpoints\n loginUrl: string;\n logoutUrl?: string;\n refreshUrl?: string;\n dataUrl?: string;\n authCheckUrl?: string; // For cookie auth verification\n\n // Token extraction from login response\n extractTokens?: (data: any) => TokenData;\n\n // Data extraction from responses (login, checkAuth)\n // Can be a function or a string key (e.g., \"user\" extracts data.user)\n extractData?: ((data: any) => D | null) | string;\n\n // Auth header format (default: \"Bearer {token}\")\n formatAuthHeader?: (token: string, tokenType?: string) => string;\n\n // Auto-refresh settings\n autoRefresh?: boolean;\n refreshThreshold?: number; // ms before expiry to refresh (default: 5 min)\n\n // Cookie-based authentication (alternative to localStorage)\n cookieAuth?: {\n enabled: boolean;\n csrf?: {\n enabled: boolean;\n headerName?: string; // Default: 'X-CSRFToken'\n // Option 1: Provide cookie name (web only, uses document.cookie)\n cookieName?: string; // Default: 'csrftoken'\n // Option 2: Provide custom token getter (platform-agnostic)\n getToken?: () => string | null;\n };\n };\n\n // Token persistence (localStorage)\n persistence?: {\n enabled: boolean;\n storage?: Storage;\n tokenKey?: string;\n refreshTokenKey?: string;\n dataKey?: string;\n expiryKey?: string;\n };\n\n // Callbacks\n onError?: (error: any) => void;\n onLogin?: (data: D) => void;\n onLogout?: () => void;\n};\n\nexport type ValidatedAuthConfig<D> = {\n axios: AxiosInstance;\n\n // Endpoints\n loginUrl: string;\n logoutUrl?: string;\n refreshUrl?: string;\n dataUrl?: string;\n authCheckUrl?: string;\n\n // Extraction functions\n extractTokens: (data: any) => TokenData;\n extractData?: (data: any) => D | null;\n\n // Auth header format\n formatAuthHeader: (token: string, tokenType?: string) => string;\n\n // Auto-refresh\n autoRefresh: boolean;\n refreshThreshold: number;\n\n // Cookie auth\n cookieAuth?: {\n enabled: boolean;\n csrf: {\n enabled: boolean;\n headerName: string;\n getToken: () => string | null;\n };\n };\n\n // Persistence\n persistence: {\n enabled: boolean;\n storage: Storage;\n tokenKey: string;\n refreshTokenKey: string;\n dataKey: string;\n expiryKey: string;\n };\n\n // Callbacks\n onError?: (error: any) => void;\n onLogin?: (data: D) => void;\n onLogout?: () => void;\n};\n\nexport const validateAuthConfig = <D>(config: AuthConfig<D>): ValidatedAuthConfig<D> => {\n if (!config.axios) {\n throw new Error('AuthConfig: axios instance is required');\n }\n\n if (!config.loginUrl) {\n throw new Error('AuthConfig: loginUrl is required');\n }\n\n // Cookie auth config\n const cookieAuth = config.cookieAuth?.enabled ? {\n enabled: true,\n csrf: {\n enabled: config.cookieAuth.csrf?.enabled ?? false,\n headerName: config.cookieAuth.csrf?.headerName || 'X-CSRFToken',\n getToken: config.cookieAuth.csrf?.getToken ?? createCookieTokenGetter(\n config.cookieAuth.csrf?.cookieName || 'csrftoken'\n ),\n },\n } : undefined;\n\n // Persistence config (disabled by default)\n const persistence = {\n enabled: config.persistence?.enabled ?? false,\n storage: config.persistence?.storage ??\n (typeof window !== 'undefined' && window.localStorage ? window.localStorage : {} as Storage),\n tokenKey: config.persistence?.tokenKey ?? 'token',\n refreshTokenKey: config.persistence?.refreshTokenKey ?? 'refresh_token',\n dataKey: config.persistence?.dataKey ?? 'data',\n expiryKey: config.persistence?.expiryKey ?? 'expires_at',\n };\n\n return {\n axios: config.axios,\n loginUrl: config.loginUrl,\n logoutUrl: config.logoutUrl,\n refreshUrl: config.refreshUrl,\n dataUrl: config.dataUrl,\n authCheckUrl: config.authCheckUrl,\n extractTokens: config.extractTokens ?? defaultExtractTokens,\n extractData: normalizeExtractData(config.extractData),\n formatAuthHeader: config.formatAuthHeader ??\n ((token: string, tokenType: string = 'Bearer') => `${tokenType} ${token}`),\n autoRefresh: config.autoRefresh ?? true,\n refreshThreshold: config.refreshThreshold ?? 300000, // 5 minutes\n cookieAuth,\n persistence,\n onError: config.onError,\n onLogin: config.onLogin,\n onLogout: config.onLogout,\n };\n};\n\n// Default token extraction - handles common response formats\nfunction defaultExtractTokens(data: any): TokenData {\n // OAuth 2.0 format: { access_token, refresh_token, expires_in, token_type }\n if (data.access_token) {\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: data.expires_in ? Date.now() + (data.expires_in * 1000) : undefined,\n tokenType: data.token_type || 'Bearer',\n };\n }\n\n // Simple format: { token } or { auth_token }\n const token = data.token || data.auth_token;\n if (token) {\n return {\n accessToken: token,\n tokenType: 'Bearer',\n };\n }\n\n throw new Error('No token found in response. Provide extractTokens or ensure response contains access_token/token field.');\n}\n\n// Normalize extractData: string becomes key accessor, function passed through\nfunction normalizeExtractData<D>(\n extractData?: ((data: any) => D | null) | string\n): ((data: any) => D | null) | undefined {\n if (typeof extractData === 'string') {\n return (data: any) => data[extractData] ?? null;\n }\n return extractData;\n}\n\n// Create a cookie-based CSRF token getter (web only)\nfunction createCookieTokenGetter(cookieName: string): () => string | null {\n return () => {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`${cookieName}=([^;]+)`));\n return match ? match[1] : null;\n };\n}\n","import { create, StoreApi, UseBoundStore } from 'zustand';\nimport { ValidatedAuthConfig, TokenData } from './authConfig';\n\nexport type AuthState<D> = {\n isAuthenticated: boolean | null; // null = not checked yet (cookie mode)\n data: D | null;\n tokens: TokenData | null; // null in cookie mode or when logged out\n\n // Methods\n setTokens: (tokens: TokenData) => void;\n setBearerToken: (token: string) => void; // Convenience for simple Bearer token auth\n setAuthenticated: (authenticated: boolean) => void; // For cookie mode\n setData: (data: D) => void;\n reset: () => void;\n isTokenExpired: () => boolean;\n};\n\nexport type AuthStore<D> = UseBoundStore<StoreApi<AuthState<D>>> & {\n config: ValidatedAuthConfig<D>;\n};\n\n// Methods that modify state (require CSRF protection)\nconst CSRF_METHODS = ['post', 'put', 'patch', 'delete'];\n\nconst setupCsrfInterceptor = <D>(config: ValidatedAuthConfig<D>): number | null => {\n if (!config.cookieAuth?.csrf.enabled) {\n return null;\n }\n\n const { headerName, getToken } = config.cookieAuth.csrf;\n\n return config.axios.interceptors.request.use((requestConfig) => {\n const method = requestConfig.method?.toLowerCase();\n if (method && CSRF_METHODS.includes(method)) {\n const csrfToken = getToken();\n if (csrfToken) {\n requestConfig.headers[headerName] = csrfToken;\n }\n }\n return requestConfig;\n });\n};\n\nexport const createAuthStore = <D>(config: ValidatedAuthConfig<D>): AuthStore<D> => {\n const { persistence, cookieAuth } = config;\n\n // Set up CSRF interceptor if enabled\n setupCsrfInterceptor(config);\n\n const getStoredTokens = (): TokenData | null => {\n // Cookie mode: No client-side tokens\n if (cookieAuth?.enabled) {\n return null;\n }\n\n // Token mode: Read from storage\n if (!persistence.enabled) return null;\n try {\n const accessToken = persistence.storage.getItem(persistence.tokenKey);\n if (!accessToken) return null;\n\n const refreshToken = persistence.storage.getItem(persistence.refreshTokenKey);\n const expiryString = persistence.storage.getItem(persistence.expiryKey);\n const expiresAt = expiryString ? parseInt(expiryString, 10) : undefined;\n\n return {\n accessToken,\n refreshToken: refreshToken || undefined,\n expiresAt: expiresAt && !isNaN(expiresAt) ? expiresAt : undefined,\n tokenType: 'Bearer',\n };\n } catch {\n return null;\n }\n };\n\n const getStoredData = (): D | null => {\n if (!persistence.enabled) return null;\n try {\n const dataString = persistence.storage.getItem(persistence.dataKey);\n return dataString ? (JSON.parse(dataString) as D) : null;\n } catch {\n return null;\n }\n };\n\n const initialTokens = getStoredTokens();\n const initialData = getStoredData();\n\n // Cookie mode: null (unknown until checkAuth)\n // Token mode: true/false based on token presence\n const initialIsAuthenticated = cookieAuth?.enabled\n ? null\n : !!initialTokens?.accessToken;\n\n const store = create<AuthState<D>>((set, get) => ({\n tokens: initialTokens,\n data: initialData,\n isAuthenticated: initialIsAuthenticated,\n\n setTokens: (tokens: TokenData) => {\n set({ tokens, isAuthenticated: true });\n\n // Cookie mode: No localStorage persistence for tokens\n if (cookieAuth?.enabled) {\n return;\n }\n\n // Token mode: Persist to storage\n if (persistence.enabled) {\n try {\n persistence.storage.setItem(persistence.tokenKey, tokens.accessToken);\n\n if (tokens.refreshToken) {\n persistence.storage.setItem(persistence.refreshTokenKey, tokens.refreshToken);\n } else {\n persistence.storage.removeItem(persistence.refreshTokenKey);\n }\n\n if (tokens.expiresAt) {\n persistence.storage.setItem(persistence.expiryKey, tokens.expiresAt.toString());\n } else {\n persistence.storage.removeItem(persistence.expiryKey);\n }\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n setBearerToken: (token: string) => {\n get().setTokens({ accessToken: token, tokenType: 'Bearer' });\n },\n\n setAuthenticated: (authenticated: boolean) => {\n set({ isAuthenticated: authenticated });\n },\n\n setData: (data: D) => {\n set({ data });\n\n if (persistence.enabled) {\n try {\n persistence.storage.setItem(persistence.dataKey, JSON.stringify(data));\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n reset: () => {\n set({ data: null, tokens: null, isAuthenticated: false });\n\n if (persistence.enabled) {\n try {\n persistence.storage.removeItem(persistence.tokenKey);\n persistence.storage.removeItem(persistence.refreshTokenKey);\n persistence.storage.removeItem(persistence.dataKey);\n persistence.storage.removeItem(persistence.expiryKey);\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n isTokenExpired: () => {\n const tokens = get().tokens;\n if (!tokens?.expiresAt) return false;\n return Date.now() >= tokens.expiresAt;\n },\n }));\n\n return Object.assign(store, { config });\n};\n","import { AuthConfig, validateAuthConfig } from './authConfig';\nimport { createAuthStore, AuthStore } from './authStore';\n\nexport function createAuthRegistry<AuthModels extends Record<string, any>>() {\n const registry: Record<string, AuthStore<any>> = {};\n\n function getAuthStore<K extends keyof AuthModels>(\n key: K,\n config: AuthConfig<AuthModels[K]>\n ): AuthStore<AuthModels[K]> {\n const stringKey = String(key);\n \n if (!registry[stringKey]) {\n const validatedConfig = validateAuthConfig(config);\n registry[stringKey] = createAuthStore(validatedConfig);\n }\n \n return registry[stringKey];\n }\n\n return getAuthStore;\n}\n","import { useEffect, useCallback } from 'react';\nimport { AuthStore } from './authStore';\n\n// Promise deduplication: prevents multiple concurrent checkAuth calls per store\nconst pendingCheckAuth = new WeakMap<AuthStore<any>, Promise<boolean>>();\n\nexport function useAuth<D>(store: AuthStore<D>) {\n const { setTokens, setAuthenticated, setData, reset, tokens, data, isAuthenticated, isTokenExpired } = store();\n const config = store.config;\n\n // Set axios Authorization header (token mode only)\n // Note: CSRF is handled by interceptor in authStore.ts\n const setAxiosAuth = useCallback((token?: string, tokenType?: string) => {\n if (config.cookieAuth?.enabled) {\n // Cookie mode: CSRF handled by interceptor, no Authorization header needed\n return;\n }\n\n // Token mode: Set Authorization header\n if (token) {\n config.axios.defaults.headers.common['Authorization'] = config.formatAuthHeader(token, tokenType);\n } else {\n delete config.axios.defaults.headers.common['Authorization'];\n }\n }, [config]);\n\n // Refresh tokens\n const refresh = useCallback(async (): Promise<boolean> => {\n if (!config.refreshUrl) return false;\n\n try {\n // Cookie mode: Just call refresh endpoint, server handles cookie\n if (config.cookieAuth?.enabled) {\n const headers = getCsrfHeaders(config);\n await config.axios.post(config.refreshUrl, {}, { headers });\n return true;\n }\n\n // Token mode: Send refresh token, get new tokens\n if (!tokens?.refreshToken) return false;\n\n const response = await config.axios.post(config.refreshUrl, {\n refresh_token: tokens.refreshToken,\n });\n\n const newTokens = config.extractTokens(response.data);\n setTokens(newTokens);\n setAxiosAuth(newTokens.accessToken, newTokens.tokenType);\n return true;\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n return false;\n }\n }, [tokens, config, setTokens, reset, setAxiosAuth]);\n\n // Check authentication (cookie mode) - with promise deduplication\n const checkAuth = useCallback(async (): Promise<boolean> => {\n const authCheckUrl = config.authCheckUrl;\n if (!config.cookieAuth?.enabled || !authCheckUrl) {\n return false;\n }\n\n // Return existing promise if check is already in progress\n const pending = pendingCheckAuth.get(store);\n if (pending) {\n return pending;\n }\n\n const doCheck = async (): Promise<boolean> => {\n try {\n const headers = getCsrfHeaders(config);\n const response = await config.axios.get(authCheckUrl, { headers });\n\n if (response.data.authenticated) {\n setAuthenticated(true);\n\n const extractedData = config.extractData?.(response.data);\n if (extractedData) {\n setData(extractedData);\n } else if (config.dataUrl) {\n await fetchData();\n }\n\n return true;\n }\n\n reset();\n return false;\n } catch (error) {\n reset();\n config.onError?.(error);\n return false;\n } finally {\n pendingCheckAuth.delete(store);\n }\n };\n\n const promise = doCheck();\n pendingCheckAuth.set(store, promise);\n return promise;\n }, [store, config, setAuthenticated, setData]);\n\n // Fetch data from dataUrl\n const fetchData = useCallback(async () => {\n if (!config.dataUrl) return;\n\n try {\n const res = await config.axios.get<D>(config.dataUrl);\n setData(res.data);\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n throw error;\n }\n }, [config, setData, reset, setAxiosAuth]);\n\n // Login\n const login = async (credentials: Record<string, string>, callback?: () => void) => {\n try {\n const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};\n const res = await config.axios.post(config.loginUrl, credentials, { headers });\n\n if (config.cookieAuth?.enabled) {\n // Cookie mode: Server sets httpOnly cookie, just mark as authenticated\n setAuthenticated(true);\n } else {\n // Token mode: Extract and store tokens\n const newTokens = config.extractTokens(res.data);\n setTokens(newTokens);\n setAxiosAuth(newTokens.accessToken, newTokens.tokenType);\n }\n\n // Extract data from response\n const extractedData = config.extractData?.(res.data);\n if (extractedData) {\n setData(extractedData);\n config.onLogin?.(extractedData);\n } else if (config.dataUrl) {\n await fetchData();\n const currentData = store.getState().data;\n if (currentData) config.onLogin?.(currentData);\n }\n\n callback?.();\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n throw error;\n }\n };\n\n // Logout\n const logout = async () => {\n try {\n if (config.logoutUrl) {\n const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};\n await config.axios.post(config.logoutUrl, {}, { headers });\n }\n } catch (error) {\n config.onError?.(error);\n } finally {\n reset();\n setAxiosAuth();\n config.onLogout?.();\n }\n };\n\n // Auto-setup on mount\n useEffect(() => {\n // Cookie mode: Check authentication if not yet determined\n if (config.cookieAuth?.enabled) {\n if (isAuthenticated === null) {\n checkAuth();\n }\n return;\n }\n\n // Token mode: Setup headers and auto-refresh\n if (tokens?.accessToken) {\n setAxiosAuth(tokens.accessToken, tokens.tokenType);\n\n // Check if expired\n if (isTokenExpired()) {\n if (tokens.refreshToken && config.autoRefresh) {\n refresh();\n } else {\n reset();\n }\n return;\n }\n\n // Setup auto-refresh timer\n if (tokens.expiresAt && tokens.refreshToken && config.autoRefresh) {\n const timeUntilExpiry = tokens.expiresAt - Date.now();\n const refreshTime = Math.max(timeUntilExpiry - config.refreshThreshold, 0);\n\n const timer = setTimeout(refresh, refreshTime);\n return () => clearTimeout(timer);\n }\n\n // Fetch data if missing\n if (!data && config.dataUrl) {\n fetchData().catch(() => {});\n }\n }\n }, [tokens, data, isAuthenticated, config, isTokenExpired, refresh, checkAuth, fetchData, setAxiosAuth, reset]);\n\n return { login, logout, refresh, checkAuth, fetchData };\n}\n\n// Helper: Get CSRF headers if enabled (uses getToken from config)\nfunction getCsrfHeaders(config: any): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.cookieAuth?.csrf?.enabled) {\n const csrfToken = config.cookieAuth.csrf.getToken();\n if (csrfToken) {\n headers[config.cookieAuth.csrf.headerName] = csrfToken;\n }\n }\n return headers;\n}\n","/**\n * Authentication error codes\n */\nexport enum AuthErrorCode {\n INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n TOKEN_INVALID = 'TOKEN_INVALID',\n REFRESH_FAILED = 'REFRESH_FAILED',\n NETWORK_ERROR = 'NETWORK_ERROR',\n USER_NOT_FOUND = 'USER_NOT_FOUND',\n UNAUTHORIZED = 'UNAUTHORIZED',\n CSRF_TOKEN_MISSING = 'CSRF_TOKEN_MISSING',\n FORBIDDEN = 'FORBIDDEN',\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * Typed authentication error\n */\nexport class AuthError extends Error {\n constructor(\n public code: AuthErrorCode,\n public originalError?: any,\n message?: string\n ) {\n super(message || code);\n this.name = 'AuthError';\n\n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AuthError);\n }\n }\n\n /**\n * Convert error to JSON (excludes originalError in production)\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n name: this.name,\n };\n }\n\n /**\n * Check if error is an AuthError\n */\n static isAuthError(error: any): error is AuthError {\n return error instanceof AuthError;\n }\n}\n\n/**\n * Create typed AuthError from any error\n */\nexport function createAuthError(error: any): AuthError {\n if (AuthError.isAuthError(error)) {\n return error;\n }\n\n // Axios error\n if (error.response) {\n const status = error.response.status;\n const data = error.response.data;\n\n switch (status) {\n case 401:\n // Check if it's an expired token\n if (data?.detail?.toLowerCase().includes('expired') ||\n data?.message?.toLowerCase().includes('expired')) {\n return new AuthError(AuthErrorCode.TOKEN_EXPIRED, error, 'Token has expired');\n }\n if (data?.detail?.toLowerCase().includes('invalid') ||\n data?.detail?.toLowerCase().includes('credentials')) {\n return new AuthError(AuthErrorCode.INVALID_CREDENTIALS, error, 'Invalid credentials');\n }\n return new AuthError(AuthErrorCode.UNAUTHORIZED, error, 'Unauthorized');\n\n case 403:\n if (data?.detail?.toLowerCase().includes('csrf')) {\n return new AuthError(AuthErrorCode.CSRF_TOKEN_MISSING, error, 'CSRF token missing or invalid');\n }\n return new AuthError(AuthErrorCode.FORBIDDEN, error, 'Access forbidden');\n\n case 404:\n if (data?.detail?.toLowerCase().includes('user')) {\n return new AuthError(AuthErrorCode.USER_NOT_FOUND, error, 'User not found');\n }\n return new AuthError(AuthErrorCode.UNKNOWN, error, 'Resource not found');\n\n default:\n return new AuthError(AuthErrorCode.UNKNOWN, error, `HTTP ${status} error`);\n }\n }\n\n // Network error (no response received)\n if (error.request) {\n return new AuthError(AuthErrorCode.NETWORK_ERROR, error, 'Network error - no response received');\n }\n\n // Other errors\n return new AuthError(AuthErrorCode.UNKNOWN, error, error.message || 'Unknown error');\n}\n"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__287__","__WEBPACK_EXTERNAL_MODULE__155__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","value","validateAuthConfig","config","axios","Error","loginUrl","cookieName","extractData","cookieAuth","enabled","csrf","headerName","getToken","document","match","cookie","RegExp","persistence","storage","window","localStorage","tokenKey","refreshTokenKey","dataKey","expiryKey","logoutUrl","refreshUrl","dataUrl","authCheckUrl","extractTokens","defaultExtractTokens","data","formatAuthHeader","token","tokenType","autoRefresh","refreshThreshold","onError","onLogin","onLogout","access_token","accessToken","refreshToken","refresh_token","expiresAt","expires_in","Date","now","token_type","auth_token","CSRF_METHODS","createAuthStore","interceptors","request","use","requestConfig","method","toLowerCase","includes","csrfToken","headers","setupCsrfInterceptor","initialTokens","getItem","expiryString","parseInt","isNaN","getStoredTokens","initialData","dataString","JSON","parse","getStoredData","initialIsAuthenticated","store","create","set","tokens","isAuthenticated","setTokens","setItem","removeItem","toString","error","setBearerToken","setAuthenticated","authenticated","setData","stringify","reset","isTokenExpired","assign","createAuthRegistry","registry","stringKey","String","validatedConfig","pendingCheckAuth","WeakMap","useAuth","setAxiosAuth","useCallback","defaults","common","refresh","getCsrfHeaders","post","response","newTokens","checkAuth","pending","doCheck","extractedData","fetchData","delete","promise","res","useEffect","timeUntilExpiry","refreshTime","Math","max","setTimeout","clearTimeout","catch","login","credentials","callback","currentData","getState","logout","AuthErrorCode","code","originalError","message","name","captureStackTrace","AuthError","toJSON","isAuthError","createAuthError","status","detail","TOKEN_EXPIRED","INVALID_CREDENTIALS","UNAUTHORIZED","CSRF_TOKEN_MISSING","FORBIDDEN","USER_NOT_FOUND","UNKNOWN","NETWORK_ERROR"],"sourceRoot":""}
1
+ {"version":3,"file":"index.js","mappings":"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","sources":["webpack://@jasperoosthoek/zustand-auth-registry/webpack/universalModuleDefinition","webpack://@jasperoosthoek/zustand-auth-registry/external umd \"react\"","webpack://@jasperoosthoek/zustand-auth-registry/external umd \"zustand\"","webpack://@jasperoosthoek/zustand-auth-registry/webpack/bootstrap","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/define property getters","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/hasOwnProperty shorthand","webpack://@jasperoosthoek/zustand-auth-registry/webpack/runtime/make namespace object","webpack://@jasperoosthoek/zustand-auth-registry/./src/authConfig.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/authStore.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/createAuthRegistry.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/useAuth.ts","webpack://@jasperoosthoek/zustand-auth-registry/./src/errors.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"zustand\"), require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"zustand\", \"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"@jasperoosthoek/zustand-auth-registry\"] = factory(require(\"zustand\"), require(\"react\"));\n\telse\n\t\troot[\"@jasperoosthoek/zustand-auth-registry\"] = factory(root[\"zustand\"], root[\"react\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__287__, __WEBPACK_EXTERNAL_MODULE__155__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__155__;","module.exports = __WEBPACK_EXTERNAL_MODULE__287__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { AxiosInstance } from 'axios';\n\n// Token data structure\nexport type TokenData = {\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n tokenType: string;\n};\n\nexport type AuthConfig<D> = {\n axios: AxiosInstance;\n\n // Endpoints\n loginUrl: string;\n logoutUrl?: string;\n refreshUrl?: string;\n dataUrl?: string;\n authCheckUrl?: string; // For cookie auth verification\n\n // Token extraction from login response\n extractTokens?: (data: any) => TokenData;\n\n // Data extraction from responses (login, checkAuth)\n // Can be a function or a string key (e.g., \"user\" extracts data.user)\n extractData?: ((data: any) => D | null) | string;\n\n // Auth header format (default: \"Bearer {token}\")\n formatAuthHeader?: (token: string, tokenType?: string) => string;\n\n // Auto-refresh settings\n autoRefresh?: boolean;\n refreshThreshold?: number; // ms before expiry to refresh (default: 5 min)\n\n // Cookie-based authentication (alternative to localStorage)\n cookieAuth?: {\n enabled: boolean;\n csrf?: {\n enabled: boolean;\n headerName?: string; // Default: 'X-CSRFToken'\n // Option 1: Provide cookie name (web only, uses document.cookie)\n cookieName?: string; // Default: 'csrftoken'\n // Option 2: Provide custom token getter (platform-agnostic)\n getToken?: () => string | null;\n };\n };\n\n // Token persistence (localStorage)\n persistence?: {\n enabled: boolean;\n storage?: Storage;\n tokenKey?: string;\n refreshTokenKey?: string;\n tokenTypeKey?: string;\n dataKey?: string;\n expiryKey?: string;\n };\n\n // Callbacks\n onError?: (error: any) => void;\n onLogin?: (data: D) => void;\n onLogout?: () => void;\n};\n\nexport type ValidatedAuthConfig<D> = {\n axios: AxiosInstance;\n\n // Endpoints\n loginUrl: string;\n logoutUrl?: string;\n refreshUrl?: string;\n dataUrl?: string;\n authCheckUrl?: string;\n\n // Extraction functions\n extractTokens: (data: any) => TokenData;\n extractData?: (data: any) => D | null;\n\n // Auth header format\n formatAuthHeader: (token: string, tokenType?: string) => string;\n\n // Auto-refresh\n autoRefresh: boolean;\n refreshThreshold: number;\n\n // Cookie auth\n cookieAuth?: {\n enabled: boolean;\n csrf: {\n enabled: boolean;\n headerName: string;\n getToken: () => string | null;\n };\n };\n\n // Persistence\n persistence: {\n enabled: boolean;\n storage: Storage;\n tokenKey: string;\n refreshTokenKey: string;\n tokenTypeKey: string;\n dataKey: string;\n expiryKey: string;\n };\n\n // Callbacks\n onError?: (error: any) => void;\n onLogin?: (data: D) => void;\n onLogout?: () => void;\n};\n\nexport const validateAuthConfig = <D>(config: AuthConfig<D>): ValidatedAuthConfig<D> => {\n if (!config.axios) {\n throw new Error('AuthConfig: axios instance is required');\n }\n\n if (!config.loginUrl) {\n throw new Error('AuthConfig: loginUrl is required');\n }\n\n // Cookie auth config\n const cookieAuth = config.cookieAuth?.enabled ? {\n enabled: true,\n csrf: {\n enabled: config.cookieAuth.csrf?.enabled ?? false,\n headerName: config.cookieAuth.csrf?.headerName || 'X-CSRFToken',\n getToken: config.cookieAuth.csrf?.getToken ?? createCookieTokenGetter(\n config.cookieAuth.csrf?.cookieName || 'csrftoken'\n ),\n },\n } : undefined;\n\n // Persistence config (disabled by default)\n const persistence = {\n enabled: config.persistence?.enabled ?? false,\n storage: config.persistence?.storage ??\n (typeof window !== 'undefined' && window.localStorage ? window.localStorage : {} as Storage),\n tokenKey: config.persistence?.tokenKey ?? 'token',\n refreshTokenKey: config.persistence?.refreshTokenKey ?? 'refresh_token',\n tokenTypeKey: config.persistence?.tokenTypeKey ?? 'token_type',\n dataKey: config.persistence?.dataKey ?? 'data',\n expiryKey: config.persistence?.expiryKey ?? 'expires_at',\n };\n\n return {\n axios: config.axios,\n loginUrl: config.loginUrl,\n logoutUrl: config.logoutUrl,\n refreshUrl: config.refreshUrl,\n dataUrl: config.dataUrl,\n authCheckUrl: config.authCheckUrl,\n extractTokens: config.extractTokens ?? defaultExtractTokens,\n extractData: normalizeExtractData(config.extractData),\n formatAuthHeader: config.formatAuthHeader ??\n ((token: string, tokenType: string = 'Bearer') => `${tokenType} ${token}`),\n autoRefresh: config.autoRefresh ?? true,\n refreshThreshold: config.refreshThreshold ?? 300000, // 5 minutes\n cookieAuth,\n persistence,\n onError: config.onError,\n onLogin: config.onLogin,\n onLogout: config.onLogout,\n };\n};\n\n// Default token extraction - handles common response formats\nfunction defaultExtractTokens(data: any): TokenData {\n // OAuth 2.0 format: { access_token, refresh_token, expires_in, token_type }\n if (data.access_token) {\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: data.expires_in ? Date.now() + (data.expires_in * 1000) : undefined,\n tokenType: data.token_type || 'Bearer',\n };\n }\n\n // Simple format: { token } or { auth_token }\n const token = data.token || data.auth_token;\n if (token) {\n return {\n accessToken: token,\n tokenType: 'Bearer',\n };\n }\n\n throw new Error('No token found in response. Provide extractTokens or ensure response contains access_token/token field.');\n}\n\n// Normalize extractData: string becomes key accessor, function passed through\nfunction normalizeExtractData<D>(\n extractData?: ((data: any) => D | null) | string\n): ((data: any) => D | null) | undefined {\n if (typeof extractData === 'string') {\n return (data: any) => data[extractData] ?? null;\n }\n return extractData;\n}\n\n// Create a cookie-based CSRF token getter (web only)\nfunction createCookieTokenGetter(cookieName: string): () => string | null {\n return () => {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`${cookieName}=([^;]+)`));\n return match ? match[1] : null;\n };\n}\n","import { create, StoreApi, UseBoundStore } from 'zustand';\nimport { ValidatedAuthConfig, TokenData } from './authConfig';\n\nexport type AuthState<D> = {\n isAuthenticated: boolean | null; // null = not checked yet (cookie mode)\n data: D | null;\n tokens: TokenData | null; // null in cookie mode or when logged out\n\n // Methods\n setTokens: (tokens: TokenData) => void;\n setBearerToken: (token: string) => void; // Convenience for simple Bearer token auth\n setAuthenticated: (authenticated: boolean) => void; // For cookie mode\n setData: (data: D) => void;\n reset: () => void;\n isTokenExpired: () => boolean;\n};\n\nexport type AuthStore<D> = UseBoundStore<StoreApi<AuthState<D>>> & {\n config: ValidatedAuthConfig<D>;\n};\n\n// Methods that modify state (require CSRF protection)\nconst CSRF_METHODS = ['post', 'put', 'patch', 'delete'];\n\nconst setupCsrfInterceptor = <D>(config: ValidatedAuthConfig<D>): number | null => {\n if (!config.cookieAuth?.csrf.enabled) {\n return null;\n }\n\n const { headerName, getToken } = config.cookieAuth.csrf;\n\n return config.axios.interceptors.request.use((requestConfig) => {\n const method = requestConfig.method?.toLowerCase();\n if (method && CSRF_METHODS.includes(method)) {\n const csrfToken = getToken();\n if (csrfToken) {\n requestConfig.headers[headerName] = csrfToken;\n }\n }\n return requestConfig;\n });\n};\n\nexport const createAuthStore = <D>(config: ValidatedAuthConfig<D>): AuthStore<D> => {\n const { persistence, cookieAuth } = config;\n\n // Set up CSRF interceptor if enabled\n setupCsrfInterceptor(config);\n\n const getStoredTokens = (): TokenData | null => {\n // Cookie mode: No client-side tokens\n if (cookieAuth?.enabled) {\n return null;\n }\n\n // Token mode: Read from storage\n if (!persistence.enabled) return null;\n try {\n const accessToken = persistence.storage.getItem(persistence.tokenKey);\n if (!accessToken) return null;\n\n const refreshToken = persistence.storage.getItem(persistence.refreshTokenKey);\n const expiryString = persistence.storage.getItem(persistence.expiryKey);\n const expiresAt = expiryString ? parseInt(expiryString, 10) : undefined;\n const tokenType = persistence.storage.getItem(persistence.tokenTypeKey);\n\n return {\n accessToken,\n refreshToken: refreshToken || undefined,\n expiresAt: expiresAt && !isNaN(expiresAt) ? expiresAt : undefined,\n tokenType: tokenType || 'Bearer',\n };\n } catch {\n return null;\n }\n };\n\n const getStoredData = (): D | null => {\n if (!persistence.enabled) return null;\n try {\n const dataString = persistence.storage.getItem(persistence.dataKey);\n return dataString ? (JSON.parse(dataString) as D) : null;\n } catch {\n return null;\n }\n };\n\n const initialTokens = getStoredTokens();\n const initialData = getStoredData();\n\n // Cookie mode: null (unknown until checkAuth)\n // Token mode: true/false based on token presence\n const initialIsAuthenticated = cookieAuth?.enabled\n ? null\n : !!initialTokens?.accessToken;\n\n const store = create<AuthState<D>>((set, get) => ({\n tokens: initialTokens,\n data: initialData,\n isAuthenticated: initialIsAuthenticated,\n\n setTokens: (tokens: TokenData) => {\n set({ tokens, isAuthenticated: true });\n\n // Cookie mode: No localStorage persistence for tokens\n if (cookieAuth?.enabled) {\n return;\n }\n\n // Token mode: Persist to storage\n if (persistence.enabled) {\n try {\n persistence.storage.setItem(persistence.tokenKey, tokens.accessToken);\n\n if (tokens.refreshToken) {\n persistence.storage.setItem(persistence.refreshTokenKey, tokens.refreshToken);\n } else {\n persistence.storage.removeItem(persistence.refreshTokenKey);\n }\n\n if (tokens.expiresAt) {\n persistence.storage.setItem(persistence.expiryKey, tokens.expiresAt.toString());\n } else {\n persistence.storage.removeItem(persistence.expiryKey);\n }\n\n if (tokens.tokenType) {\n persistence.storage.setItem(persistence.tokenTypeKey, tokens.tokenType);\n } else {\n persistence.storage.removeItem(persistence.tokenTypeKey);\n }\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n setBearerToken: (token: string) => {\n get().setTokens({ accessToken: token, tokenType: 'Bearer' });\n },\n\n setAuthenticated: (authenticated: boolean) => {\n set({ isAuthenticated: authenticated });\n },\n\n setData: (data: D) => {\n set({ data });\n\n if (persistence.enabled) {\n try {\n persistence.storage.setItem(persistence.dataKey, JSON.stringify(data));\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n reset: () => {\n set({ data: null, tokens: null, isAuthenticated: false });\n\n if (persistence.enabled) {\n try {\n persistence.storage.removeItem(persistence.tokenKey);\n persistence.storage.removeItem(persistence.refreshTokenKey);\n persistence.storage.removeItem(persistence.tokenTypeKey);\n persistence.storage.removeItem(persistence.dataKey);\n persistence.storage.removeItem(persistence.expiryKey);\n } catch (error) {\n config.onError?.(error);\n }\n }\n },\n\n isTokenExpired: () => {\n const tokens = get().tokens;\n if (!tokens?.expiresAt) return false;\n return Date.now() >= tokens.expiresAt;\n },\n }));\n\n return Object.assign(store, { config });\n};\n","import { AuthConfig, validateAuthConfig } from './authConfig';\nimport { createAuthStore, AuthStore } from './authStore';\n\nexport function createAuthRegistry<AuthModels extends Record<string, any>>() {\n const registry: Record<string, AuthStore<any>> = {};\n\n function getAuthStore<K extends keyof AuthModels>(\n key: K,\n config: AuthConfig<AuthModels[K]>\n ): AuthStore<AuthModels[K]> {\n const stringKey = String(key);\n \n if (!registry[stringKey]) {\n const validatedConfig = validateAuthConfig(config);\n registry[stringKey] = createAuthStore(validatedConfig);\n }\n \n return registry[stringKey];\n }\n\n return getAuthStore;\n}\n","import { useEffect, useCallback } from 'react';\nimport { AuthStore } from './authStore';\n\n// Promise deduplication: prevents multiple concurrent checkAuth calls per store\nconst pendingCheckAuth = new WeakMap<AuthStore<any>, Promise<boolean>>();\n\nexport function useAuth<D>(store: AuthStore<D>) {\n const { setTokens, setAuthenticated, setData, reset, tokens, data, isAuthenticated, isTokenExpired } = store();\n const config = store.config;\n\n // Set axios Authorization header (token mode only)\n // Note: CSRF is handled by interceptor in authStore.ts\n const setAxiosAuth = useCallback((token?: string, tokenType?: string) => {\n if (config.cookieAuth?.enabled) {\n // Cookie mode: CSRF handled by interceptor, no Authorization header needed\n return;\n }\n\n // Token mode: Set Authorization header\n if (token) {\n config.axios.defaults.headers.common['Authorization'] = config.formatAuthHeader(token, tokenType);\n } else {\n delete config.axios.defaults.headers.common['Authorization'];\n }\n }, [config]);\n\n // Refresh tokens\n const refresh = useCallback(async (): Promise<boolean> => {\n if (!config.refreshUrl) return false;\n\n try {\n // Cookie mode: Just call refresh endpoint, server handles cookie\n if (config.cookieAuth?.enabled) {\n const headers = getCsrfHeaders(config);\n await config.axios.post(config.refreshUrl, {}, { headers });\n return true;\n }\n\n // Token mode: Send refresh token, get new tokens\n if (!tokens?.refreshToken) return false;\n\n const response = await config.axios.post(config.refreshUrl, {\n refresh_token: tokens.refreshToken,\n });\n\n const newTokens = config.extractTokens(response.data);\n setTokens(newTokens);\n setAxiosAuth(newTokens.accessToken, newTokens.tokenType);\n return true;\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n return false;\n }\n }, [tokens, config, setTokens, reset, setAxiosAuth]);\n\n // Check authentication (cookie mode) - with promise deduplication\n const checkAuth = useCallback(async (): Promise<boolean> => {\n const authCheckUrl = config.authCheckUrl;\n if (!config.cookieAuth?.enabled || !authCheckUrl) {\n return false;\n }\n\n // Return existing promise if check is already in progress\n const pending = pendingCheckAuth.get(store);\n if (pending) {\n return pending;\n }\n\n const doCheck = async (): Promise<boolean> => {\n try {\n const headers = getCsrfHeaders(config);\n const response = await config.axios.get(authCheckUrl, { headers });\n\n if (response.data.authenticated) {\n setAuthenticated(true);\n\n const extractedData = config.extractData?.(response.data);\n if (extractedData) {\n setData(extractedData);\n } else if (config.dataUrl) {\n await fetchData();\n }\n\n return true;\n }\n\n reset();\n return false;\n } catch (error) {\n reset();\n config.onError?.(error);\n return false;\n } finally {\n pendingCheckAuth.delete(store);\n }\n };\n\n const promise = doCheck();\n pendingCheckAuth.set(store, promise);\n return promise;\n }, [store, config, setAuthenticated, setData]);\n\n // Fetch data from dataUrl\n const fetchData = useCallback(async () => {\n if (!config.dataUrl) return;\n\n try {\n const res = await config.axios.get<D>(config.dataUrl);\n setData(res.data);\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n throw error;\n }\n }, [config, setData, reset, setAxiosAuth]);\n\n // Login\n const login = async (credentials: Record<string, string>, callback?: () => void) => {\n try {\n const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};\n const res = await config.axios.post(config.loginUrl, credentials, { headers });\n\n if (config.cookieAuth?.enabled) {\n // Cookie mode: Server sets httpOnly cookie, just mark as authenticated\n setAuthenticated(true);\n } else {\n // Token mode: Extract and store tokens\n const newTokens = config.extractTokens(res.data);\n setTokens(newTokens);\n setAxiosAuth(newTokens.accessToken, newTokens.tokenType);\n }\n\n // Extract data from response\n const extractedData = config.extractData?.(res.data);\n if (extractedData) {\n setData(extractedData);\n config.onLogin?.(extractedData);\n } else if (config.dataUrl) {\n await fetchData();\n const currentData = store.getState().data;\n if (currentData) config.onLogin?.(currentData);\n }\n\n callback?.();\n } catch (error) {\n reset();\n setAxiosAuth();\n config.onError?.(error);\n throw error;\n }\n };\n\n // Logout\n const logout = async () => {\n try {\n if (config.logoutUrl) {\n const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};\n await config.axios.post(config.logoutUrl, {}, { headers });\n }\n } catch (error) {\n config.onError?.(error);\n } finally {\n reset();\n setAxiosAuth();\n config.onLogout?.();\n }\n };\n\n // Auto-setup on mount\n useEffect(() => {\n // Cookie mode: Check authentication if not yet determined\n if (config.cookieAuth?.enabled) {\n if (isAuthenticated === null) {\n checkAuth();\n }\n return;\n }\n\n // Token mode: Setup headers and auto-refresh\n if (tokens?.accessToken) {\n setAxiosAuth(tokens.accessToken, tokens.tokenType);\n\n // Check if expired\n if (isTokenExpired()) {\n if (tokens.refreshToken && config.autoRefresh) {\n refresh();\n } else {\n reset();\n }\n return;\n }\n\n // Setup auto-refresh timer\n if (tokens.expiresAt && tokens.refreshToken && config.autoRefresh) {\n const timeUntilExpiry = tokens.expiresAt - Date.now();\n const refreshTime = Math.max(timeUntilExpiry - config.refreshThreshold, 0);\n\n const timer = setTimeout(refresh, refreshTime);\n return () => clearTimeout(timer);\n }\n\n // Fetch data if missing\n if (!data && config.dataUrl) {\n fetchData().catch(() => {});\n }\n }\n return;\n }, [tokens, data, isAuthenticated, config, isTokenExpired, refresh, checkAuth, fetchData, setAxiosAuth, reset]);\n\n return { login, logout, refresh, checkAuth, fetchData };\n}\n\n// Helper: Get CSRF headers if enabled (uses getToken from config)\nfunction getCsrfHeaders(config: any): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.cookieAuth?.csrf?.enabled) {\n const csrfToken = config.cookieAuth.csrf.getToken();\n if (csrfToken) {\n headers[config.cookieAuth.csrf.headerName] = csrfToken;\n }\n }\n return headers;\n}\n","/**\n * Authentication error codes\n */\nexport enum AuthErrorCode {\n INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n TOKEN_INVALID = 'TOKEN_INVALID',\n REFRESH_FAILED = 'REFRESH_FAILED',\n NETWORK_ERROR = 'NETWORK_ERROR',\n USER_NOT_FOUND = 'USER_NOT_FOUND',\n UNAUTHORIZED = 'UNAUTHORIZED',\n CSRF_TOKEN_MISSING = 'CSRF_TOKEN_MISSING',\n FORBIDDEN = 'FORBIDDEN',\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * Typed authentication error\n */\nexport class AuthError extends Error {\n constructor(\n public code: AuthErrorCode,\n public originalError?: any,\n message?: string\n ) {\n super(message || code);\n this.name = 'AuthError';\n\n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AuthError);\n }\n }\n\n /**\n * Convert error to JSON (excludes originalError in production)\n */\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n name: this.name,\n };\n }\n\n /**\n * Check if error is an AuthError\n */\n static isAuthError(error: any): error is AuthError {\n return error instanceof AuthError;\n }\n}\n\n/**\n * Create typed AuthError from any error\n */\nexport function createAuthError(error: any): AuthError {\n if (AuthError.isAuthError(error)) {\n return error;\n }\n\n // Axios error\n if (error.response) {\n const status = error.response.status;\n const data = error.response.data;\n\n switch (status) {\n case 401:\n // Check if it's an expired token\n if (data?.detail?.toLowerCase().includes('expired') ||\n data?.message?.toLowerCase().includes('expired')) {\n return new AuthError(AuthErrorCode.TOKEN_EXPIRED, error, 'Token has expired');\n }\n if (data?.detail?.toLowerCase().includes('invalid') ||\n data?.detail?.toLowerCase().includes('credentials')) {\n return new AuthError(AuthErrorCode.INVALID_CREDENTIALS, error, 'Invalid credentials');\n }\n return new AuthError(AuthErrorCode.UNAUTHORIZED, error, 'Unauthorized');\n\n case 403:\n if (data?.detail?.toLowerCase().includes('csrf')) {\n return new AuthError(AuthErrorCode.CSRF_TOKEN_MISSING, error, 'CSRF token missing or invalid');\n }\n return new AuthError(AuthErrorCode.FORBIDDEN, error, 'Access forbidden');\n\n case 404:\n if (data?.detail?.toLowerCase().includes('user')) {\n return new AuthError(AuthErrorCode.USER_NOT_FOUND, error, 'User not found');\n }\n return new AuthError(AuthErrorCode.UNKNOWN, error, 'Resource not found');\n\n default:\n return new AuthError(AuthErrorCode.UNKNOWN, error, `HTTP ${status} error`);\n }\n }\n\n // Network error (no response received)\n if (error.request) {\n return new AuthError(AuthErrorCode.NETWORK_ERROR, error, 'Network error - no response received');\n }\n\n // Other errors\n return new AuthError(AuthErrorCode.UNKNOWN, error, error.message || 'Unknown error');\n}\n"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__287__","__WEBPACK_EXTERNAL_MODULE__155__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","value","validateAuthConfig","config","axios","Error","loginUrl","cookieName","extractData","cookieAuth","enabled","csrf","headerName","getToken","document","match","cookie","RegExp","persistence","storage","window","localStorage","tokenKey","refreshTokenKey","tokenTypeKey","dataKey","expiryKey","logoutUrl","refreshUrl","dataUrl","authCheckUrl","extractTokens","defaultExtractTokens","data","formatAuthHeader","token","tokenType","autoRefresh","refreshThreshold","onError","onLogin","onLogout","access_token","accessToken","refreshToken","refresh_token","expiresAt","expires_in","Date","now","token_type","auth_token","CSRF_METHODS","createAuthStore","interceptors","request","use","requestConfig","method","toLowerCase","includes","csrfToken","headers","setupCsrfInterceptor","initialTokens","getItem","expiryString","parseInt","isNaN","getStoredTokens","initialData","dataString","JSON","parse","getStoredData","initialIsAuthenticated","store","create","set","tokens","isAuthenticated","setTokens","setItem","removeItem","toString","error","setBearerToken","setAuthenticated","authenticated","setData","stringify","reset","isTokenExpired","assign","createAuthRegistry","registry","stringKey","String","validatedConfig","pendingCheckAuth","WeakMap","useAuth","setAxiosAuth","useCallback","defaults","common","refresh","getCsrfHeaders","post","response","newTokens","checkAuth","pending","doCheck","extractedData","fetchData","delete","promise","res","useEffect","timeUntilExpiry","refreshTime","Math","max","setTimeout","clearTimeout","catch","login","credentials","callback","currentData","getState","logout","AuthErrorCode","code","originalError","message","name","captureStackTrace","AuthError","toJSON","isAuthError","createAuthError","status","detail","TOKEN_EXPIRED","INVALID_CREDENTIALS","UNAUTHORIZED","CSRF_TOKEN_MISSING","FORBIDDEN","USER_NOT_FOUND","UNKNOWN","NETWORK_ERROR"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@jasperoosthoek/zustand-auth-registry",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "author": "jasperoosthoek",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/jasperoosthoek/zustand-auth-registry",
7
7
  "description": "Authentication state management using Zustand and Axios",
8
8
  "files": [
9
- "src/",
10
- "dist/"
9
+ "dist/",
10
+ "CHANGELOG.md"
11
11
  ],
12
12
  "types": "dist/index.d.ts",
13
13
  "main": "dist/index.js",
package/src/authConfig.ts DELETED
@@ -1,205 +0,0 @@
1
- import { AxiosInstance } from 'axios';
2
-
3
- // Token data structure
4
- export type TokenData = {
5
- accessToken: string;
6
- refreshToken?: string;
7
- expiresAt?: number;
8
- tokenType: string;
9
- };
10
-
11
- export type AuthConfig<D> = {
12
- axios: AxiosInstance;
13
-
14
- // Endpoints
15
- loginUrl: string;
16
- logoutUrl?: string;
17
- refreshUrl?: string;
18
- dataUrl?: string;
19
- authCheckUrl?: string; // For cookie auth verification
20
-
21
- // Token extraction from login response
22
- extractTokens?: (data: any) => TokenData;
23
-
24
- // Data extraction from responses (login, checkAuth)
25
- // Can be a function or a string key (e.g., "user" extracts data.user)
26
- extractData?: ((data: any) => D | null) | string;
27
-
28
- // Auth header format (default: "Bearer {token}")
29
- formatAuthHeader?: (token: string, tokenType?: string) => string;
30
-
31
- // Auto-refresh settings
32
- autoRefresh?: boolean;
33
- refreshThreshold?: number; // ms before expiry to refresh (default: 5 min)
34
-
35
- // Cookie-based authentication (alternative to localStorage)
36
- cookieAuth?: {
37
- enabled: boolean;
38
- csrf?: {
39
- enabled: boolean;
40
- headerName?: string; // Default: 'X-CSRFToken'
41
- // Option 1: Provide cookie name (web only, uses document.cookie)
42
- cookieName?: string; // Default: 'csrftoken'
43
- // Option 2: Provide custom token getter (platform-agnostic)
44
- getToken?: () => string | null;
45
- };
46
- };
47
-
48
- // Token persistence (localStorage)
49
- persistence?: {
50
- enabled: boolean;
51
- storage?: Storage;
52
- tokenKey?: string;
53
- refreshTokenKey?: string;
54
- dataKey?: string;
55
- expiryKey?: string;
56
- };
57
-
58
- // Callbacks
59
- onError?: (error: any) => void;
60
- onLogin?: (data: D) => void;
61
- onLogout?: () => void;
62
- };
63
-
64
- export type ValidatedAuthConfig<D> = {
65
- axios: AxiosInstance;
66
-
67
- // Endpoints
68
- loginUrl: string;
69
- logoutUrl?: string;
70
- refreshUrl?: string;
71
- dataUrl?: string;
72
- authCheckUrl?: string;
73
-
74
- // Extraction functions
75
- extractTokens: (data: any) => TokenData;
76
- extractData?: (data: any) => D | null;
77
-
78
- // Auth header format
79
- formatAuthHeader: (token: string, tokenType?: string) => string;
80
-
81
- // Auto-refresh
82
- autoRefresh: boolean;
83
- refreshThreshold: number;
84
-
85
- // Cookie auth
86
- cookieAuth?: {
87
- enabled: boolean;
88
- csrf: {
89
- enabled: boolean;
90
- headerName: string;
91
- getToken: () => string | null;
92
- };
93
- };
94
-
95
- // Persistence
96
- persistence: {
97
- enabled: boolean;
98
- storage: Storage;
99
- tokenKey: string;
100
- refreshTokenKey: string;
101
- dataKey: string;
102
- expiryKey: string;
103
- };
104
-
105
- // Callbacks
106
- onError?: (error: any) => void;
107
- onLogin?: (data: D) => void;
108
- onLogout?: () => void;
109
- };
110
-
111
- export const validateAuthConfig = <D>(config: AuthConfig<D>): ValidatedAuthConfig<D> => {
112
- if (!config.axios) {
113
- throw new Error('AuthConfig: axios instance is required');
114
- }
115
-
116
- if (!config.loginUrl) {
117
- throw new Error('AuthConfig: loginUrl is required');
118
- }
119
-
120
- // Cookie auth config
121
- const cookieAuth = config.cookieAuth?.enabled ? {
122
- enabled: true,
123
- csrf: {
124
- enabled: config.cookieAuth.csrf?.enabled ?? false,
125
- headerName: config.cookieAuth.csrf?.headerName || 'X-CSRFToken',
126
- getToken: config.cookieAuth.csrf?.getToken ?? createCookieTokenGetter(
127
- config.cookieAuth.csrf?.cookieName || 'csrftoken'
128
- ),
129
- },
130
- } : undefined;
131
-
132
- // Persistence config (disabled by default)
133
- const persistence = {
134
- enabled: config.persistence?.enabled ?? false,
135
- storage: config.persistence?.storage ??
136
- (typeof window !== 'undefined' && window.localStorage ? window.localStorage : {} as Storage),
137
- tokenKey: config.persistence?.tokenKey ?? 'token',
138
- refreshTokenKey: config.persistence?.refreshTokenKey ?? 'refresh_token',
139
- dataKey: config.persistence?.dataKey ?? 'data',
140
- expiryKey: config.persistence?.expiryKey ?? 'expires_at',
141
- };
142
-
143
- return {
144
- axios: config.axios,
145
- loginUrl: config.loginUrl,
146
- logoutUrl: config.logoutUrl,
147
- refreshUrl: config.refreshUrl,
148
- dataUrl: config.dataUrl,
149
- authCheckUrl: config.authCheckUrl,
150
- extractTokens: config.extractTokens ?? defaultExtractTokens,
151
- extractData: normalizeExtractData(config.extractData),
152
- formatAuthHeader: config.formatAuthHeader ??
153
- ((token: string, tokenType: string = 'Bearer') => `${tokenType} ${token}`),
154
- autoRefresh: config.autoRefresh ?? true,
155
- refreshThreshold: config.refreshThreshold ?? 300000, // 5 minutes
156
- cookieAuth,
157
- persistence,
158
- onError: config.onError,
159
- onLogin: config.onLogin,
160
- onLogout: config.onLogout,
161
- };
162
- };
163
-
164
- // Default token extraction - handles common response formats
165
- function defaultExtractTokens(data: any): TokenData {
166
- // OAuth 2.0 format: { access_token, refresh_token, expires_in, token_type }
167
- if (data.access_token) {
168
- return {
169
- accessToken: data.access_token,
170
- refreshToken: data.refresh_token,
171
- expiresAt: data.expires_in ? Date.now() + (data.expires_in * 1000) : undefined,
172
- tokenType: data.token_type || 'Bearer',
173
- };
174
- }
175
-
176
- // Simple format: { token } or { auth_token }
177
- const token = data.token || data.auth_token;
178
- if (token) {
179
- return {
180
- accessToken: token,
181
- tokenType: 'Bearer',
182
- };
183
- }
184
-
185
- throw new Error('No token found in response. Provide extractTokens or ensure response contains access_token/token field.');
186
- }
187
-
188
- // Normalize extractData: string becomes key accessor, function passed through
189
- function normalizeExtractData<D>(
190
- extractData?: ((data: any) => D | null) | string
191
- ): ((data: any) => D | null) | undefined {
192
- if (typeof extractData === 'string') {
193
- return (data: any) => data[extractData] ?? null;
194
- }
195
- return extractData;
196
- }
197
-
198
- // Create a cookie-based CSRF token getter (web only)
199
- function createCookieTokenGetter(cookieName: string): () => string | null {
200
- return () => {
201
- if (typeof document === 'undefined') return null;
202
- const match = document.cookie.match(new RegExp(`${cookieName}=([^;]+)`));
203
- return match ? match[1] : null;
204
- };
205
- }
package/src/authStore.ts DELETED
@@ -1,174 +0,0 @@
1
- import { create, StoreApi, UseBoundStore } from 'zustand';
2
- import { ValidatedAuthConfig, TokenData } from './authConfig';
3
-
4
- export type AuthState<D> = {
5
- isAuthenticated: boolean | null; // null = not checked yet (cookie mode)
6
- data: D | null;
7
- tokens: TokenData | null; // null in cookie mode or when logged out
8
-
9
- // Methods
10
- setTokens: (tokens: TokenData) => void;
11
- setBearerToken: (token: string) => void; // Convenience for simple Bearer token auth
12
- setAuthenticated: (authenticated: boolean) => void; // For cookie mode
13
- setData: (data: D) => void;
14
- reset: () => void;
15
- isTokenExpired: () => boolean;
16
- };
17
-
18
- export type AuthStore<D> = UseBoundStore<StoreApi<AuthState<D>>> & {
19
- config: ValidatedAuthConfig<D>;
20
- };
21
-
22
- // Methods that modify state (require CSRF protection)
23
- const CSRF_METHODS = ['post', 'put', 'patch', 'delete'];
24
-
25
- const setupCsrfInterceptor = <D>(config: ValidatedAuthConfig<D>): number | null => {
26
- if (!config.cookieAuth?.csrf.enabled) {
27
- return null;
28
- }
29
-
30
- const { headerName, getToken } = config.cookieAuth.csrf;
31
-
32
- return config.axios.interceptors.request.use((requestConfig) => {
33
- const method = requestConfig.method?.toLowerCase();
34
- if (method && CSRF_METHODS.includes(method)) {
35
- const csrfToken = getToken();
36
- if (csrfToken) {
37
- requestConfig.headers[headerName] = csrfToken;
38
- }
39
- }
40
- return requestConfig;
41
- });
42
- };
43
-
44
- export const createAuthStore = <D>(config: ValidatedAuthConfig<D>): AuthStore<D> => {
45
- const { persistence, cookieAuth } = config;
46
-
47
- // Set up CSRF interceptor if enabled
48
- setupCsrfInterceptor(config);
49
-
50
- const getStoredTokens = (): TokenData | null => {
51
- // Cookie mode: No client-side tokens
52
- if (cookieAuth?.enabled) {
53
- return null;
54
- }
55
-
56
- // Token mode: Read from storage
57
- if (!persistence.enabled) return null;
58
- try {
59
- const accessToken = persistence.storage.getItem(persistence.tokenKey);
60
- if (!accessToken) return null;
61
-
62
- const refreshToken = persistence.storage.getItem(persistence.refreshTokenKey);
63
- const expiryString = persistence.storage.getItem(persistence.expiryKey);
64
- const expiresAt = expiryString ? parseInt(expiryString, 10) : undefined;
65
-
66
- return {
67
- accessToken,
68
- refreshToken: refreshToken || undefined,
69
- expiresAt: expiresAt && !isNaN(expiresAt) ? expiresAt : undefined,
70
- tokenType: 'Bearer',
71
- };
72
- } catch {
73
- return null;
74
- }
75
- };
76
-
77
- const getStoredData = (): D | null => {
78
- if (!persistence.enabled) return null;
79
- try {
80
- const dataString = persistence.storage.getItem(persistence.dataKey);
81
- return dataString ? (JSON.parse(dataString) as D) : null;
82
- } catch {
83
- return null;
84
- }
85
- };
86
-
87
- const initialTokens = getStoredTokens();
88
- const initialData = getStoredData();
89
-
90
- // Cookie mode: null (unknown until checkAuth)
91
- // Token mode: true/false based on token presence
92
- const initialIsAuthenticated = cookieAuth?.enabled
93
- ? null
94
- : !!initialTokens?.accessToken;
95
-
96
- const store = create<AuthState<D>>((set, get) => ({
97
- tokens: initialTokens,
98
- data: initialData,
99
- isAuthenticated: initialIsAuthenticated,
100
-
101
- setTokens: (tokens: TokenData) => {
102
- set({ tokens, isAuthenticated: true });
103
-
104
- // Cookie mode: No localStorage persistence for tokens
105
- if (cookieAuth?.enabled) {
106
- return;
107
- }
108
-
109
- // Token mode: Persist to storage
110
- if (persistence.enabled) {
111
- try {
112
- persistence.storage.setItem(persistence.tokenKey, tokens.accessToken);
113
-
114
- if (tokens.refreshToken) {
115
- persistence.storage.setItem(persistence.refreshTokenKey, tokens.refreshToken);
116
- } else {
117
- persistence.storage.removeItem(persistence.refreshTokenKey);
118
- }
119
-
120
- if (tokens.expiresAt) {
121
- persistence.storage.setItem(persistence.expiryKey, tokens.expiresAt.toString());
122
- } else {
123
- persistence.storage.removeItem(persistence.expiryKey);
124
- }
125
- } catch (error) {
126
- config.onError?.(error);
127
- }
128
- }
129
- },
130
-
131
- setBearerToken: (token: string) => {
132
- get().setTokens({ accessToken: token, tokenType: 'Bearer' });
133
- },
134
-
135
- setAuthenticated: (authenticated: boolean) => {
136
- set({ isAuthenticated: authenticated });
137
- },
138
-
139
- setData: (data: D) => {
140
- set({ data });
141
-
142
- if (persistence.enabled) {
143
- try {
144
- persistence.storage.setItem(persistence.dataKey, JSON.stringify(data));
145
- } catch (error) {
146
- config.onError?.(error);
147
- }
148
- }
149
- },
150
-
151
- reset: () => {
152
- set({ data: null, tokens: null, isAuthenticated: false });
153
-
154
- if (persistence.enabled) {
155
- try {
156
- persistence.storage.removeItem(persistence.tokenKey);
157
- persistence.storage.removeItem(persistence.refreshTokenKey);
158
- persistence.storage.removeItem(persistence.dataKey);
159
- persistence.storage.removeItem(persistence.expiryKey);
160
- } catch (error) {
161
- config.onError?.(error);
162
- }
163
- }
164
- },
165
-
166
- isTokenExpired: () => {
167
- const tokens = get().tokens;
168
- if (!tokens?.expiresAt) return false;
169
- return Date.now() >= tokens.expiresAt;
170
- },
171
- }));
172
-
173
- return Object.assign(store, { config });
174
- };
@@ -1,22 +0,0 @@
1
- import { AuthConfig, validateAuthConfig } from './authConfig';
2
- import { createAuthStore, AuthStore } from './authStore';
3
-
4
- export function createAuthRegistry<AuthModels extends Record<string, any>>() {
5
- const registry: Record<string, AuthStore<any>> = {};
6
-
7
- function getAuthStore<K extends keyof AuthModels>(
8
- key: K,
9
- config: AuthConfig<AuthModels[K]>
10
- ): AuthStore<AuthModels[K]> {
11
- const stringKey = String(key);
12
-
13
- if (!registry[stringKey]) {
14
- const validatedConfig = validateAuthConfig(config);
15
- registry[stringKey] = createAuthStore(validatedConfig);
16
- }
17
-
18
- return registry[stringKey];
19
- }
20
-
21
- return getAuthStore;
22
- }
package/src/errors.ts DELETED
@@ -1,104 +0,0 @@
1
- /**
2
- * Authentication error codes
3
- */
4
- export enum AuthErrorCode {
5
- INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
6
- TOKEN_EXPIRED = 'TOKEN_EXPIRED',
7
- TOKEN_INVALID = 'TOKEN_INVALID',
8
- REFRESH_FAILED = 'REFRESH_FAILED',
9
- NETWORK_ERROR = 'NETWORK_ERROR',
10
- USER_NOT_FOUND = 'USER_NOT_FOUND',
11
- UNAUTHORIZED = 'UNAUTHORIZED',
12
- CSRF_TOKEN_MISSING = 'CSRF_TOKEN_MISSING',
13
- FORBIDDEN = 'FORBIDDEN',
14
- UNKNOWN = 'UNKNOWN',
15
- }
16
-
17
- /**
18
- * Typed authentication error
19
- */
20
- export class AuthError extends Error {
21
- constructor(
22
- public code: AuthErrorCode,
23
- public originalError?: any,
24
- message?: string
25
- ) {
26
- super(message || code);
27
- this.name = 'AuthError';
28
-
29
- // Maintain proper stack trace for where our error was thrown (only available on V8)
30
- if (Error.captureStackTrace) {
31
- Error.captureStackTrace(this, AuthError);
32
- }
33
- }
34
-
35
- /**
36
- * Convert error to JSON (excludes originalError in production)
37
- */
38
- toJSON() {
39
- return {
40
- code: this.code,
41
- message: this.message,
42
- name: this.name,
43
- };
44
- }
45
-
46
- /**
47
- * Check if error is an AuthError
48
- */
49
- static isAuthError(error: any): error is AuthError {
50
- return error instanceof AuthError;
51
- }
52
- }
53
-
54
- /**
55
- * Create typed AuthError from any error
56
- */
57
- export function createAuthError(error: any): AuthError {
58
- if (AuthError.isAuthError(error)) {
59
- return error;
60
- }
61
-
62
- // Axios error
63
- if (error.response) {
64
- const status = error.response.status;
65
- const data = error.response.data;
66
-
67
- switch (status) {
68
- case 401:
69
- // Check if it's an expired token
70
- if (data?.detail?.toLowerCase().includes('expired') ||
71
- data?.message?.toLowerCase().includes('expired')) {
72
- return new AuthError(AuthErrorCode.TOKEN_EXPIRED, error, 'Token has expired');
73
- }
74
- if (data?.detail?.toLowerCase().includes('invalid') ||
75
- data?.detail?.toLowerCase().includes('credentials')) {
76
- return new AuthError(AuthErrorCode.INVALID_CREDENTIALS, error, 'Invalid credentials');
77
- }
78
- return new AuthError(AuthErrorCode.UNAUTHORIZED, error, 'Unauthorized');
79
-
80
- case 403:
81
- if (data?.detail?.toLowerCase().includes('csrf')) {
82
- return new AuthError(AuthErrorCode.CSRF_TOKEN_MISSING, error, 'CSRF token missing or invalid');
83
- }
84
- return new AuthError(AuthErrorCode.FORBIDDEN, error, 'Access forbidden');
85
-
86
- case 404:
87
- if (data?.detail?.toLowerCase().includes('user')) {
88
- return new AuthError(AuthErrorCode.USER_NOT_FOUND, error, 'User not found');
89
- }
90
- return new AuthError(AuthErrorCode.UNKNOWN, error, 'Resource not found');
91
-
92
- default:
93
- return new AuthError(AuthErrorCode.UNKNOWN, error, `HTTP ${status} error`);
94
- }
95
- }
96
-
97
- // Network error (no response received)
98
- if (error.request) {
99
- return new AuthError(AuthErrorCode.NETWORK_ERROR, error, 'Network error - no response received');
100
- }
101
-
102
- // Other errors
103
- return new AuthError(AuthErrorCode.UNKNOWN, error, error.message || 'Unknown error');
104
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './authConfig';
2
- export * from './authStore';
3
- export * from './createAuthRegistry';
4
- export * from './useAuth';
5
- export * from './errors';
package/src/useAuth.ts DELETED
@@ -1,225 +0,0 @@
1
- import { useEffect, useCallback } from 'react';
2
- import { AuthStore } from './authStore';
3
-
4
- // Promise deduplication: prevents multiple concurrent checkAuth calls per store
5
- const pendingCheckAuth = new WeakMap<AuthStore<any>, Promise<boolean>>();
6
-
7
- export function useAuth<D>(store: AuthStore<D>) {
8
- const { setTokens, setAuthenticated, setData, reset, tokens, data, isAuthenticated, isTokenExpired } = store();
9
- const config = store.config;
10
-
11
- // Set axios Authorization header (token mode only)
12
- // Note: CSRF is handled by interceptor in authStore.ts
13
- const setAxiosAuth = useCallback((token?: string, tokenType?: string) => {
14
- if (config.cookieAuth?.enabled) {
15
- // Cookie mode: CSRF handled by interceptor, no Authorization header needed
16
- return;
17
- }
18
-
19
- // Token mode: Set Authorization header
20
- if (token) {
21
- config.axios.defaults.headers.common['Authorization'] = config.formatAuthHeader(token, tokenType);
22
- } else {
23
- delete config.axios.defaults.headers.common['Authorization'];
24
- }
25
- }, [config]);
26
-
27
- // Refresh tokens
28
- const refresh = useCallback(async (): Promise<boolean> => {
29
- if (!config.refreshUrl) return false;
30
-
31
- try {
32
- // Cookie mode: Just call refresh endpoint, server handles cookie
33
- if (config.cookieAuth?.enabled) {
34
- const headers = getCsrfHeaders(config);
35
- await config.axios.post(config.refreshUrl, {}, { headers });
36
- return true;
37
- }
38
-
39
- // Token mode: Send refresh token, get new tokens
40
- if (!tokens?.refreshToken) return false;
41
-
42
- const response = await config.axios.post(config.refreshUrl, {
43
- refresh_token: tokens.refreshToken,
44
- });
45
-
46
- const newTokens = config.extractTokens(response.data);
47
- setTokens(newTokens);
48
- setAxiosAuth(newTokens.accessToken, newTokens.tokenType);
49
- return true;
50
- } catch (error) {
51
- reset();
52
- setAxiosAuth();
53
- config.onError?.(error);
54
- return false;
55
- }
56
- }, [tokens, config, setTokens, reset, setAxiosAuth]);
57
-
58
- // Check authentication (cookie mode) - with promise deduplication
59
- const checkAuth = useCallback(async (): Promise<boolean> => {
60
- const authCheckUrl = config.authCheckUrl;
61
- if (!config.cookieAuth?.enabled || !authCheckUrl) {
62
- return false;
63
- }
64
-
65
- // Return existing promise if check is already in progress
66
- const pending = pendingCheckAuth.get(store);
67
- if (pending) {
68
- return pending;
69
- }
70
-
71
- const doCheck = async (): Promise<boolean> => {
72
- try {
73
- const headers = getCsrfHeaders(config);
74
- const response = await config.axios.get(authCheckUrl, { headers });
75
-
76
- if (response.data.authenticated) {
77
- setAuthenticated(true);
78
-
79
- const extractedData = config.extractData?.(response.data);
80
- if (extractedData) {
81
- setData(extractedData);
82
- } else if (config.dataUrl) {
83
- await fetchData();
84
- }
85
-
86
- return true;
87
- }
88
-
89
- reset();
90
- return false;
91
- } catch (error) {
92
- reset();
93
- config.onError?.(error);
94
- return false;
95
- } finally {
96
- pendingCheckAuth.delete(store);
97
- }
98
- };
99
-
100
- const promise = doCheck();
101
- pendingCheckAuth.set(store, promise);
102
- return promise;
103
- }, [store, config, setAuthenticated, setData]);
104
-
105
- // Fetch data from dataUrl
106
- const fetchData = useCallback(async () => {
107
- if (!config.dataUrl) return;
108
-
109
- try {
110
- const res = await config.axios.get<D>(config.dataUrl);
111
- setData(res.data);
112
- } catch (error) {
113
- reset();
114
- setAxiosAuth();
115
- config.onError?.(error);
116
- throw error;
117
- }
118
- }, [config, setData, reset, setAxiosAuth]);
119
-
120
- // Login
121
- const login = async (credentials: Record<string, string>, callback?: () => void) => {
122
- try {
123
- const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};
124
- const res = await config.axios.post(config.loginUrl, credentials, { headers });
125
-
126
- if (config.cookieAuth?.enabled) {
127
- // Cookie mode: Server sets httpOnly cookie, just mark as authenticated
128
- setAuthenticated(true);
129
- } else {
130
- // Token mode: Extract and store tokens
131
- const newTokens = config.extractTokens(res.data);
132
- setTokens(newTokens);
133
- setAxiosAuth(newTokens.accessToken, newTokens.tokenType);
134
- }
135
-
136
- // Extract data from response
137
- const extractedData = config.extractData?.(res.data);
138
- if (extractedData) {
139
- setData(extractedData);
140
- config.onLogin?.(extractedData);
141
- } else if (config.dataUrl) {
142
- await fetchData();
143
- const currentData = store.getState().data;
144
- if (currentData) config.onLogin?.(currentData);
145
- }
146
-
147
- callback?.();
148
- } catch (error) {
149
- reset();
150
- setAxiosAuth();
151
- config.onError?.(error);
152
- throw error;
153
- }
154
- };
155
-
156
- // Logout
157
- const logout = async () => {
158
- try {
159
- if (config.logoutUrl) {
160
- const headers = config.cookieAuth?.enabled ? getCsrfHeaders(config) : {};
161
- await config.axios.post(config.logoutUrl, {}, { headers });
162
- }
163
- } catch (error) {
164
- config.onError?.(error);
165
- } finally {
166
- reset();
167
- setAxiosAuth();
168
- config.onLogout?.();
169
- }
170
- };
171
-
172
- // Auto-setup on mount
173
- useEffect(() => {
174
- // Cookie mode: Check authentication if not yet determined
175
- if (config.cookieAuth?.enabled) {
176
- if (isAuthenticated === null) {
177
- checkAuth();
178
- }
179
- return;
180
- }
181
-
182
- // Token mode: Setup headers and auto-refresh
183
- if (tokens?.accessToken) {
184
- setAxiosAuth(tokens.accessToken, tokens.tokenType);
185
-
186
- // Check if expired
187
- if (isTokenExpired()) {
188
- if (tokens.refreshToken && config.autoRefresh) {
189
- refresh();
190
- } else {
191
- reset();
192
- }
193
- return;
194
- }
195
-
196
- // Setup auto-refresh timer
197
- if (tokens.expiresAt && tokens.refreshToken && config.autoRefresh) {
198
- const timeUntilExpiry = tokens.expiresAt - Date.now();
199
- const refreshTime = Math.max(timeUntilExpiry - config.refreshThreshold, 0);
200
-
201
- const timer = setTimeout(refresh, refreshTime);
202
- return () => clearTimeout(timer);
203
- }
204
-
205
- // Fetch data if missing
206
- if (!data && config.dataUrl) {
207
- fetchData().catch(() => {});
208
- }
209
- }
210
- }, [tokens, data, isAuthenticated, config, isTokenExpired, refresh, checkAuth, fetchData, setAxiosAuth, reset]);
211
-
212
- return { login, logout, refresh, checkAuth, fetchData };
213
- }
214
-
215
- // Helper: Get CSRF headers if enabled (uses getToken from config)
216
- function getCsrfHeaders(config: any): Record<string, string> {
217
- const headers: Record<string, string> = {};
218
- if (config.cookieAuth?.csrf?.enabled) {
219
- const csrfToken = config.cookieAuth.csrf.getToken();
220
- if (csrfToken) {
221
- headers[config.cookieAuth.csrf.headerName] = csrfToken;
222
- }
223
- }
224
- return headers;
225
- }