@spidy092/auth-client 2.1.1 → 2.1.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 +38 -10
- package/package.json +1 -1
- package/token.js +50 -28
package/core.js
CHANGED
|
@@ -189,18 +189,22 @@ export function handleCallback() {
|
|
|
189
189
|
if (accessToken) {
|
|
190
190
|
setToken(accessToken);
|
|
191
191
|
|
|
192
|
-
// ✅
|
|
193
|
-
//
|
|
192
|
+
// ✅ For HTTP development, store refresh token from URL
|
|
193
|
+
// In HTTPS production, refresh token is in httpOnly cookie (more secure)
|
|
194
194
|
const refreshTokenInUrl = params.get('refresh_token');
|
|
195
195
|
if (refreshTokenInUrl) {
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
const isHttpDev = typeof window !== 'undefined' && window.location?.protocol === 'http:';
|
|
197
|
+
if (isHttpDev) {
|
|
198
|
+
console.log('📦 HTTP dev mode: Storing refresh token from callback URL');
|
|
199
|
+
setRefreshToken(refreshTokenInUrl);
|
|
200
|
+
} else {
|
|
201
|
+
console.log('🔒 HTTPS mode: Refresh token is in httpOnly cookie (ignoring URL param)');
|
|
202
|
+
}
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
const url = new URL(window.location);
|
|
201
206
|
url.searchParams.delete('access_token');
|
|
202
207
|
url.searchParams.delete('refresh_token');
|
|
203
|
-
url.searchParams.delete('refresh_token');
|
|
204
208
|
url.searchParams.delete('state');
|
|
205
209
|
url.searchParams.delete('error');
|
|
206
210
|
url.searchParams.delete('error_description');
|
|
@@ -233,15 +237,31 @@ export async function refreshToken() {
|
|
|
233
237
|
refreshInProgress = true;
|
|
234
238
|
refreshPromise = (async () => {
|
|
235
239
|
try {
|
|
240
|
+
// Get stored refresh token (for HTTP development)
|
|
241
|
+
const storedRefreshToken = getRefreshToken();
|
|
242
|
+
|
|
236
243
|
console.log('🔄 Refreshing token:', {
|
|
237
244
|
clientKey,
|
|
238
|
-
mode: isRouterMode() ? 'ROUTER' : 'CLIENT'
|
|
245
|
+
mode: isRouterMode() ? 'ROUTER' : 'CLIENT',
|
|
246
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
239
247
|
});
|
|
240
248
|
|
|
241
|
-
|
|
249
|
+
// Build request options - send refresh token in body and header for HTTP dev
|
|
250
|
+
const requestOptions = {
|
|
242
251
|
method: 'POST',
|
|
243
|
-
credentials: 'include', // ✅ Include httpOnly cookies
|
|
244
|
-
|
|
252
|
+
credentials: 'include', // ✅ Include httpOnly cookies (for HTTPS)
|
|
253
|
+
headers: {
|
|
254
|
+
'Content-Type': 'application/json'
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// For HTTP development, send refresh token in body and header
|
|
259
|
+
if (storedRefreshToken) {
|
|
260
|
+
requestOptions.headers['X-Refresh-Token'] = storedRefreshToken;
|
|
261
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
245
265
|
|
|
246
266
|
if (!response.ok) {
|
|
247
267
|
const errorText = await response.text();
|
|
@@ -249,7 +269,8 @@ export async function refreshToken() {
|
|
|
249
269
|
throw new Error(`Refresh failed: ${response.status}`);
|
|
250
270
|
}
|
|
251
271
|
|
|
252
|
-
const
|
|
272
|
+
const data = await response.json();
|
|
273
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
253
274
|
|
|
254
275
|
if (!access_token) {
|
|
255
276
|
throw new Error('No access token in refresh response');
|
|
@@ -257,6 +278,13 @@ export async function refreshToken() {
|
|
|
257
278
|
|
|
258
279
|
// ✅ This will trigger token listeners
|
|
259
280
|
setToken(access_token);
|
|
281
|
+
|
|
282
|
+
// ✅ Store new refresh token if provided (token rotation)
|
|
283
|
+
if (new_refresh_token) {
|
|
284
|
+
setRefreshToken(new_refresh_token);
|
|
285
|
+
console.log('🔄 New refresh token stored from rotation');
|
|
286
|
+
}
|
|
287
|
+
|
|
260
288
|
console.log('✅ Token refresh successful, listeners notified');
|
|
261
289
|
return access_token;
|
|
262
290
|
} catch (err) {
|
package/package.json
CHANGED
package/token.js
CHANGED
|
@@ -145,55 +145,77 @@ export function clearToken() {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
// ========== REFRESH TOKEN STORAGE FOR HTTP DEVELOPMENT ==========
|
|
149
|
+
// In production (HTTPS), refresh tokens should ONLY be in httpOnly cookies set by server
|
|
150
|
+
// For HTTP development (cross-origin cookies don't work), we store in localStorage
|
|
151
|
+
const REFRESH_TOKEN_KEY = 'auth_refresh_token';
|
|
152
152
|
|
|
153
|
+
function isHttpDevelopment() {
|
|
154
|
+
try {
|
|
155
|
+
return typeof window !== 'undefined' &&
|
|
156
|
+
window.location?.protocol === 'http:';
|
|
157
|
+
} catch (err) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function setRefreshToken(token) {
|
|
153
163
|
if (!token) {
|
|
154
164
|
clearRefreshToken();
|
|
155
165
|
return;
|
|
156
166
|
}
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
// For HTTP development, store in localStorage (since httpOnly cookies don't work cross-origin)
|
|
169
|
+
if (isHttpDevelopment()) {
|
|
170
|
+
try {
|
|
171
|
+
localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
|
172
|
+
console.log('📦 Refresh token stored in localStorage (HTTP dev mode)');
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.warn('Could not store refresh token:', err);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// In production (HTTPS), refresh token should be in httpOnly cookie only
|
|
178
|
+
console.log('🔒 Refresh token managed by server httpOnly cookie (production mode)');
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
181
|
|
|
172
182
|
export function getRefreshToken() {
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
// For HTTP development, read from localStorage
|
|
184
|
+
if (isHttpDevelopment()) {
|
|
185
|
+
try {
|
|
186
|
+
const token = localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
187
|
+
return token;
|
|
188
|
+
} catch (err) {
|
|
189
|
+
console.warn('Could not read refresh token:', err);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// In production, refresh token is in httpOnly cookie (not accessible via JS)
|
|
195
|
+
// The refresh endpoint uses credentials: 'include' to send the cookie
|
|
196
|
+
return null;
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
export function clearRefreshToken() {
|
|
200
|
+
// Clear localStorage (for HTTP dev)
|
|
201
|
+
try {
|
|
202
|
+
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
// Ignore
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Clear cookie (for production)
|
|
188
208
|
try {
|
|
189
209
|
document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Strict${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
190
210
|
} catch (err) {
|
|
191
211
|
console.warn('Could not clear refresh token cookie:', err);
|
|
192
212
|
}
|
|
213
|
+
|
|
214
|
+
// Clear sessionStorage
|
|
193
215
|
try {
|
|
194
216
|
sessionStorage.removeItem(REFRESH_COOKIE);
|
|
195
217
|
} catch (err) {
|
|
196
|
-
|
|
218
|
+
// Ignore
|
|
197
219
|
}
|
|
198
220
|
}
|
|
199
221
|
|