@vaultix.ai/react 0.2.0 → 0.2.1
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/dist/index.js +87 -18
- package/dist/index.mjs +87 -18
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -60,6 +60,26 @@ function resolveApiOrigin(key, apiUrlProp) {
|
|
|
60
60
|
throw new Error("Publishable key has an unreadable API origin payload.");
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
var STORAGE_KEY = "vaultix_jwt";
|
|
64
|
+
function storeJwt(jwt) {
|
|
65
|
+
try {
|
|
66
|
+
sessionStorage.setItem(STORAGE_KEY, jwt);
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function loadJwt() {
|
|
71
|
+
try {
|
|
72
|
+
return sessionStorage.getItem(STORAGE_KEY);
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function clearJwt() {
|
|
78
|
+
try {
|
|
79
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
63
83
|
var VaultixContext = (0, import_react.createContext)(null);
|
|
64
84
|
function useVaultixContext() {
|
|
65
85
|
const ctx = (0, import_react.useContext)(VaultixContext);
|
|
@@ -82,42 +102,89 @@ function VaultixProvider({
|
|
|
82
102
|
const [user, setUser] = (0, import_react.useState)(null);
|
|
83
103
|
const [session, setSession] = (0, import_react.useState)(null);
|
|
84
104
|
const [organization, setOrganization] = (0, import_react.useState)(null);
|
|
105
|
+
const jwtRef = (0, import_react.useRef)(null);
|
|
85
106
|
const refreshTimerRef = (0, import_react.useRef)(null);
|
|
107
|
+
const clearAuth = (0, import_react.useCallback)(() => {
|
|
108
|
+
jwtRef.current = null;
|
|
109
|
+
clearJwt();
|
|
110
|
+
setUser(null);
|
|
111
|
+
setSession(null);
|
|
112
|
+
setOrganization(null);
|
|
113
|
+
}, []);
|
|
86
114
|
const scheduleRefresh = (0, import_react.useCallback)(
|
|
87
115
|
(expiresAt) => {
|
|
88
116
|
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
89
|
-
const msUntilRefresh = expiresAt * 1e3 - Date.now() -
|
|
117
|
+
const msUntilRefresh = expiresAt * 1e3 - Date.now() - 3e4;
|
|
90
118
|
if (msUntilRefresh <= 0) return;
|
|
91
119
|
refreshTimerRef.current = setTimeout(async () => {
|
|
92
120
|
try {
|
|
93
|
-
const
|
|
121
|
+
const headers = {};
|
|
122
|
+
if (jwtRef.current) headers["Authorization"] = `Bearer ${jwtRef.current}`;
|
|
123
|
+
const res = await fetch(`${apiOrigin}/api/v1/session/refresh`, {
|
|
94
124
|
method: "POST",
|
|
95
|
-
credentials: "include"
|
|
125
|
+
credentials: jwtRef.current ? "omit" : "include",
|
|
126
|
+
headers
|
|
96
127
|
});
|
|
97
128
|
if (!res.ok) {
|
|
98
|
-
|
|
99
|
-
setSession(null);
|
|
100
|
-
setOrganization(null);
|
|
129
|
+
clearAuth();
|
|
101
130
|
return;
|
|
102
131
|
}
|
|
103
132
|
const data = await res.json();
|
|
133
|
+
if (data.session_jwt) {
|
|
134
|
+
jwtRef.current = data.session_jwt;
|
|
135
|
+
storeJwt(data.session_jwt);
|
|
136
|
+
}
|
|
104
137
|
setSession(data.session);
|
|
105
138
|
scheduleRefresh(data.session.expiresAt);
|
|
106
139
|
} catch {
|
|
107
140
|
}
|
|
108
141
|
}, msUntilRefresh);
|
|
109
142
|
},
|
|
110
|
-
[apiOrigin]
|
|
143
|
+
[apiOrigin, clearAuth]
|
|
111
144
|
);
|
|
112
145
|
(0, import_react.useEffect)(() => {
|
|
113
146
|
let cancelled = false;
|
|
114
147
|
async function hydrate() {
|
|
148
|
+
let jwt = null;
|
|
149
|
+
if (typeof window !== "undefined") {
|
|
150
|
+
const url = new URL(window.location.href);
|
|
151
|
+
const handshakeToken = url.searchParams.get("__vaultix_handshake");
|
|
152
|
+
if (handshakeToken) {
|
|
153
|
+
url.searchParams.delete("__vaultix_handshake");
|
|
154
|
+
window.history.replaceState({}, "", url.toString());
|
|
155
|
+
try {
|
|
156
|
+
const res = await fetch(`${apiOrigin}/api/v1/tokens/exchange`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "Content-Type": "application/json" },
|
|
159
|
+
body: JSON.stringify({ handshake_token: handshakeToken })
|
|
160
|
+
});
|
|
161
|
+
if (res.ok) {
|
|
162
|
+
const data = await res.json();
|
|
163
|
+
jwt = data.session_jwt;
|
|
164
|
+
storeJwt(jwt);
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
jwt = loadJwt();
|
|
170
|
+
}
|
|
171
|
+
jwtRef.current = jwt;
|
|
172
|
+
}
|
|
115
173
|
try {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
174
|
+
const headers = {
|
|
175
|
+
"X-Vaultix-Publishable-Key": publishableKey
|
|
176
|
+
};
|
|
177
|
+
if (jwt) headers["Authorization"] = `Bearer ${jwt}`;
|
|
178
|
+
const res = await fetch(`${apiOrigin}/api/v1/me`, {
|
|
179
|
+
// Use Bearer token for cross-domain; fall back to cookie for same-domain
|
|
180
|
+
credentials: jwt ? "omit" : "include",
|
|
181
|
+
headers
|
|
119
182
|
});
|
|
120
183
|
if (!res.ok) {
|
|
184
|
+
if (jwt) {
|
|
185
|
+
clearJwt();
|
|
186
|
+
jwtRef.current = null;
|
|
187
|
+
}
|
|
121
188
|
if (!cancelled) setIsLoaded(true);
|
|
122
189
|
return;
|
|
123
190
|
}
|
|
@@ -140,19 +207,21 @@ function VaultixProvider({
|
|
|
140
207
|
};
|
|
141
208
|
}, [apiOrigin, publishableKey, scheduleRefresh]);
|
|
142
209
|
const signOut = (0, import_react.useCallback)(async () => {
|
|
210
|
+
const jwt = jwtRef.current;
|
|
211
|
+
clearAuth();
|
|
212
|
+
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
143
213
|
try {
|
|
144
|
-
|
|
214
|
+
const headers = {};
|
|
215
|
+
if (jwt) headers["Authorization"] = `Bearer ${jwt}`;
|
|
216
|
+
await fetch(`${apiOrigin}/api/v1/session`, {
|
|
145
217
|
method: "DELETE",
|
|
146
|
-
credentials: "include"
|
|
218
|
+
credentials: jwt ? "omit" : "include",
|
|
219
|
+
headers
|
|
147
220
|
});
|
|
148
221
|
} catch {
|
|
149
|
-
} finally {
|
|
150
|
-
setUser(null);
|
|
151
|
-
setSession(null);
|
|
152
|
-
setOrganization(null);
|
|
153
|
-
if (afterSignInUrl) window.location.href = afterSignInUrl;
|
|
154
222
|
}
|
|
155
|
-
|
|
223
|
+
if (afterSignInUrl) window.location.href = afterSignInUrl;
|
|
224
|
+
}, [apiOrigin, afterSignInUrl, clearAuth]);
|
|
156
225
|
const value = {
|
|
157
226
|
user,
|
|
158
227
|
session,
|
package/dist/index.mjs
CHANGED
|
@@ -28,6 +28,26 @@ function resolveApiOrigin(key, apiUrlProp) {
|
|
|
28
28
|
throw new Error("Publishable key has an unreadable API origin payload.");
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
var STORAGE_KEY = "vaultix_jwt";
|
|
32
|
+
function storeJwt(jwt) {
|
|
33
|
+
try {
|
|
34
|
+
sessionStorage.setItem(STORAGE_KEY, jwt);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function loadJwt() {
|
|
39
|
+
try {
|
|
40
|
+
return sessionStorage.getItem(STORAGE_KEY);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function clearJwt() {
|
|
46
|
+
try {
|
|
47
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
31
51
|
var VaultixContext = createContext(null);
|
|
32
52
|
function useVaultixContext() {
|
|
33
53
|
const ctx = useContext(VaultixContext);
|
|
@@ -50,42 +70,89 @@ function VaultixProvider({
|
|
|
50
70
|
const [user, setUser] = useState(null);
|
|
51
71
|
const [session, setSession] = useState(null);
|
|
52
72
|
const [organization, setOrganization] = useState(null);
|
|
73
|
+
const jwtRef = useRef(null);
|
|
53
74
|
const refreshTimerRef = useRef(null);
|
|
75
|
+
const clearAuth = useCallback(() => {
|
|
76
|
+
jwtRef.current = null;
|
|
77
|
+
clearJwt();
|
|
78
|
+
setUser(null);
|
|
79
|
+
setSession(null);
|
|
80
|
+
setOrganization(null);
|
|
81
|
+
}, []);
|
|
54
82
|
const scheduleRefresh = useCallback(
|
|
55
83
|
(expiresAt) => {
|
|
56
84
|
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
57
|
-
const msUntilRefresh = expiresAt * 1e3 - Date.now() -
|
|
85
|
+
const msUntilRefresh = expiresAt * 1e3 - Date.now() - 3e4;
|
|
58
86
|
if (msUntilRefresh <= 0) return;
|
|
59
87
|
refreshTimerRef.current = setTimeout(async () => {
|
|
60
88
|
try {
|
|
61
|
-
const
|
|
89
|
+
const headers = {};
|
|
90
|
+
if (jwtRef.current) headers["Authorization"] = `Bearer ${jwtRef.current}`;
|
|
91
|
+
const res = await fetch(`${apiOrigin}/api/v1/session/refresh`, {
|
|
62
92
|
method: "POST",
|
|
63
|
-
credentials: "include"
|
|
93
|
+
credentials: jwtRef.current ? "omit" : "include",
|
|
94
|
+
headers
|
|
64
95
|
});
|
|
65
96
|
if (!res.ok) {
|
|
66
|
-
|
|
67
|
-
setSession(null);
|
|
68
|
-
setOrganization(null);
|
|
97
|
+
clearAuth();
|
|
69
98
|
return;
|
|
70
99
|
}
|
|
71
100
|
const data = await res.json();
|
|
101
|
+
if (data.session_jwt) {
|
|
102
|
+
jwtRef.current = data.session_jwt;
|
|
103
|
+
storeJwt(data.session_jwt);
|
|
104
|
+
}
|
|
72
105
|
setSession(data.session);
|
|
73
106
|
scheduleRefresh(data.session.expiresAt);
|
|
74
107
|
} catch {
|
|
75
108
|
}
|
|
76
109
|
}, msUntilRefresh);
|
|
77
110
|
},
|
|
78
|
-
[apiOrigin]
|
|
111
|
+
[apiOrigin, clearAuth]
|
|
79
112
|
);
|
|
80
113
|
useEffect(() => {
|
|
81
114
|
let cancelled = false;
|
|
82
115
|
async function hydrate() {
|
|
116
|
+
let jwt = null;
|
|
117
|
+
if (typeof window !== "undefined") {
|
|
118
|
+
const url = new URL(window.location.href);
|
|
119
|
+
const handshakeToken = url.searchParams.get("__vaultix_handshake");
|
|
120
|
+
if (handshakeToken) {
|
|
121
|
+
url.searchParams.delete("__vaultix_handshake");
|
|
122
|
+
window.history.replaceState({}, "", url.toString());
|
|
123
|
+
try {
|
|
124
|
+
const res = await fetch(`${apiOrigin}/api/v1/tokens/exchange`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "Content-Type": "application/json" },
|
|
127
|
+
body: JSON.stringify({ handshake_token: handshakeToken })
|
|
128
|
+
});
|
|
129
|
+
if (res.ok) {
|
|
130
|
+
const data = await res.json();
|
|
131
|
+
jwt = data.session_jwt;
|
|
132
|
+
storeJwt(jwt);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
jwt = loadJwt();
|
|
138
|
+
}
|
|
139
|
+
jwtRef.current = jwt;
|
|
140
|
+
}
|
|
83
141
|
try {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
142
|
+
const headers = {
|
|
143
|
+
"X-Vaultix-Publishable-Key": publishableKey
|
|
144
|
+
};
|
|
145
|
+
if (jwt) headers["Authorization"] = `Bearer ${jwt}`;
|
|
146
|
+
const res = await fetch(`${apiOrigin}/api/v1/me`, {
|
|
147
|
+
// Use Bearer token for cross-domain; fall back to cookie for same-domain
|
|
148
|
+
credentials: jwt ? "omit" : "include",
|
|
149
|
+
headers
|
|
87
150
|
});
|
|
88
151
|
if (!res.ok) {
|
|
152
|
+
if (jwt) {
|
|
153
|
+
clearJwt();
|
|
154
|
+
jwtRef.current = null;
|
|
155
|
+
}
|
|
89
156
|
if (!cancelled) setIsLoaded(true);
|
|
90
157
|
return;
|
|
91
158
|
}
|
|
@@ -108,19 +175,21 @@ function VaultixProvider({
|
|
|
108
175
|
};
|
|
109
176
|
}, [apiOrigin, publishableKey, scheduleRefresh]);
|
|
110
177
|
const signOut = useCallback(async () => {
|
|
178
|
+
const jwt = jwtRef.current;
|
|
179
|
+
clearAuth();
|
|
180
|
+
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
111
181
|
try {
|
|
112
|
-
|
|
182
|
+
const headers = {};
|
|
183
|
+
if (jwt) headers["Authorization"] = `Bearer ${jwt}`;
|
|
184
|
+
await fetch(`${apiOrigin}/api/v1/session`, {
|
|
113
185
|
method: "DELETE",
|
|
114
|
-
credentials: "include"
|
|
186
|
+
credentials: jwt ? "omit" : "include",
|
|
187
|
+
headers
|
|
115
188
|
});
|
|
116
189
|
} catch {
|
|
117
|
-
} finally {
|
|
118
|
-
setUser(null);
|
|
119
|
-
setSession(null);
|
|
120
|
-
setOrganization(null);
|
|
121
|
-
if (afterSignInUrl) window.location.href = afterSignInUrl;
|
|
122
190
|
}
|
|
123
|
-
|
|
191
|
+
if (afterSignInUrl) window.location.href = afterSignInUrl;
|
|
192
|
+
}, [apiOrigin, afterSignInUrl, clearAuth]);
|
|
124
193
|
const value = {
|
|
125
194
|
user,
|
|
126
195
|
session,
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaultix.ai/react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Vaultix-ID React SDK — drop-in auth components for any React app",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
9
11
|
"exports": {
|
|
10
12
|
".": {
|
|
11
13
|
"types": "./dist/index.d.ts",
|
|
@@ -41,7 +43,14 @@
|
|
|
41
43
|
"@testing-library/jest-dom": "^6.0.0",
|
|
42
44
|
"jest-environment-jsdom": "^29.0.0"
|
|
43
45
|
},
|
|
44
|
-
"keywords": [
|
|
46
|
+
"keywords": [
|
|
47
|
+
"auth",
|
|
48
|
+
"authentication",
|
|
49
|
+
"react",
|
|
50
|
+
"vaultix",
|
|
51
|
+
"oauth",
|
|
52
|
+
"sdk"
|
|
53
|
+
],
|
|
45
54
|
"license": "MIT",
|
|
46
55
|
"repository": {
|
|
47
56
|
"type": "git",
|