@darkauth/client 1.4.3 → 1.5.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/dist/index.js +70 -49
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ function viteEnvGet(key) {
|
|
|
11
11
|
return undefined;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
let callbackInFlight = null;
|
|
15
|
+
let callbackInFlightCode = null;
|
|
14
16
|
let cfg = {
|
|
15
17
|
issuer: (typeof window !== "undefined" && window.__APP_CONFIG__?.issuer) ||
|
|
16
18
|
viteEnvGet("VITE_DARKAUTH_ISSUER") ||
|
|
@@ -139,65 +141,81 @@ export async function handleCallback() {
|
|
|
139
141
|
const code = params.get("code");
|
|
140
142
|
if (!code)
|
|
141
143
|
return null;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const response = await fetch(tokenUrl.toString(), {
|
|
145
|
-
method: "POST",
|
|
146
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
147
|
-
body: new URLSearchParams({
|
|
148
|
-
grant_type: "authorization_code",
|
|
149
|
-
code,
|
|
150
|
-
client_id: cfg.clientId,
|
|
151
|
-
redirect_uri: cfg.redirectUri,
|
|
152
|
-
code_verifier: verifier,
|
|
153
|
-
}),
|
|
154
|
-
});
|
|
155
|
-
if (!response.ok) {
|
|
156
|
-
throw new Error("Token exchange failed");
|
|
144
|
+
if (callbackInFlight && callbackInFlightCode === code) {
|
|
145
|
+
return callbackInFlight;
|
|
157
146
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
147
|
+
const exchangePromise = (async () => {
|
|
148
|
+
const tokenUrl = new URL("/token", cfg.issuer);
|
|
149
|
+
const verifier = sessionStorage.getItem("pkce_verifier") || "";
|
|
150
|
+
const response = await fetch(tokenUrl.toString(), {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
153
|
+
body: new URLSearchParams({
|
|
154
|
+
grant_type: "authorization_code",
|
|
155
|
+
code,
|
|
156
|
+
client_id: cfg.clientId,
|
|
157
|
+
redirect_uri: cfg.redirectUri,
|
|
158
|
+
code_verifier: verifier,
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
if (!response.ok) {
|
|
162
|
+
throw new Error("Token exchange failed");
|
|
163
|
+
}
|
|
164
|
+
const tokenResponse = await response.json();
|
|
165
|
+
const fragmentParams = parseFragmentParams(location.hash || "");
|
|
166
|
+
const drkJwe = fragmentParams.drk_jwe;
|
|
167
|
+
const zkDrkHash = typeof tokenResponse.zk_drk_hash === "string" ? tokenResponse.zk_drk_hash : null;
|
|
168
|
+
const idToken = tokenResponse.id_token;
|
|
169
|
+
const refreshToken = tokenResponse.refresh_token;
|
|
170
|
+
const hasZkArtifacts = !!drkJwe || !!zkDrkHash;
|
|
171
|
+
if (!hasZkArtifacts) {
|
|
172
|
+
sessionStorage.removeItem("zk_eph_priv_jwk");
|
|
173
|
+
try {
|
|
174
|
+
history.replaceState(null, "", location.origin + location.pathname);
|
|
175
|
+
}
|
|
176
|
+
catch { }
|
|
177
|
+
setStoredIdToken(idToken);
|
|
178
|
+
localStorage.removeItem("drk_protected");
|
|
179
|
+
if (refreshToken)
|
|
180
|
+
localStorage.setItem("refresh_token", refreshToken);
|
|
181
|
+
return { idToken, drk: EMPTY_DRK, refreshToken };
|
|
182
|
+
}
|
|
183
|
+
if (!drkJwe || typeof drkJwe !== "string")
|
|
184
|
+
throw new Error("Missing DRK JWE from URL fragment");
|
|
185
|
+
if (zkDrkHash) {
|
|
186
|
+
const hash = bytesToBase64Url(await sha256(new TextEncoder().encode(drkJwe)));
|
|
187
|
+
if (zkDrkHash !== hash)
|
|
188
|
+
throw new Error("DRK hash mismatch");
|
|
189
|
+
}
|
|
190
|
+
const privateJwkString = sessionStorage.getItem("zk_eph_priv_jwk");
|
|
191
|
+
if (!privateJwkString)
|
|
192
|
+
throw new Error("Missing ZK private key for callback");
|
|
166
193
|
sessionStorage.removeItem("zk_eph_priv_jwk");
|
|
194
|
+
const privateKey = await crypto.subtle.importKey("jwk", JSON.parse(privateJwkString), { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits", "deriveKey"]);
|
|
195
|
+
const { plaintext } = await compactDecrypt(drkJwe, privateKey);
|
|
196
|
+
const drk = new Uint8Array(plaintext);
|
|
167
197
|
try {
|
|
168
198
|
history.replaceState(null, "", location.origin + location.pathname);
|
|
169
199
|
}
|
|
170
200
|
catch { }
|
|
171
201
|
setStoredIdToken(idToken);
|
|
172
|
-
|
|
202
|
+
const obfuscatedDrk = obfuscateKey(drk);
|
|
203
|
+
localStorage.setItem("drk_protected", bytesToBase64Url(obfuscatedDrk));
|
|
173
204
|
if (refreshToken)
|
|
174
205
|
localStorage.setItem("refresh_token", refreshToken);
|
|
175
|
-
return { idToken, drk
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (zkDrkHash) {
|
|
180
|
-
const hash = bytesToBase64Url(await sha256(new TextEncoder().encode(drkJwe)));
|
|
181
|
-
if (zkDrkHash !== hash)
|
|
182
|
-
throw new Error("DRK hash mismatch");
|
|
183
|
-
}
|
|
184
|
-
const privateJwkString = sessionStorage.getItem("zk_eph_priv_jwk");
|
|
185
|
-
if (!privateJwkString)
|
|
186
|
-
throw new Error("Missing ZK private key for callback");
|
|
187
|
-
sessionStorage.removeItem("zk_eph_priv_jwk");
|
|
188
|
-
const privateKey = await crypto.subtle.importKey("jwk", JSON.parse(privateJwkString), { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits", "deriveKey"]);
|
|
189
|
-
const { plaintext } = await compactDecrypt(drkJwe, privateKey);
|
|
190
|
-
const drk = new Uint8Array(plaintext);
|
|
206
|
+
return { idToken, drk, refreshToken };
|
|
207
|
+
})();
|
|
208
|
+
callbackInFlight = exchangePromise;
|
|
209
|
+
callbackInFlightCode = code;
|
|
191
210
|
try {
|
|
192
|
-
|
|
211
|
+
return await exchangePromise;
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
if (callbackInFlight === exchangePromise) {
|
|
215
|
+
callbackInFlight = null;
|
|
216
|
+
callbackInFlightCode = null;
|
|
217
|
+
}
|
|
193
218
|
}
|
|
194
|
-
catch { }
|
|
195
|
-
setStoredIdToken(idToken);
|
|
196
|
-
const obfuscatedDrk = obfuscateKey(drk);
|
|
197
|
-
localStorage.setItem("drk_protected", bytesToBase64Url(obfuscatedDrk));
|
|
198
|
-
if (refreshToken)
|
|
199
|
-
localStorage.setItem("refresh_token", refreshToken);
|
|
200
|
-
return { idToken, drk, refreshToken };
|
|
201
219
|
}
|
|
202
220
|
export function getStoredSession() {
|
|
203
221
|
const idToken = getStoredIdToken();
|
|
@@ -234,7 +252,10 @@ export async function refreshSession() {
|
|
|
234
252
|
});
|
|
235
253
|
if (!response.ok) {
|
|
236
254
|
if (response.status === 401) {
|
|
237
|
-
localStorage.
|
|
255
|
+
const latestRefreshToken = localStorage.getItem("refresh_token");
|
|
256
|
+
if (latestRefreshToken === refreshToken) {
|
|
257
|
+
localStorage.removeItem("refresh_token");
|
|
258
|
+
}
|
|
238
259
|
}
|
|
239
260
|
return null;
|
|
240
261
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darkauth/client",
|
|
3
|
-
"version": "1.4
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"directory": "packages/darkauth-client",
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/puzed/darkauth"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"main": "dist/index.js",
|
|
7
12
|
"types": "dist/index.d.ts",
|