@darkauth/client 1.4.4 → 1.5.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.
Files changed (2) hide show
  1. package/dist/index.js +66 -48
  2. 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
- const tokenUrl = new URL("/token", cfg.issuer);
143
- const verifier = sessionStorage.getItem("pkce_verifier") || "";
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 tokenResponse = await response.json();
159
- const fragmentParams = parseFragmentParams(location.hash || "");
160
- const drkJwe = fragmentParams.drk_jwe;
161
- const zkDrkHash = typeof tokenResponse.zk_drk_hash === "string" ? tokenResponse.zk_drk_hash : null;
162
- const idToken = tokenResponse.id_token;
163
- const refreshToken = tokenResponse.refresh_token;
164
- const hasZkArtifacts = !!drkJwe || !!zkDrkHash;
165
- if (!hasZkArtifacts) {
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
- localStorage.removeItem("drk_protected");
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: EMPTY_DRK, refreshToken };
176
- }
177
- if (!drkJwe || typeof drkJwe !== "string")
178
- throw new Error("Missing DRK JWE from URL fragment");
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
- history.replaceState(null, "", location.origin + location.pathname);
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();
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@darkauth/client",
3
- "version": "1.4.4",
3
+ "version": "1.5.5",
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",