@tenxyte/core 0.9.3 → 0.9.4
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/README.md +47 -2
- package/dist/index.cjs +35 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -8
- package/dist/index.d.ts +29 -8
- package/dist/index.js +35 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,6 +88,10 @@ const tx = new TenxyteClient({
|
|
|
88
88
|
|
|
89
89
|
// Optional — override auto-detected device info
|
|
90
90
|
deviceInfoOverride: { app_name: 'MyApp', app_version: '2.0.0' },
|
|
91
|
+
|
|
92
|
+
// Optional — cookie-based refresh token transport (default: false)
|
|
93
|
+
// Enable when backend has TENXYTE_REFRESH_TOKEN_COOKIE_ENABLED=True
|
|
94
|
+
cookieMode: false,
|
|
91
95
|
});
|
|
92
96
|
```
|
|
93
97
|
|
|
@@ -128,9 +132,16 @@ const tokens = await tx.auth.verifyMagicLink(urlToken);
|
|
|
128
132
|
|
|
129
133
|
// Social OAuth2
|
|
130
134
|
const tokens = await tx.auth.loginWithSocial('google', { id_token: 'jwt...' });
|
|
131
|
-
const tokens = await tx.auth.handleSocialCallback('github', 'auth_code', 'https://myapp.com/cb');
|
|
132
135
|
|
|
133
|
-
//
|
|
136
|
+
// Social OAuth2 with PKCE (RFC 7636)
|
|
137
|
+
const tokens = await tx.auth.loginWithSocial('google', {
|
|
138
|
+
code: 'auth_code',
|
|
139
|
+
redirect_uri: 'https://myapp.com/cb',
|
|
140
|
+
code_verifier: 'pkce_verifier_string',
|
|
141
|
+
});
|
|
142
|
+
const tokens = await tx.auth.handleSocialCallback('github', 'auth_code', 'https://myapp.com/cb', 'pkce_verifier');
|
|
143
|
+
|
|
144
|
+
// Session management (refreshToken param is optional in cookie mode)
|
|
134
145
|
await tx.auth.logout('refresh_token_value');
|
|
135
146
|
await tx.auth.logoutAll();
|
|
136
147
|
await tx.auth.refreshToken('refresh_token_value');
|
|
@@ -384,6 +395,39 @@ const state = await tx.getState();
|
|
|
384
395
|
|
|
385
396
|
---
|
|
386
397
|
|
|
398
|
+
## Migration Guide: v0.9 → v0.10
|
|
399
|
+
|
|
400
|
+
### New Features in v0.10
|
|
401
|
+
|
|
402
|
+
- **Cookie-based refresh tokens** — New `cookieMode` config option. When enabled, the SDK uses `credentials: 'include'` for refresh/logout requests and does not require a stored refresh token for silent refresh.
|
|
403
|
+
- **PKCE support** — `code_verifier` parameter added to `SocialLoginRequest` and `handleSocialCallback()` for RFC 7636 compliance.
|
|
404
|
+
- **Expanded error codes** — `TenxyteErrorCode` now includes all backend error codes: `MISSING_REFRESH_TOKEN`, `INVALID_REDIRECT_URI`, `PASSWORD_BREACHED`, `PASSWORD_REUSED`, `WEBAUTHN_*`, `LINK_EXPIRED`, `2FA_ALREADY_ENABLED`, and more.
|
|
405
|
+
- **Optional refresh token in responses** — `TokenPair.refresh_token` is now optional (absent when cookie mode is enabled on the backend).
|
|
406
|
+
|
|
407
|
+
### Breaking Changes
|
|
408
|
+
|
|
409
|
+
1. **`TokenPair.refresh_token` is now optional** — If you access `tokens.refresh_token` without a null check, add one:
|
|
410
|
+
```typescript
|
|
411
|
+
if (tokens.refresh_token) {
|
|
412
|
+
// Store or use the refresh token
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
2. **`logout()` and `refreshToken()` parameters are now optional** — In cookie mode, you can call them without arguments:
|
|
417
|
+
```typescript
|
|
418
|
+
// Cookie mode (refresh token is in HttpOnly cookie)
|
|
419
|
+
await tx.auth.logout();
|
|
420
|
+
await tx.auth.refreshToken();
|
|
421
|
+
|
|
422
|
+
// Classic mode (still works)
|
|
423
|
+
await tx.auth.logout('refresh_token_value');
|
|
424
|
+
await tx.auth.refreshToken('refresh_token_value');
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
3. **`handleSocialCallback()` now accepts an optional 4th parameter** (`codeVerifier`).
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
387
431
|
## Migration Guide: v0.8 → v0.9
|
|
388
432
|
|
|
389
433
|
### Breaking Changes
|
|
@@ -436,6 +480,7 @@ const state = await tx.getState();
|
|
|
436
480
|
- High-level helpers (`isAuthenticated`, `getCurrentUser`, `isTokenExpired`)
|
|
437
481
|
- `getState()` for framework wrapper integration
|
|
438
482
|
- EventEmitter for reactive state (`session:expired`, `token:refreshed`, etc.)
|
|
483
|
+
- WebAuthn / Passkeys (FIDO2) support
|
|
439
484
|
|
|
440
485
|
---
|
|
441
486
|
|
package/dist/index.cjs
CHANGED
|
@@ -174,7 +174,8 @@ function resolveConfig(config) {
|
|
|
174
174
|
logger: config.logger ?? NOOP_LOGGER,
|
|
175
175
|
logLevel: config.logLevel ?? "silent",
|
|
176
176
|
deviceInfoOverride: config.deviceInfoOverride,
|
|
177
|
-
retryConfig: config.retryConfig
|
|
177
|
+
retryConfig: config.retryConfig,
|
|
178
|
+
cookieMode: config.cookieMode ?? false
|
|
178
179
|
};
|
|
179
180
|
}
|
|
180
181
|
|
|
@@ -396,7 +397,7 @@ function createAuthInterceptor(storage, context) {
|
|
|
396
397
|
return { ...request, headers };
|
|
397
398
|
};
|
|
398
399
|
}
|
|
399
|
-
function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefreshed) {
|
|
400
|
+
function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefreshed, cookieMode = false) {
|
|
400
401
|
let isRefreshing = false;
|
|
401
402
|
let refreshQueue = [];
|
|
402
403
|
const processQueue = (error, token = null) => {
|
|
@@ -406,7 +407,7 @@ function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefr
|
|
|
406
407
|
return async (response, request) => {
|
|
407
408
|
if (response.status === 401 && !request.url.includes("/auth/refresh") && !request.url.includes("/auth/login")) {
|
|
408
409
|
const refreshToken = await storage.getItem("tx_refresh");
|
|
409
|
-
if (!refreshToken) {
|
|
410
|
+
if (!refreshToken && !cookieMode) {
|
|
410
411
|
onSessionExpired();
|
|
411
412
|
return response;
|
|
412
413
|
}
|
|
@@ -424,10 +425,15 @@ function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefr
|
|
|
424
425
|
}
|
|
425
426
|
isRefreshing = true;
|
|
426
427
|
try {
|
|
428
|
+
const refreshBody = {};
|
|
429
|
+
if (refreshToken) {
|
|
430
|
+
refreshBody.refresh_token = refreshToken;
|
|
431
|
+
}
|
|
427
432
|
const refreshResponse = await fetch(`${client["baseUrl"]}/auth/refresh/`, {
|
|
428
433
|
method: "POST",
|
|
429
434
|
headers: { "Content-Type": "application/json" },
|
|
430
|
-
body: JSON.stringify(
|
|
435
|
+
body: JSON.stringify(refreshBody),
|
|
436
|
+
...cookieMode ? { credentials: "include" } : {}
|
|
431
437
|
});
|
|
432
438
|
if (!refreshResponse.ok) {
|
|
433
439
|
throw new Error("Refresh failed");
|
|
@@ -585,10 +591,16 @@ var AuthModule = class {
|
|
|
585
591
|
/**
|
|
586
592
|
* Logout from the current session.
|
|
587
593
|
* Informs the backend to immediately revoke the specified refresh token.
|
|
588
|
-
*
|
|
594
|
+
* When cookie mode is enabled, the refresh token parameter is optional —
|
|
595
|
+
* the server reads it from the HttpOnly cookie and clears it via Set-Cookie.
|
|
596
|
+
* @param refreshToken - The refresh token to revoke (optional in cookie mode).
|
|
589
597
|
*/
|
|
590
598
|
async logout(refreshToken) {
|
|
591
|
-
|
|
599
|
+
const body = {};
|
|
600
|
+
if (refreshToken) {
|
|
601
|
+
body.refresh_token = refreshToken;
|
|
602
|
+
}
|
|
603
|
+
await this.client.post("/api/v1/auth/logout/", body);
|
|
592
604
|
await this.clearTokens();
|
|
593
605
|
}
|
|
594
606
|
/**
|
|
@@ -602,11 +614,17 @@ var AuthModule = class {
|
|
|
602
614
|
/**
|
|
603
615
|
* Manually refresh the access token using a valid refresh token.
|
|
604
616
|
* The refresh token is automatically rotated for improved security.
|
|
605
|
-
*
|
|
617
|
+
* When cookie mode is enabled, the refresh token parameter is optional —
|
|
618
|
+
* the server reads it from the HttpOnly cookie.
|
|
619
|
+
* @param refreshToken - The current refresh token (optional in cookie mode).
|
|
606
620
|
* @returns A new token pair (access + rotated refresh).
|
|
607
621
|
*/
|
|
608
622
|
async refreshToken(refreshToken) {
|
|
609
|
-
const
|
|
623
|
+
const body = {};
|
|
624
|
+
if (refreshToken) {
|
|
625
|
+
body.refresh_token = refreshToken;
|
|
626
|
+
}
|
|
627
|
+
const tokens = await this.client.post("/api/v1/auth/refresh/", body);
|
|
610
628
|
return this.persistTokens(tokens);
|
|
611
629
|
}
|
|
612
630
|
/**
|
|
@@ -641,11 +659,16 @@ var AuthModule = class {
|
|
|
641
659
|
* @param provider - The OAuth provider ('google', 'github', etc.)
|
|
642
660
|
* @param code - The authorization code retrieved from the query string parameters.
|
|
643
661
|
* @param redirectUri - The original redirect URI that was requested.
|
|
662
|
+
* @param codeVerifier - Optional PKCE code verifier (RFC 7636).
|
|
644
663
|
* @returns An active session token pair after successful code exchange.
|
|
645
664
|
*/
|
|
646
|
-
async handleSocialCallback(provider, code, redirectUri) {
|
|
665
|
+
async handleSocialCallback(provider, code, redirectUri, codeVerifier) {
|
|
666
|
+
const params = { code, redirect_uri: redirectUri };
|
|
667
|
+
if (codeVerifier) {
|
|
668
|
+
params.code_verifier = codeVerifier;
|
|
669
|
+
}
|
|
647
670
|
const tokens = await this.client.get(`/api/v1/auth/social/${provider}/callback/`, {
|
|
648
|
-
params
|
|
671
|
+
params
|
|
649
672
|
});
|
|
650
673
|
return this.persistTokens(tokens);
|
|
651
674
|
}
|
|
@@ -1748,7 +1771,8 @@ var TenxyteClient = class {
|
|
|
1748
1771
|
this.rbac.setToken(accessToken);
|
|
1749
1772
|
this.emit("token:refreshed", { accessToken });
|
|
1750
1773
|
this.emit("token:stored", { accessToken, refreshToken });
|
|
1751
|
-
}
|
|
1774
|
+
},
|
|
1775
|
+
this.config.cookieMode
|
|
1752
1776
|
)
|
|
1753
1777
|
);
|
|
1754
1778
|
}
|