@basictech/react 0.7.0-beta.3 → 0.7.0-beta.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/.turbo/turbo-build.log +10 -10
- package/changelog.md +12 -0
- package/dist/index.d.mts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +196 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +196 -81
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +229 -127
- package/src/index.ts +3 -7
- package/src/sync/index.ts +4 -4
- package/src/utils/storage.ts +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @basictech/react@0.
|
|
3
|
+
> @basictech/react@0.7.0-beta.3 build
|
|
4
4
|
> tsup
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
[34mCLI[39m Cleaning output folder
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
13
|
[34mESM[39m Build start
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
14
|
+
[32mCJS[39m [1mdist/index.js [22m[32m41.20 KB[39m
|
|
15
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m82.45 KB[39m
|
|
16
|
+
[32mCJS[39m ⚡️ Build success in 17ms
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m39.45 KB[39m
|
|
18
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m82.44 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 18ms
|
|
20
20
|
[34mDTS[39m Build start
|
|
21
|
-
[32mDTS[39m ⚡️ Build success in
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
23
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
21
|
+
[32mDTS[39m ⚡️ Build success in 905ms
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.76 KB[39m
|
|
23
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.76 KB[39m
|
package/changelog.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -7,7 +7,25 @@ interface BasicStorage {
|
|
|
7
7
|
set(key: string, value: string): Promise<void>;
|
|
8
8
|
remove(key: string): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
declare class LocalStorageAdapter implements BasicStorage {
|
|
11
|
+
get(key: string): Promise<string | null>;
|
|
12
|
+
set(key: string, value: string): Promise<void>;
|
|
13
|
+
remove(key: string): Promise<void>;
|
|
14
|
+
}
|
|
10
15
|
|
|
16
|
+
type AuthConfig = {
|
|
17
|
+
scopes?: string | string[];
|
|
18
|
+
server_url?: string;
|
|
19
|
+
ws_url?: string;
|
|
20
|
+
};
|
|
21
|
+
type BasicProviderProps = {
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
project_id?: string;
|
|
24
|
+
schema?: any;
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
storage?: BasicStorage;
|
|
27
|
+
auth?: AuthConfig;
|
|
28
|
+
};
|
|
11
29
|
declare enum DBStatus {
|
|
12
30
|
LOADING = "LOADING",
|
|
13
31
|
OFFLINE = "OFFLINE",
|
|
@@ -25,13 +43,7 @@ type User = {
|
|
|
25
43
|
};
|
|
26
44
|
fullName?: string;
|
|
27
45
|
};
|
|
28
|
-
declare function BasicProvider({ children, project_id, schema, debug, storage }:
|
|
29
|
-
children: React.ReactNode;
|
|
30
|
-
project_id?: string;
|
|
31
|
-
schema?: any;
|
|
32
|
-
debug?: boolean;
|
|
33
|
-
storage?: BasicStorage;
|
|
34
|
-
}): react_jsx_runtime.JSX.Element;
|
|
46
|
+
declare function BasicProvider({ children, project_id, schema, debug, storage, auth }: BasicProviderProps): react_jsx_runtime.JSX.Element;
|
|
35
47
|
declare function useBasic(): {
|
|
36
48
|
unicorn: string;
|
|
37
49
|
isAuthReady: boolean;
|
|
@@ -49,4 +61,4 @@ declare function useBasic(): {
|
|
|
49
61
|
dbStatus: DBStatus;
|
|
50
62
|
};
|
|
51
63
|
|
|
52
|
-
export { BasicProvider, useBasic };
|
|
64
|
+
export { AuthConfig, BasicProvider, BasicProviderProps, BasicStorage, LocalStorageAdapter, useBasic };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,25 @@ interface BasicStorage {
|
|
|
7
7
|
set(key: string, value: string): Promise<void>;
|
|
8
8
|
remove(key: string): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
declare class LocalStorageAdapter implements BasicStorage {
|
|
11
|
+
get(key: string): Promise<string | null>;
|
|
12
|
+
set(key: string, value: string): Promise<void>;
|
|
13
|
+
remove(key: string): Promise<void>;
|
|
14
|
+
}
|
|
10
15
|
|
|
16
|
+
type AuthConfig = {
|
|
17
|
+
scopes?: string | string[];
|
|
18
|
+
server_url?: string;
|
|
19
|
+
ws_url?: string;
|
|
20
|
+
};
|
|
21
|
+
type BasicProviderProps = {
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
project_id?: string;
|
|
24
|
+
schema?: any;
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
storage?: BasicStorage;
|
|
27
|
+
auth?: AuthConfig;
|
|
28
|
+
};
|
|
11
29
|
declare enum DBStatus {
|
|
12
30
|
LOADING = "LOADING",
|
|
13
31
|
OFFLINE = "OFFLINE",
|
|
@@ -25,13 +43,7 @@ type User = {
|
|
|
25
43
|
};
|
|
26
44
|
fullName?: string;
|
|
27
45
|
};
|
|
28
|
-
declare function BasicProvider({ children, project_id, schema, debug, storage }:
|
|
29
|
-
children: React.ReactNode;
|
|
30
|
-
project_id?: string;
|
|
31
|
-
schema?: any;
|
|
32
|
-
debug?: boolean;
|
|
33
|
-
storage?: BasicStorage;
|
|
34
|
-
}): react_jsx_runtime.JSX.Element;
|
|
46
|
+
declare function BasicProvider({ children, project_id, schema, debug, storage, auth }: BasicProviderProps): react_jsx_runtime.JSX.Element;
|
|
35
47
|
declare function useBasic(): {
|
|
36
48
|
unicorn: string;
|
|
37
49
|
isAuthReady: boolean;
|
|
@@ -49,4 +61,4 @@ declare function useBasic(): {
|
|
|
49
61
|
dbStatus: DBStatus;
|
|
50
62
|
};
|
|
51
63
|
|
|
52
|
-
export { BasicProvider, useBasic };
|
|
64
|
+
export { AuthConfig, BasicProvider, BasicProviderProps, BasicStorage, LocalStorageAdapter, useBasic };
|
package/dist/index.js
CHANGED
|
@@ -167,15 +167,15 @@ var BasicSync = class extends import_dexie2.Dexie {
|
|
|
167
167
|
this.version(2).stores({});
|
|
168
168
|
this.Collection.prototype.get = this.Collection.prototype.toArray;
|
|
169
169
|
}
|
|
170
|
-
async connect({ access_token }) {
|
|
171
|
-
const WS_URL =
|
|
170
|
+
async connect({ access_token, ws_url }) {
|
|
171
|
+
const WS_URL = ws_url || "wss://pds.basic.id/ws";
|
|
172
172
|
log("Connecting to", WS_URL);
|
|
173
173
|
await this.updateSyncNodes();
|
|
174
174
|
log("Starting connection...");
|
|
175
175
|
return this.syncable.connect("websocket", WS_URL, { authToken: access_token, schema: this.basic_schema });
|
|
176
176
|
}
|
|
177
|
-
async disconnect() {
|
|
178
|
-
const WS_URL =
|
|
177
|
+
async disconnect({ ws_url } = {}) {
|
|
178
|
+
const WS_URL = ws_url || "wss://pds.basic.id/ws";
|
|
179
179
|
return this.syncable.disconnect(WS_URL);
|
|
180
180
|
}
|
|
181
181
|
async updateSyncNodes() {
|
|
@@ -279,7 +279,7 @@ var BasicSync = class extends import_dexie2.Dexie {
|
|
|
279
279
|
};
|
|
280
280
|
|
|
281
281
|
// package.json
|
|
282
|
-
var version = "0.7.0-beta.
|
|
282
|
+
var version = "0.7.0-beta.4";
|
|
283
283
|
|
|
284
284
|
// src/updater/versionUpdater.ts
|
|
285
285
|
var VersionUpdater = class {
|
|
@@ -422,6 +422,8 @@ var STORAGE_KEYS = {
|
|
|
422
422
|
REFRESH_TOKEN: "basic_refresh_token",
|
|
423
423
|
USER_INFO: "basic_user_info",
|
|
424
424
|
AUTH_STATE: "basic_auth_state",
|
|
425
|
+
REDIRECT_URI: "basic_redirect_uri",
|
|
426
|
+
SERVER_URL: "basic_server_url",
|
|
425
427
|
DEBUG: "basic_debug"
|
|
426
428
|
};
|
|
427
429
|
function getCookie(name) {
|
|
@@ -619,6 +621,11 @@ async function validateAndCheckSchema(schema) {
|
|
|
619
621
|
|
|
620
622
|
// src/AuthContext.tsx
|
|
621
623
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
624
|
+
var DEFAULT_AUTH_CONFIG = {
|
|
625
|
+
scopes: "profile,email,app:admin",
|
|
626
|
+
server_url: "https://api.basic.tech",
|
|
627
|
+
ws_url: "wss://pds.basic.id/ws"
|
|
628
|
+
};
|
|
622
629
|
var BasicContext = (0, import_react.createContext)({
|
|
623
630
|
unicorn: "\u{1F984}",
|
|
624
631
|
isAuthReady: false,
|
|
@@ -639,7 +646,8 @@ function BasicProvider({
|
|
|
639
646
|
project_id,
|
|
640
647
|
schema,
|
|
641
648
|
debug = false,
|
|
642
|
-
storage
|
|
649
|
+
storage,
|
|
650
|
+
auth
|
|
643
651
|
}) {
|
|
644
652
|
const [isAuthReady, setIsAuthReady] = (0, import_react.useState)(false);
|
|
645
653
|
const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
|
|
@@ -653,6 +661,13 @@ function BasicProvider({
|
|
|
653
661
|
const [pendingRefresh, setPendingRefresh] = (0, import_react.useState)(false);
|
|
654
662
|
const syncRef = (0, import_react.useRef)(null);
|
|
655
663
|
const storageAdapter = storage || new LocalStorageAdapter();
|
|
664
|
+
const authConfig = {
|
|
665
|
+
scopes: auth?.scopes || DEFAULT_AUTH_CONFIG.scopes,
|
|
666
|
+
server_url: auth?.server_url || DEFAULT_AUTH_CONFIG.server_url,
|
|
667
|
+
ws_url: auth?.ws_url || DEFAULT_AUTH_CONFIG.ws_url
|
|
668
|
+
};
|
|
669
|
+
const scopesString = Array.isArray(authConfig.scopes) ? authConfig.scopes.join(" ") : authConfig.scopes;
|
|
670
|
+
const refreshPromiseRef = (0, import_react.useRef)(null);
|
|
656
671
|
const isDevMode = () => isDevelopment(debug);
|
|
657
672
|
const cleanOAuthParams = () => cleanOAuthParamsFromUrl();
|
|
658
673
|
(0, import_react.useEffect)(() => {
|
|
@@ -740,7 +755,10 @@ function BasicProvider({
|
|
|
740
755
|
return;
|
|
741
756
|
}
|
|
742
757
|
log("connecting to db...");
|
|
743
|
-
syncRef.current?.connect({
|
|
758
|
+
syncRef.current?.connect({
|
|
759
|
+
access_token: tok,
|
|
760
|
+
ws_url: authConfig.ws_url
|
|
761
|
+
}).catch((e) => {
|
|
744
762
|
log("error connecting to db", e);
|
|
745
763
|
});
|
|
746
764
|
}
|
|
@@ -750,6 +768,17 @@ function BasicProvider({
|
|
|
750
768
|
(0, import_react.useEffect)(() => {
|
|
751
769
|
const initializeAuth = async () => {
|
|
752
770
|
await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
|
|
771
|
+
const storedServerUrl = await storageAdapter.get(STORAGE_KEYS.SERVER_URL);
|
|
772
|
+
if (storedServerUrl && storedServerUrl !== authConfig.server_url) {
|
|
773
|
+
log("Server URL changed, clearing stored tokens");
|
|
774
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
775
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
776
|
+
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
|
|
777
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
778
|
+
clearCookie("basic_token");
|
|
779
|
+
clearCookie("basic_access_token");
|
|
780
|
+
}
|
|
781
|
+
await storageAdapter.set(STORAGE_KEYS.SERVER_URL, authConfig.server_url);
|
|
753
782
|
try {
|
|
754
783
|
const versionUpdater = createVersionUpdater(storageAdapter, version, getMigrations());
|
|
755
784
|
const updateResult = await versionUpdater.checkAndUpdate();
|
|
@@ -820,16 +849,21 @@ function BasicProvider({
|
|
|
820
849
|
(0, import_react.useEffect)(() => {
|
|
821
850
|
async function fetchUser(acc_token) {
|
|
822
851
|
console.info("fetching user");
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
852
|
+
try {
|
|
853
|
+
const response = await fetch(`${authConfig.server_url}/auth/userInfo`, {
|
|
854
|
+
method: "GET",
|
|
855
|
+
headers: {
|
|
856
|
+
"Authorization": `Bearer ${acc_token}`
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
if (!response.ok) {
|
|
860
|
+
throw new Error(`Failed to fetch user info: ${response.status}`);
|
|
861
|
+
}
|
|
862
|
+
const user2 = await response.json();
|
|
863
|
+
if (user2.error) {
|
|
864
|
+
log("error fetching user", user2.error);
|
|
865
|
+
throw new Error(`User info error: ${user2.error}`);
|
|
827
866
|
}
|
|
828
|
-
}).then((response) => response.json()).catch((error2) => log("Error:", error2));
|
|
829
|
-
if (user2.error) {
|
|
830
|
-
log("error fetching user", user2.error);
|
|
831
|
-
return;
|
|
832
|
-
} else {
|
|
833
867
|
if (token?.refresh_token) {
|
|
834
868
|
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token);
|
|
835
869
|
}
|
|
@@ -840,6 +874,9 @@ function BasicProvider({
|
|
|
840
874
|
setUser(user2);
|
|
841
875
|
setIsSignedIn(true);
|
|
842
876
|
setIsAuthReady(true);
|
|
877
|
+
} catch (error2) {
|
|
878
|
+
log("Failed to fetch user info:", error2);
|
|
879
|
+
setIsAuthReady(true);
|
|
843
880
|
}
|
|
844
881
|
}
|
|
845
882
|
async function checkToken() {
|
|
@@ -849,7 +886,8 @@ function BasicProvider({
|
|
|
849
886
|
return;
|
|
850
887
|
}
|
|
851
888
|
const decoded = (0, import_jwt_decode.jwtDecode)(token?.access_token);
|
|
852
|
-
const
|
|
889
|
+
const expirationBuffer = 5;
|
|
890
|
+
const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3 + expirationBuffer;
|
|
853
891
|
if (isExpired) {
|
|
854
892
|
log("token is expired - refreshing ...");
|
|
855
893
|
try {
|
|
@@ -884,13 +922,15 @@ function BasicProvider({
|
|
|
884
922
|
if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
|
|
885
923
|
throw new Error("Invalid redirect URI provided");
|
|
886
924
|
}
|
|
887
|
-
|
|
925
|
+
await storageAdapter.set(STORAGE_KEYS.REDIRECT_URI, redirectUrl);
|
|
926
|
+
log("Stored redirect_uri for token exchange:", redirectUrl);
|
|
927
|
+
let baseUrl = `${authConfig.server_url}/auth/authorize`;
|
|
888
928
|
baseUrl += `?client_id=${project_id}`;
|
|
889
929
|
baseUrl += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
|
|
890
930
|
baseUrl += `&response_type=code`;
|
|
891
|
-
baseUrl += `&scope
|
|
931
|
+
baseUrl += `&scope=${encodeURIComponent(scopesString)}`;
|
|
892
932
|
baseUrl += `&state=${randomState}`;
|
|
893
|
-
log("Generated sign-in link successfully");
|
|
933
|
+
log("Generated sign-in link successfully with scopes:", scopesString);
|
|
894
934
|
return baseUrl;
|
|
895
935
|
} catch (error2) {
|
|
896
936
|
log("Error generating sign-in link:", error2);
|
|
@@ -906,7 +946,9 @@ function BasicProvider({
|
|
|
906
946
|
}
|
|
907
947
|
const signInLink = await getSignInLink();
|
|
908
948
|
log("Generated sign-in link:", signInLink);
|
|
909
|
-
|
|
949
|
+
try {
|
|
950
|
+
new URL(signInLink);
|
|
951
|
+
} catch {
|
|
910
952
|
log("Error: Invalid sign-in link generated");
|
|
911
953
|
throw new Error("Failed to generate valid sign-in URL");
|
|
912
954
|
}
|
|
@@ -963,6 +1005,8 @@ function BasicProvider({
|
|
|
963
1005
|
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
|
|
964
1006
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
965
1007
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
1008
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1009
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL);
|
|
966
1010
|
if (syncRef.current) {
|
|
967
1011
|
(async () => {
|
|
968
1012
|
try {
|
|
@@ -982,6 +1026,18 @@ function BasicProvider({
|
|
|
982
1026
|
const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
983
1027
|
if (refreshToken) {
|
|
984
1028
|
log("No token in memory, attempting to refresh from storage");
|
|
1029
|
+
if (refreshPromiseRef.current) {
|
|
1030
|
+
log("Token refresh already in progress, waiting...");
|
|
1031
|
+
try {
|
|
1032
|
+
const newToken = await refreshPromiseRef.current;
|
|
1033
|
+
if (newToken?.access_token) {
|
|
1034
|
+
return newToken.access_token;
|
|
1035
|
+
}
|
|
1036
|
+
} catch (error2) {
|
|
1037
|
+
log("In-flight refresh failed:", error2);
|
|
1038
|
+
throw error2;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
985
1041
|
try {
|
|
986
1042
|
const newToken = await fetchToken(refreshToken, true);
|
|
987
1043
|
if (newToken?.access_token) {
|
|
@@ -1004,9 +1060,24 @@ function BasicProvider({
|
|
|
1004
1060
|
throw new Error("no token found");
|
|
1005
1061
|
}
|
|
1006
1062
|
const decoded = (0, import_jwt_decode.jwtDecode)(token?.access_token);
|
|
1007
|
-
const
|
|
1063
|
+
const expirationBuffer = 5;
|
|
1064
|
+
const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3 + expirationBuffer;
|
|
1008
1065
|
if (isExpired) {
|
|
1009
1066
|
log("token is expired - refreshing ...");
|
|
1067
|
+
if (refreshPromiseRef.current) {
|
|
1068
|
+
log("Token refresh already in progress, waiting...");
|
|
1069
|
+
try {
|
|
1070
|
+
const newToken = await refreshPromiseRef.current;
|
|
1071
|
+
return newToken?.access_token || "";
|
|
1072
|
+
} catch (error2) {
|
|
1073
|
+
log("In-flight refresh failed:", error2);
|
|
1074
|
+
if (error2.message.includes("offline") || error2.message.includes("Network")) {
|
|
1075
|
+
log("Network issue - using expired token until network is restored");
|
|
1076
|
+
return token.access_token;
|
|
1077
|
+
}
|
|
1078
|
+
throw error2;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1010
1081
|
const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1011
1082
|
if (refreshToken) {
|
|
1012
1083
|
try {
|
|
@@ -1027,73 +1098,117 @@ function BasicProvider({
|
|
|
1027
1098
|
return token?.access_token || "";
|
|
1028
1099
|
};
|
|
1029
1100
|
const fetchToken = async (codeOrRefreshToken, isRefreshToken = false) => {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const requestBody = isRefreshToken ? {
|
|
1037
|
-
grant_type: "refresh_token",
|
|
1038
|
-
refresh_token: codeOrRefreshToken
|
|
1039
|
-
} : {
|
|
1040
|
-
grant_type: "authorization_code",
|
|
1041
|
-
code: codeOrRefreshToken
|
|
1042
|
-
};
|
|
1043
|
-
const token2 = await fetch("https://api.basic.tech/auth/token", {
|
|
1044
|
-
method: "POST",
|
|
1045
|
-
headers: {
|
|
1046
|
-
"Content-Type": "application/json"
|
|
1047
|
-
},
|
|
1048
|
-
body: JSON.stringify(requestBody)
|
|
1049
|
-
}).then((response) => response.json()).catch((error2) => {
|
|
1050
|
-
log("Network error fetching token:", error2);
|
|
1101
|
+
if (isRefreshToken && refreshPromiseRef.current) {
|
|
1102
|
+
log("Reusing in-flight refresh token request");
|
|
1103
|
+
return refreshPromiseRef.current;
|
|
1104
|
+
}
|
|
1105
|
+
const refreshPromise = (async () => {
|
|
1106
|
+
try {
|
|
1051
1107
|
if (!isOnline) {
|
|
1108
|
+
log("Network is offline, marking refresh as pending");
|
|
1052
1109
|
setPendingRefresh(true);
|
|
1053
1110
|
throw new Error("Network offline - refresh will be retried when online");
|
|
1054
1111
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1112
|
+
let requestBody;
|
|
1113
|
+
if (isRefreshToken) {
|
|
1114
|
+
requestBody = {
|
|
1115
|
+
grant_type: "refresh_token",
|
|
1116
|
+
refresh_token: codeOrRefreshToken
|
|
1117
|
+
};
|
|
1118
|
+
if (project_id) {
|
|
1119
|
+
requestBody.client_id = project_id;
|
|
1120
|
+
}
|
|
1121
|
+
} else {
|
|
1122
|
+
requestBody = {
|
|
1123
|
+
grant_type: "authorization_code",
|
|
1124
|
+
code: codeOrRefreshToken
|
|
1125
|
+
};
|
|
1126
|
+
const storedRedirectUri = await storageAdapter.get(STORAGE_KEYS.REDIRECT_URI);
|
|
1127
|
+
if (storedRedirectUri) {
|
|
1128
|
+
requestBody.redirect_uri = storedRedirectUri;
|
|
1129
|
+
log("Including redirect_uri in token exchange:", storedRedirectUri);
|
|
1130
|
+
} else {
|
|
1131
|
+
log("Warning: No redirect_uri found in storage for token exchange");
|
|
1132
|
+
}
|
|
1133
|
+
if (project_id) {
|
|
1134
|
+
requestBody.client_id = project_id;
|
|
1135
|
+
}
|
|
1062
1136
|
}
|
|
1063
|
-
|
|
1064
|
-
await
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1137
|
+
log("Token exchange request body:", { ...requestBody, refresh_token: isRefreshToken ? "[REDACTED]" : void 0, code: !isRefreshToken ? "[REDACTED]" : void 0 });
|
|
1138
|
+
const token2 = await fetch(`${authConfig.server_url}/auth/token`, {
|
|
1139
|
+
method: "POST",
|
|
1140
|
+
headers: {
|
|
1141
|
+
"Content-Type": "application/json"
|
|
1142
|
+
},
|
|
1143
|
+
body: JSON.stringify(requestBody)
|
|
1144
|
+
}).then((response) => response.json()).catch((error2) => {
|
|
1145
|
+
log("Network error fetching token:", error2);
|
|
1146
|
+
if (!isOnline) {
|
|
1147
|
+
setPendingRefresh(true);
|
|
1148
|
+
throw new Error("Network offline - refresh will be retried when online");
|
|
1149
|
+
}
|
|
1150
|
+
throw new Error("Network error during token refresh");
|
|
1151
|
+
});
|
|
1152
|
+
if (token2.error) {
|
|
1153
|
+
log("error fetching token", token2.error);
|
|
1154
|
+
if (token2.error.includes("network") || token2.error.includes("timeout")) {
|
|
1155
|
+
setPendingRefresh(true);
|
|
1156
|
+
throw new Error("Network issue - refresh will be retried when online");
|
|
1157
|
+
}
|
|
1158
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1159
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
1160
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1161
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL);
|
|
1162
|
+
clearCookie("basic_token");
|
|
1163
|
+
clearCookie("basic_access_token");
|
|
1164
|
+
setUser({});
|
|
1165
|
+
setIsSignedIn(false);
|
|
1166
|
+
setToken(null);
|
|
1167
|
+
setIsAuthReady(true);
|
|
1168
|
+
throw new Error(`Token refresh failed: ${token2.error}`);
|
|
1169
|
+
} else {
|
|
1170
|
+
setToken(token2);
|
|
1171
|
+
setPendingRefresh(false);
|
|
1172
|
+
if (token2.refresh_token) {
|
|
1173
|
+
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token2.refresh_token);
|
|
1174
|
+
log("Updated refresh token in storage");
|
|
1175
|
+
}
|
|
1176
|
+
if (!isRefreshToken) {
|
|
1177
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1178
|
+
log("Cleaned up redirect_uri from storage after successful exchange");
|
|
1179
|
+
}
|
|
1180
|
+
setCookie("basic_access_token", token2.access_token, { httpOnly: false });
|
|
1181
|
+
setCookie("basic_token", JSON.stringify(token2));
|
|
1182
|
+
log("Updated access token and full token in cookies");
|
|
1078
1183
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1184
|
+
return token2;
|
|
1185
|
+
} catch (error2) {
|
|
1186
|
+
log("Token refresh error:", error2);
|
|
1187
|
+
if (!error2.message.includes("offline") && !error2.message.includes("Network")) {
|
|
1188
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1189
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
1190
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1191
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL);
|
|
1192
|
+
clearCookie("basic_token");
|
|
1193
|
+
clearCookie("basic_access_token");
|
|
1194
|
+
setUser({});
|
|
1195
|
+
setIsSignedIn(false);
|
|
1196
|
+
setToken(null);
|
|
1197
|
+
setIsAuthReady(true);
|
|
1198
|
+
}
|
|
1199
|
+
throw error2;
|
|
1094
1200
|
}
|
|
1095
|
-
|
|
1201
|
+
})();
|
|
1202
|
+
if (isRefreshToken) {
|
|
1203
|
+
refreshPromiseRef.current = refreshPromise;
|
|
1204
|
+
refreshPromise.finally(() => {
|
|
1205
|
+
if (refreshPromiseRef.current === refreshPromise) {
|
|
1206
|
+
refreshPromiseRef.current = null;
|
|
1207
|
+
log("Cleared refresh promise reference");
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1096
1210
|
}
|
|
1211
|
+
return refreshPromise;
|
|
1097
1212
|
};
|
|
1098
1213
|
const noDb = {
|
|
1099
1214
|
collection: () => {
|