@spidy092/auth-client 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core.js +23 -145
- package/package.json +1 -1
- package/token.js +15 -47
package/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// auth-client/core.js
|
|
1
|
+
// auth-client/core.js - MINIMAL WORKING VERSION
|
|
2
|
+
|
|
2
3
|
import {
|
|
3
4
|
setToken,
|
|
4
5
|
clearToken,
|
|
@@ -9,11 +10,9 @@ import {
|
|
|
9
10
|
} from './token';
|
|
10
11
|
import { getConfig, isRouterMode } from './config';
|
|
11
12
|
|
|
12
|
-
// ✅ Track callback state with listeners
|
|
13
13
|
let callbackProcessed = false;
|
|
14
14
|
|
|
15
|
-
export
|
|
16
|
-
// ✅ Reset callback state when starting new login
|
|
15
|
+
export function login(clientKeyArg, redirectUriArg) {
|
|
17
16
|
resetCallbackState();
|
|
18
17
|
|
|
19
18
|
const {
|
|
@@ -36,138 +35,40 @@ export async function login(clientKeyArg, redirectUriArg) {
|
|
|
36
35
|
throw new Error('Missing clientKey or redirectUri');
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
// Store app info
|
|
40
38
|
sessionStorage.setItem('originalApp', clientKey);
|
|
41
39
|
sessionStorage.setItem('returnUrl', redirectUri);
|
|
42
40
|
|
|
43
|
-
try {
|
|
44
|
-
const hasValidSession = await checkExistingTokens();
|
|
45
|
-
if (hasValidSession) {
|
|
46
|
-
console.log('✅ Valid session found, skipping login redirect');
|
|
47
|
-
return getToken();
|
|
48
|
-
}
|
|
49
|
-
} catch (err) {
|
|
50
|
-
console.log('⚠️ No valid session, proceeding with login flow');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ✅ Smart Router Logic
|
|
54
41
|
if (isRouterMode()) {
|
|
55
|
-
// Router mode: Direct backend authentication
|
|
56
42
|
return routerLogin(clientKey, redirectUri);
|
|
57
43
|
} else {
|
|
58
|
-
// Client mode: Redirect to centralized login
|
|
59
44
|
return clientLogin(clientKey, redirectUri);
|
|
60
45
|
}
|
|
61
46
|
}
|
|
62
47
|
|
|
63
|
-
// ✅ Router mode: Direct backend call
|
|
64
48
|
function routerLogin(clientKey, redirectUri) {
|
|
65
49
|
const { authBaseUrl } = getConfig();
|
|
66
50
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
67
51
|
|
|
68
|
-
console.log('🏭 Router Login:
|
|
69
|
-
clientKey,
|
|
70
|
-
redirectUri,
|
|
71
|
-
backendUrl: backendLoginUrl
|
|
72
|
-
});
|
|
73
|
-
|
|
52
|
+
console.log('🏭 Router Login:', backendLoginUrl);
|
|
74
53
|
window.location.href = backendLoginUrl;
|
|
75
54
|
}
|
|
76
55
|
|
|
77
|
-
|
|
78
|
-
async function checkExistingTokens() {
|
|
79
|
-
const token = getToken();
|
|
80
|
-
const refreshTokenValue = getRefreshToken();
|
|
81
|
-
|
|
82
|
-
console.log('🔍 Checking existing tokens:', {
|
|
83
|
-
hasAccessToken: !!token,
|
|
84
|
-
hasRefreshToken: !!refreshTokenValue
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// No tokens at all
|
|
88
|
-
if (!token && !refreshTokenValue) {
|
|
89
|
-
console.log('❌ No tokens found');
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Have valid access token
|
|
94
|
-
if (token && !isTokenExpiredLocal(token)) {
|
|
95
|
-
console.log('✅ Valid access token exists');
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Have refresh token, try to get new access token
|
|
100
|
-
if (refreshTokenValue) {
|
|
101
|
-
try {
|
|
102
|
-
console.log('🔄 Access token expired, attempting refresh...');
|
|
103
|
-
const newToken = await refreshToken();
|
|
104
|
-
console.log('✅ Token refreshed successfully');
|
|
105
|
-
return !!newToken;
|
|
106
|
-
} catch (err) {
|
|
107
|
-
console.warn('❌ Token refresh failed:', err);
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ✅ NEW HELPER: Check if token is expired
|
|
116
|
-
function isTokenExpiredLocal(token, bufferSeconds = 60) {
|
|
117
|
-
if (!token) return true;
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
const parts = token.split('.');
|
|
121
|
-
if (parts.length !== 3) return true;
|
|
122
|
-
|
|
123
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
124
|
-
|
|
125
|
-
if (!payload.exp) return true;
|
|
126
|
-
|
|
127
|
-
const now = Date.now() / 1000;
|
|
128
|
-
const isExpired = payload.exp < (now + bufferSeconds);
|
|
129
|
-
|
|
130
|
-
console.log('🕐 Token expiry check:', {
|
|
131
|
-
expiresAt: new Date(payload.exp * 1000).toLocaleString(),
|
|
132
|
-
now: new Date(now * 1000).toLocaleString(),
|
|
133
|
-
isExpired
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
return isExpired;
|
|
137
|
-
} catch (err) {
|
|
138
|
-
console.error('❌ Failed to decode token:', err);
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ✅ Client mode: Centralized login
|
|
144
56
|
function clientLogin(clientKey, redirectUri) {
|
|
145
57
|
const { accountUiUrl } = getConfig();
|
|
146
58
|
const centralizedLoginUrl = `${accountUiUrl}/login?client=${clientKey}&redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
147
59
|
|
|
148
|
-
console.log('🔄 Client Login:
|
|
149
|
-
clientKey,
|
|
150
|
-
redirectUri,
|
|
151
|
-
centralizedUrl: centralizedLoginUrl
|
|
152
|
-
});
|
|
153
|
-
|
|
60
|
+
console.log('🔄 Client Login:', centralizedLoginUrl);
|
|
154
61
|
window.location.href = centralizedLoginUrl;
|
|
155
62
|
}
|
|
156
63
|
|
|
157
64
|
export function logout() {
|
|
158
|
-
// ✅ Reset callback state on logout
|
|
159
65
|
resetCallbackState();
|
|
160
66
|
|
|
161
67
|
const { clientKey, authBaseUrl, accountUiUrl } = getConfig();
|
|
162
68
|
const token = getToken();
|
|
163
69
|
|
|
164
|
-
console.log('🚪 Smart Logout initiated
|
|
165
|
-
mode: isRouterMode() ? 'ROUTER' : 'CLIENT',
|
|
166
|
-
clientKey,
|
|
167
|
-
hasToken: !!token
|
|
168
|
-
});
|
|
70
|
+
console.log('🚪 Smart Logout initiated');
|
|
169
71
|
|
|
170
|
-
// Clear local storage immediately (this will trigger listeners)
|
|
171
72
|
clearToken();
|
|
172
73
|
clearRefreshToken();
|
|
173
74
|
sessionStorage.removeItem('originalApp');
|
|
@@ -180,12 +81,10 @@ export function logout() {
|
|
|
180
81
|
}
|
|
181
82
|
}
|
|
182
83
|
|
|
183
|
-
// ✅ Router logout
|
|
184
84
|
async function routerLogout(clientKey, authBaseUrl, accountUiUrl, token) {
|
|
185
|
-
console.log('🏭
|
|
85
|
+
console.log('🏭 Router Logout');
|
|
186
86
|
|
|
187
87
|
const refreshToken = getRefreshToken();
|
|
188
|
-
console.log('Refresh token available:', refreshToken ? 'FOUND' : 'MISSING');
|
|
189
88
|
|
|
190
89
|
try {
|
|
191
90
|
const response = await fetch(`${authBaseUrl}/logout/${clientKey}`, {
|
|
@@ -196,19 +95,17 @@ async function routerLogout(clientKey, authBaseUrl, accountUiUrl, token) {
|
|
|
196
95
|
'Content-Type': 'application/json'
|
|
197
96
|
},
|
|
198
97
|
body: JSON.stringify({
|
|
199
|
-
refreshToken: refreshToken
|
|
98
|
+
refreshToken: refreshToken
|
|
200
99
|
})
|
|
201
100
|
});
|
|
202
101
|
|
|
203
102
|
const data = await response.json();
|
|
204
103
|
console.log('✅ Logout response:', data);
|
|
205
104
|
|
|
206
|
-
// Clear stored tokens
|
|
207
105
|
clearRefreshToken();
|
|
208
106
|
clearToken();
|
|
209
107
|
|
|
210
|
-
|
|
211
|
-
await new Promise(resolve => setTimeout(resolve, 5000)); // ⏳ wait 5 sec
|
|
108
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
212
109
|
|
|
213
110
|
if (data.success && data.keycloakLogoutUrl) {
|
|
214
111
|
window.location.href = data.keycloakLogoutUrl;
|
|
@@ -221,16 +118,12 @@ async function routerLogout(clientKey, authBaseUrl, accountUiUrl, token) {
|
|
|
221
118
|
clearToken();
|
|
222
119
|
}
|
|
223
120
|
|
|
224
|
-
|
|
225
|
-
await new Promise(resolve => setTimeout(resolve, 5000)); // ⏳ wait 5 sec
|
|
121
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
226
122
|
window.location.href = '/login';
|
|
227
123
|
}
|
|
228
124
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// ✅ Client logout
|
|
232
125
|
function clientLogout(clientKey, accountUiUrl) {
|
|
233
|
-
console.log('🔄 Client Logout
|
|
126
|
+
console.log('🔄 Client Logout');
|
|
234
127
|
const logoutUrl = `${accountUiUrl}/login?client=${clientKey}&logout=true`;
|
|
235
128
|
window.location.href = logoutUrl;
|
|
236
129
|
}
|
|
@@ -238,10 +131,10 @@ function clientLogout(clientKey, accountUiUrl) {
|
|
|
238
131
|
export function handleCallback() {
|
|
239
132
|
const params = new URLSearchParams(window.location.search);
|
|
240
133
|
const accessToken = params.get('access_token');
|
|
241
|
-
const refreshToken = params.get('refresh_token');
|
|
134
|
+
const refreshToken = params.get('refresh_token');
|
|
242
135
|
const error = params.get('error');
|
|
243
136
|
|
|
244
|
-
console.log('🔄
|
|
137
|
+
console.log('🔄 Callback handling:', {
|
|
245
138
|
hasAccessToken: !!accessToken,
|
|
246
139
|
hasRefreshToken: !!refreshToken,
|
|
247
140
|
error
|
|
@@ -263,16 +156,14 @@ export function handleCallback() {
|
|
|
263
156
|
if (accessToken) {
|
|
264
157
|
setToken(accessToken);
|
|
265
158
|
|
|
266
|
-
// Store refresh token for future refresh calls
|
|
267
159
|
if (refreshToken) {
|
|
268
160
|
setRefreshToken(refreshToken);
|
|
269
161
|
console.log('✅ Refresh token persisted');
|
|
270
162
|
}
|
|
271
163
|
|
|
272
|
-
// Clean URL parameters
|
|
273
164
|
const url = new URL(window.location);
|
|
274
165
|
url.searchParams.delete('access_token');
|
|
275
|
-
url.searchParams.delete('refresh_token');
|
|
166
|
+
url.searchParams.delete('refresh_token');
|
|
276
167
|
url.searchParams.delete('state');
|
|
277
168
|
window.history.replaceState({}, '', url);
|
|
278
169
|
|
|
@@ -282,27 +173,18 @@ export function handleCallback() {
|
|
|
282
173
|
throw new Error('No access token found in callback URL');
|
|
283
174
|
}
|
|
284
175
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// ✅ Reset callback state
|
|
288
176
|
export function resetCallbackState() {
|
|
289
177
|
callbackProcessed = false;
|
|
290
|
-
console.log('🔄 Callback state reset');
|
|
291
178
|
}
|
|
292
179
|
|
|
293
|
-
// auth-client/core.js
|
|
294
180
|
export async function refreshToken() {
|
|
295
181
|
const { clientKey, authBaseUrl } = getConfig();
|
|
296
|
-
const refreshTokenValue = getRefreshToken();
|
|
182
|
+
const refreshTokenValue = getRefreshToken();
|
|
297
183
|
|
|
298
|
-
console.log('🔄 Refreshing token
|
|
299
|
-
clientKey,
|
|
300
|
-
mode: isRouterMode() ? 'ROUTER' : 'CLIENT',
|
|
301
|
-
hasRefreshToken: !!refreshTokenValue
|
|
302
|
-
});
|
|
184
|
+
console.log('🔄 Refreshing token');
|
|
303
185
|
|
|
304
186
|
if (!refreshTokenValue) {
|
|
305
|
-
console.warn('⚠️ No refresh token available
|
|
187
|
+
console.warn('⚠️ No refresh token available');
|
|
306
188
|
clearToken();
|
|
307
189
|
throw new Error('No refresh token available');
|
|
308
190
|
}
|
|
@@ -310,13 +192,13 @@ export async function refreshToken() {
|
|
|
310
192
|
try {
|
|
311
193
|
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, {
|
|
312
194
|
method: 'POST',
|
|
313
|
-
credentials: 'include',
|
|
195
|
+
credentials: 'include',
|
|
314
196
|
headers: {
|
|
315
197
|
'Content-Type': 'application/json',
|
|
316
|
-
'X-Refresh-Token': refreshTokenValue
|
|
198
|
+
'X-Refresh-Token': refreshTokenValue
|
|
317
199
|
},
|
|
318
200
|
body: JSON.stringify({
|
|
319
|
-
refreshToken: refreshTokenValue
|
|
201
|
+
refreshToken: refreshTokenValue
|
|
320
202
|
})
|
|
321
203
|
});
|
|
322
204
|
|
|
@@ -326,27 +208,22 @@ export async function refreshToken() {
|
|
|
326
208
|
|
|
327
209
|
const { access_token, refresh_token: new_refresh_token } = await response.json();
|
|
328
210
|
|
|
329
|
-
// ✅ Update access token (triggers listeners)
|
|
330
211
|
setToken(access_token);
|
|
331
|
-
|
|
332
|
-
// ✅ Update refresh token in BOTH storages if backend returned new one
|
|
212
|
+
|
|
333
213
|
if (new_refresh_token) {
|
|
334
214
|
setRefreshToken(new_refresh_token);
|
|
335
215
|
}
|
|
336
216
|
|
|
337
|
-
console.log('✅ Token refresh successful
|
|
217
|
+
console.log('✅ Token refresh successful');
|
|
338
218
|
return access_token;
|
|
339
219
|
} catch (err) {
|
|
340
220
|
console.error('❌ Token refresh failed:', err);
|
|
341
|
-
// ✅ Clear everything on refresh failure
|
|
342
221
|
clearToken();
|
|
343
222
|
clearRefreshToken();
|
|
344
223
|
throw err;
|
|
345
224
|
}
|
|
346
225
|
}
|
|
347
226
|
|
|
348
|
-
|
|
349
|
-
|
|
350
227
|
export async function validateCurrentSession() {
|
|
351
228
|
try {
|
|
352
229
|
const { authBaseUrl } = getConfig();
|
|
@@ -386,6 +263,7 @@ export async function validateCurrentSession() {
|
|
|
386
263
|
|
|
387
264
|
|
|
388
265
|
|
|
266
|
+
|
|
389
267
|
// export async function refreshToken() {
|
|
390
268
|
// const { clientKey, authBaseUrl } = getConfig();
|
|
391
269
|
|
package/package.json
CHANGED
package/token.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// auth-client/token.js -
|
|
1
|
+
// auth-client/token.js - MINIMAL WORKING VERSION
|
|
2
2
|
|
|
3
3
|
import { jwtDecode } from 'jwt-decode';
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ let accessToken = null;
|
|
|
6
6
|
const listeners = new Set();
|
|
7
7
|
|
|
8
8
|
const REFRESH_COOKIE = 'account_refresh_token';
|
|
9
|
-
const COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
|
|
9
|
+
const COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
|
|
10
10
|
|
|
11
11
|
function secureAttribute() {
|
|
12
12
|
try {
|
|
@@ -18,7 +18,7 @@ function secureAttribute() {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// ========== ACCESS TOKEN
|
|
21
|
+
// ========== ACCESS TOKEN ==========
|
|
22
22
|
function writeAccessToken(token) {
|
|
23
23
|
if (!token) {
|
|
24
24
|
try {
|
|
@@ -45,8 +45,7 @@ function readAccessToken() {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// ========== REFRESH TOKEN (
|
|
49
|
-
|
|
48
|
+
// ========== REFRESH TOKEN (KEEP SIMPLE) ==========
|
|
50
49
|
export function setRefreshToken(token) {
|
|
51
50
|
if (!token) {
|
|
52
51
|
clearRefreshToken();
|
|
@@ -55,70 +54,38 @@ export function setRefreshToken(token) {
|
|
|
55
54
|
|
|
56
55
|
const expires = new Date(Date.now() + COOKIE_MAX_AGE * 1000);
|
|
57
56
|
|
|
58
|
-
// ✅ REVERT: Use SameSite=Lax (NOT Strict) for SSO to work
|
|
59
57
|
try {
|
|
60
58
|
document.cookie = `${REFRESH_COOKIE}=${encodeURIComponent(token)}; Path=/; SameSite=Lax${secureAttribute()}; Expires=${expires.toUTCString()}`;
|
|
61
|
-
console.log('✅ Refresh token cookie set (SameSite=Lax for SSO)');
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.warn('Could not persist refresh token cookie:', err);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ✅ REVERT: Keep sessionStorage (NOT localStorage) as fallback
|
|
67
|
-
try {
|
|
68
|
-
sessionStorage.setItem(REFRESH_COOKIE, token);
|
|
69
|
-
console.log('✅ Refresh token sessionStorage backup set');
|
|
70
59
|
} catch (err) {
|
|
71
|
-
console.warn('Could not
|
|
60
|
+
console.warn('Could not set refresh token:', err);
|
|
72
61
|
}
|
|
73
62
|
}
|
|
74
63
|
|
|
75
64
|
export function getRefreshToken() {
|
|
76
|
-
// Prefer cookie to align with server expectations
|
|
77
|
-
let cookieMatch = null;
|
|
78
65
|
try {
|
|
79
|
-
|
|
66
|
+
const match = document.cookie
|
|
80
67
|
?.split('; ')
|
|
81
68
|
?.find((row) => row.startsWith(`${REFRESH_COOKIE}=`));
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (cookieMatch) {
|
|
87
|
-
console.log('✅ Retrieved refresh token from cookie');
|
|
88
|
-
return decodeURIComponent(cookieMatch.split('=')[1]);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ✅ REVERT: Fallback to sessionStorage (NOT localStorage)
|
|
92
|
-
try {
|
|
93
|
-
const token = sessionStorage.getItem(REFRESH_COOKIE);
|
|
94
|
-
if (token) {
|
|
95
|
-
console.log('✅ Retrieved refresh token from sessionStorage (fallback)');
|
|
69
|
+
|
|
70
|
+
if (match) {
|
|
71
|
+
return decodeURIComponent(match.split('=')[1]);
|
|
96
72
|
}
|
|
97
|
-
return token;
|
|
98
73
|
} catch (err) {
|
|
99
|
-
console.warn('Could not read refresh token
|
|
100
|
-
return null;
|
|
74
|
+
console.warn('Could not read refresh token:', err);
|
|
101
75
|
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
102
78
|
}
|
|
103
79
|
|
|
104
80
|
export function clearRefreshToken() {
|
|
105
|
-
// ✅ REVERT: Clear with SameSite=Lax
|
|
106
81
|
try {
|
|
107
82
|
document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Lax${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
108
83
|
} catch (err) {
|
|
109
|
-
console.warn('Could not clear refresh token
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ✅ REVERT: Clear sessionStorage (NOT localStorage)
|
|
113
|
-
try {
|
|
114
|
-
sessionStorage.removeItem(REFRESH_COOKIE);
|
|
115
|
-
} catch (err) {
|
|
116
|
-
console.warn('Could not clear refresh token from sessionStorage:', err);
|
|
84
|
+
console.warn('Could not clear refresh token:', err);
|
|
117
85
|
}
|
|
118
86
|
}
|
|
119
87
|
|
|
120
|
-
// ========== ACCESS TOKEN FUNCTIONS
|
|
121
|
-
|
|
88
|
+
// ========== ACCESS TOKEN FUNCTIONS ==========
|
|
122
89
|
function decode(token) {
|
|
123
90
|
try {
|
|
124
91
|
return jwtDecode(token);
|
|
@@ -203,6 +170,7 @@ export function isAuthenticated() {
|
|
|
203
170
|
|
|
204
171
|
|
|
205
172
|
|
|
173
|
+
|
|
206
174
|
// // auth-client/token.js
|
|
207
175
|
// import { jwtDecode } from 'jwt-decode';
|
|
208
176
|
|