dauth-context-react 1.0.2 → 2.0.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.
@@ -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 } });
@@ -32,12 +33,10 @@ export async function setDauthStateAction({
32
33
  isAuthenticated: true,
33
34
  },
34
35
  });
35
- window.history.replaceState(
36
- {},
37
- document.title,
38
- getUserFetch.data.domain.loginRedirect
39
- );
40
- return localStorage.setItem(TOKEN_LS, token);
36
+ window.history.replaceState({}, document.title, window.location.pathname);
37
+ localStorage.setItem(TOKEN_LS, token);
38
+ localStorage.setItem(REFRESH_TOKEN_LS, refreshToken);
39
+ return;
41
40
  } else {
42
41
  return resetUser(dispatch);
43
42
  }
@@ -54,23 +53,29 @@ export async function setDauthStateAction({
54
53
 
55
54
  type TSetAutoLoginAction = {
56
55
  dispatch: React.Dispatch<any>;
57
- token_ls: string;
58
56
  domainName: string;
59
57
  };
60
58
  export async function setAutoLoginAction({
61
59
  dispatch,
62
- token_ls,
63
60
  domainName,
64
61
  }: TSetAutoLoginAction) {
65
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
+ }
66
71
  try {
67
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
68
- domainName,
69
- token_ls
70
- );
71
- if (refreshAccessTokenFetch.response.status === 200) {
72
- const newToken = refreshAccessTokenFetch.data.accessToken || token_ls;
73
- 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);
74
79
  if (getUserFetch.response.status === 200) {
75
80
  dispatch({
76
81
  type: DauthTypes.LOGIN,
@@ -80,26 +85,14 @@ export async function setAutoLoginAction({
80
85
  isAuthenticated: true,
81
86
  },
82
87
  });
83
- localStorage.setItem(
84
- TOKEN_LS,
85
- refreshAccessTokenFetch.data.accessToken
86
- );
87
88
  return;
88
- } else {
89
- window.location.replace(
90
- `${getClientBasePath()}/${domainName}/${routes.signin}`
91
- );
92
- return resetUser(dispatch);
93
89
  }
94
- } else {
95
- window.location.replace(
96
- `${getClientBasePath()}/${domainName}/${routes.signin}`
97
- );
98
- return resetUser(dispatch);
99
90
  }
91
+ // Refresh failed — session expired
92
+ resetUser(dispatch);
100
93
  } catch (error) {
101
94
  console.error(error);
102
- return resetUser(dispatch);
95
+ resetUser(dispatch);
103
96
  } finally {
104
97
  dispatch({
105
98
  type: DauthTypes.SET_IS_LOADING,
@@ -108,29 +101,67 @@ export async function setAutoLoginAction({
108
101
  }
109
102
  }
110
103
 
111
- export function setLogoutAction({
104
+ export async function setLogoutAction({
112
105
  dispatch,
106
+ domainName,
113
107
  }: {
114
108
  dispatch: React.Dispatch<any>;
109
+ domainName: string;
115
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
+ }
116
119
  dispatch({ type: DauthTypes.SET_IS_LOADING, payload: { isLoading: true } });
117
120
  dispatch({
118
121
  type: DauthTypes.LOGIN,
119
122
  payload: {
120
123
  user: {
121
- language: window.document.documentElement.getAttribute('lang') || 'es',
124
+ language:
125
+ window.document.documentElement.getAttribute('lang') || 'es',
122
126
  },
123
127
  domain: {},
124
128
  isAuthenticated: false,
125
129
  },
126
130
  });
127
131
  localStorage.removeItem(TOKEN_LS);
132
+ localStorage.removeItem(REFRESH_TOKEN_LS);
128
133
  return dispatch({
129
134
  type: DauthTypes.SET_IS_LOADING,
130
135
  payload: { isLoading: false },
131
136
  });
132
137
  }
133
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
+
134
165
  type TSetUpdateAction = {
135
166
  dispatch: React.Dispatch<any>;
136
167
  domainName: string;
@@ -228,40 +259,6 @@ export async function sendEmailVerificationAction({
228
259
  }
229
260
  }
230
261
 
231
- export async function checkTokenAction({
232
- dispatch,
233
- domainName,
234
- token,
235
- }: {
236
- dispatch: React.Dispatch<any>;
237
- domainName: string;
238
- token: string;
239
- }) {
240
- try {
241
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
242
- domainName,
243
- token
244
- );
245
- if (refreshAccessTokenFetch.response.status === 200) {
246
- if (refreshAccessTokenFetch.data.accessToken) {
247
- localStorage.setItem(
248
- TOKEN_LS,
249
- refreshAccessTokenFetch.data.accessToken
250
- );
251
- }
252
- return;
253
- } else {
254
- window.location.replace(
255
- `${getClientBasePath()}/${domainName}/${routes.signin}`
256
- );
257
- return resetUser(dispatch);
258
- }
259
- } catch (error) {
260
- resetUser(dispatch);
261
- throw error;
262
- }
263
- }
264
-
265
262
  export async function getAccessTokenAction({
266
263
  dispatch,
267
264
  domainName,
@@ -270,28 +267,31 @@ export async function getAccessTokenAction({
270
267
  domainName: string;
271
268
  }) {
272
269
  const token_ls = localStorage.getItem(TOKEN_LS);
273
- 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)
274
272
  try {
275
- const refreshAccessTokenFetch = await refreshAccessTokenAPI(
276
- domainName,
277
- token_ls
278
- );
279
- if (refreshAccessTokenFetch.response.status === 200) {
280
- return refreshAccessTokenFetch.data.accessToken ?? token_ls;
281
- } else {
282
- resetUser(dispatch);
283
- 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
+ }
284
283
  }
285
- } catch (error) {
286
- resetUser(dispatch);
287
- throw error;
284
+ } catch (_) {
285
+ // If decode fails, return stored token and let server validate
288
286
  }
287
+ return token_ls;
289
288
  }
290
289
 
291
290
  ///////////////////////////////////////////
292
291
  //////////////////////////////////////////
293
292
  export const resetUser = (dispatch: React.Dispatch<any>) => {
294
293
  localStorage.removeItem(TOKEN_LS);
294
+ localStorage.removeItem(REFRESH_TOKEN_LS);
295
295
  return dispatch({
296
296
  type: DauthTypes.LOGIN,
297
297
  payload: {