dauth-context-react 1.0.3 → 2.1.0

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/src/interfaces.ts CHANGED
@@ -13,6 +13,9 @@ export interface IDauthUser {
13
13
  role: string;
14
14
  telPrefix: string;
15
15
  telSuffix: string;
16
+ birthDate?: Date;
17
+ country?: string;
18
+ metadata?: Record<string, unknown>;
16
19
  createdAt: Date;
17
20
  updatedAt: Date;
18
21
  lastLogin: Date;
@@ -32,15 +35,7 @@ export interface IDauthState {
32
35
  loginWithRedirect: () => void;
33
36
  logout: () => void;
34
37
  getAccessToken: () => Promise<string>;
35
- updateUser: ({
36
- name,
37
- lastname,
38
- nickname,
39
- telPrefix,
40
- telSuffix,
41
- language,
42
- avatar,
43
- }: Partial<IDauthUser>) => Promise<boolean>;
38
+ updateUser: (fields: Partial<IDauthUser>) => Promise<boolean>;
44
39
  updateUserWithRedirect: () => void;
45
40
  // Send email verification
46
41
  sendEmailVerificationStatus: {
@@ -1,23 +1,24 @@
1
1
  import {
2
2
  getUserAPI,
3
- refreshAccessTokenAPI,
3
+ logoutAPI,
4
+ refreshTokenAPI,
4
5
  sendEmailVerificationAPI,
5
6
  updateUserAPI,
6
7
  } from '../api/dauth.api';
7
- import { getClientBasePath } from '../api/utils/config';
8
- import { TOKEN_LS } from '../constants';
8
+ import { TOKEN_LS, REFRESH_TOKEN_LS } from '../constants';
9
9
  import { IDauthDomainState, IDauthUser } from '../interfaces';
10
- import { routes } from '../api/utils/routes';
11
10
  import * as DauthTypes from './dauth.types';
12
11
 
13
12
  type TSetDauthStateAction = {
14
13
  dispatch: React.Dispatch<any>;
15
14
  token: string;
15
+ refreshToken: string;
16
16
  domainName: string;
17
17
  };
18
18
  export async function setDauthStateAction({
19
19
  dispatch,
20
20
  token,
21
+ refreshToken,
21
22
  domainName,
22
23
  }: TSetDauthStateAction) {
23
24
  dispatch({ type: DauthTypes.SET_IS_LOADING, payload: { isLoading: true } });
@@ -33,7 +34,9 @@ export async function setDauthStateAction({
33
34
  },
34
35
  });
35
36
  window.history.replaceState({}, document.title, window.location.pathname);
36
- return localStorage.setItem(TOKEN_LS, token);
37
+ localStorage.setItem(TOKEN_LS, token);
38
+ localStorage.setItem(REFRESH_TOKEN_LS, refreshToken);
39
+ return;
37
40
  } else {
38
41
  return resetUser(dispatch);
39
42
  }
@@ -50,23 +53,29 @@ export async function setDauthStateAction({
50
53
 
51
54
  type TSetAutoLoginAction = {
52
55
  dispatch: React.Dispatch<any>;
53
- token_ls: string;
54
56
  domainName: string;
55
57
  };
56
58
  export async function setAutoLoginAction({
57
59
  dispatch,
58
- token_ls,
59
60
  domainName,
60
61
  }: TSetAutoLoginAction) {
61
62
  dispatch({ type: DauthTypes.SET_IS_LOADING, payload: { isLoading: true } });
63
+ const storedRefreshToken = localStorage.getItem(REFRESH_TOKEN_LS);
64
+ if (!storedRefreshToken) {
65
+ dispatch({
66
+ type: DauthTypes.SET_IS_LOADING,
67
+ payload: { isLoading: false },
68
+ });
69
+ return resetUser(dispatch);
70
+ }
62
71
  try {
63
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
64
- domainName,
65
- token_ls
66
- );
67
- if (refreshAccessTokenFetch.response.status === 200) {
68
- const newToken = refreshAccessTokenFetch.data.accessToken || token_ls;
69
- const getUserFetch = await getUserAPI(domainName, newToken);
72
+ const refreshResult = await refreshTokenAPI(domainName, storedRefreshToken);
73
+ if (refreshResult.response.status === 200) {
74
+ const newAccessToken = refreshResult.data.accessToken;
75
+ const newRefreshToken = refreshResult.data.refreshToken;
76
+ localStorage.setItem(TOKEN_LS, newAccessToken);
77
+ localStorage.setItem(REFRESH_TOKEN_LS, newRefreshToken);
78
+ const getUserFetch = await getUserAPI(domainName, newAccessToken);
70
79
  if (getUserFetch.response.status === 200) {
71
80
  dispatch({
72
81
  type: DauthTypes.LOGIN,
@@ -76,26 +85,14 @@ export async function setAutoLoginAction({
76
85
  isAuthenticated: true,
77
86
  },
78
87
  });
79
- localStorage.setItem(
80
- TOKEN_LS,
81
- refreshAccessTokenFetch.data.accessToken
82
- );
83
88
  return;
84
- } else {
85
- window.location.replace(
86
- `${getClientBasePath()}/${domainName}/${routes.signin}`
87
- );
88
- return resetUser(dispatch);
89
89
  }
90
- } else {
91
- window.location.replace(
92
- `${getClientBasePath()}/${domainName}/${routes.signin}`
93
- );
94
- return resetUser(dispatch);
95
90
  }
91
+ // Refresh failed — session expired
92
+ resetUser(dispatch);
96
93
  } catch (error) {
97
94
  console.error(error);
98
- return resetUser(dispatch);
95
+ resetUser(dispatch);
99
96
  } finally {
100
97
  dispatch({
101
98
  type: DauthTypes.SET_IS_LOADING,
@@ -104,29 +101,67 @@ export async function setAutoLoginAction({
104
101
  }
105
102
  }
106
103
 
107
- export function setLogoutAction({
104
+ export async function setLogoutAction({
108
105
  dispatch,
106
+ domainName,
109
107
  }: {
110
108
  dispatch: React.Dispatch<any>;
109
+ domainName: string;
111
110
  }) {
111
+ const storedRefreshToken = localStorage.getItem(REFRESH_TOKEN_LS);
112
+ if (storedRefreshToken && domainName) {
113
+ try {
114
+ await logoutAPI(domainName, storedRefreshToken);
115
+ } catch (_) {
116
+ // Best-effort server-side logout
117
+ }
118
+ }
112
119
  dispatch({ type: DauthTypes.SET_IS_LOADING, payload: { isLoading: true } });
113
120
  dispatch({
114
121
  type: DauthTypes.LOGIN,
115
122
  payload: {
116
123
  user: {
117
- language: window.document.documentElement.getAttribute('lang') || 'es',
124
+ language:
125
+ window.document.documentElement.getAttribute('lang') || 'es',
118
126
  },
119
127
  domain: {},
120
128
  isAuthenticated: false,
121
129
  },
122
130
  });
123
131
  localStorage.removeItem(TOKEN_LS);
132
+ localStorage.removeItem(REFRESH_TOKEN_LS);
124
133
  return dispatch({
125
134
  type: DauthTypes.SET_IS_LOADING,
126
135
  payload: { isLoading: false },
127
136
  });
128
137
  }
129
138
 
139
+ export async function refreshSessionAction({
140
+ dispatch,
141
+ domainName,
142
+ }: {
143
+ dispatch: React.Dispatch<any>;
144
+ domainName: string;
145
+ }) {
146
+ const storedRefreshToken = localStorage.getItem(REFRESH_TOKEN_LS);
147
+ if (!storedRefreshToken) {
148
+ return resetUser(dispatch);
149
+ }
150
+ try {
151
+ const refreshResult = await refreshTokenAPI(domainName, storedRefreshToken);
152
+ if (refreshResult.response.status === 200) {
153
+ localStorage.setItem(TOKEN_LS, refreshResult.data.accessToken);
154
+ localStorage.setItem(REFRESH_TOKEN_LS, refreshResult.data.refreshToken);
155
+ return;
156
+ }
157
+ // Refresh failed — revoked or expired
158
+ resetUser(dispatch);
159
+ } catch (error) {
160
+ console.error(error);
161
+ resetUser(dispatch);
162
+ }
163
+ }
164
+
130
165
  type TSetUpdateAction = {
131
166
  dispatch: React.Dispatch<any>;
132
167
  domainName: string;
@@ -224,40 +259,6 @@ export async function sendEmailVerificationAction({
224
259
  }
225
260
  }
226
261
 
227
- export async function checkTokenAction({
228
- dispatch,
229
- domainName,
230
- token,
231
- }: {
232
- dispatch: React.Dispatch<any>;
233
- domainName: string;
234
- token: string;
235
- }) {
236
- try {
237
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
238
- domainName,
239
- token
240
- );
241
- if (refreshAccessTokenFetch.response.status === 200) {
242
- if (refreshAccessTokenFetch.data.accessToken) {
243
- localStorage.setItem(
244
- TOKEN_LS,
245
- refreshAccessTokenFetch.data.accessToken
246
- );
247
- }
248
- return;
249
- } else {
250
- window.location.replace(
251
- `${getClientBasePath()}/${domainName}/${routes.signin}`
252
- );
253
- return resetUser(dispatch);
254
- }
255
- } catch (error) {
256
- resetUser(dispatch);
257
- throw error;
258
- }
259
- }
260
-
261
262
  export async function getAccessTokenAction({
262
263
  dispatch,
263
264
  domainName,
@@ -266,28 +267,31 @@ export async function getAccessTokenAction({
266
267
  domainName: string;
267
268
  }) {
268
269
  const token_ls = localStorage.getItem(TOKEN_LS);
269
- if (!token_ls) return;
270
+ if (!token_ls) return 'token-not-found';
271
+ // Decode JWT to check expiry (without verification — that's the server's job)
270
272
  try {
271
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
272
- domainName,
273
- token_ls
274
- );
275
- if (refreshAccessTokenFetch.response.status === 200) {
276
- return refreshAccessTokenFetch.data.accessToken ?? token_ls;
277
- } else {
278
- resetUser(dispatch);
279
- return 'token-not-found';
273
+ const payloadB64 = token_ls.split('.')[1];
274
+ if (payloadB64) {
275
+ const payload = JSON.parse(atob(payloadB64));
276
+ const expiresIn = (payload.exp || 0) * 1000 - Date.now();
277
+ // If token expires in less than 5 minutes, refresh proactively
278
+ if (expiresIn < 5 * 60 * 1000) {
279
+ await refreshSessionAction({ dispatch, domainName });
280
+ const refreshedToken = localStorage.getItem(TOKEN_LS);
281
+ return refreshedToken || 'token-not-found';
282
+ }
280
283
  }
281
- } catch (error) {
282
- resetUser(dispatch);
283
- throw error;
284
+ } catch (_) {
285
+ // If decode fails, return stored token and let server validate
284
286
  }
287
+ return token_ls;
285
288
  }
286
289
 
287
290
  ///////////////////////////////////////////
288
291
  //////////////////////////////////////////
289
292
  export const resetUser = (dispatch: React.Dispatch<any>) => {
290
293
  localStorage.removeItem(TOKEN_LS);
294
+ localStorage.removeItem(REFRESH_TOKEN_LS);
291
295
  return dispatch({
292
296
  type: DauthTypes.LOGIN,
293
297
  payload: {