@darkauth/client
Advanced tools
+66
-48
@@ -14,2 +14,4 @@ import { compactDecrypt } from "jose"; | ||
| } | ||
| let callbackInFlight = null; | ||
| let callbackInFlightCode = null; | ||
| let cfg = { | ||
@@ -143,27 +145,55 @@ issuer: (typeof window !== "undefined" && window.__APP_CONFIG__?.issuer) || | ||
| return null; | ||
| const tokenUrl = new URL("/token", cfg.issuer); | ||
| const verifier = sessionStorage.getItem("pkce_verifier") || ""; | ||
| const response = await fetch(tokenUrl.toString(), { | ||
| method: "POST", | ||
| headers: { "content-type": "application/x-www-form-urlencoded" }, | ||
| body: new URLSearchParams({ | ||
| grant_type: "authorization_code", | ||
| code, | ||
| client_id: cfg.clientId, | ||
| redirect_uri: cfg.redirectUri, | ||
| code_verifier: verifier, | ||
| }), | ||
| }); | ||
| if (!response.ok) { | ||
| throw new Error("Token exchange failed"); | ||
| if (callbackInFlight && callbackInFlightCode === code) { | ||
| return callbackInFlight; | ||
| } | ||
| const tokenResponse = await response.json(); | ||
| const fragmentParams = parseFragmentParams(location.hash || ""); | ||
| const drkJwe = fragmentParams.drk_jwe; | ||
| const zkDrkHash = typeof tokenResponse.zk_drk_hash === "string" ? tokenResponse.zk_drk_hash : null; | ||
| const idToken = tokenResponse.id_token; | ||
| const refreshToken = tokenResponse.refresh_token; | ||
| const hasZkArtifacts = !!drkJwe || !!zkDrkHash; | ||
| if (!hasZkArtifacts) { | ||
| const exchangePromise = (async () => { | ||
| const tokenUrl = new URL("/token", cfg.issuer); | ||
| const verifier = sessionStorage.getItem("pkce_verifier") || ""; | ||
| const response = await fetch(tokenUrl.toString(), { | ||
| method: "POST", | ||
| headers: { "content-type": "application/x-www-form-urlencoded" }, | ||
| body: new URLSearchParams({ | ||
| grant_type: "authorization_code", | ||
| code, | ||
| client_id: cfg.clientId, | ||
| redirect_uri: cfg.redirectUri, | ||
| code_verifier: verifier, | ||
| }), | ||
| }); | ||
| if (!response.ok) { | ||
| throw new Error("Token exchange failed"); | ||
| } | ||
| const tokenResponse = await response.json(); | ||
| const fragmentParams = parseFragmentParams(location.hash || ""); | ||
| const drkJwe = fragmentParams.drk_jwe; | ||
| const zkDrkHash = typeof tokenResponse.zk_drk_hash === "string" ? tokenResponse.zk_drk_hash : null; | ||
| const idToken = tokenResponse.id_token; | ||
| const refreshToken = tokenResponse.refresh_token; | ||
| const hasZkArtifacts = !!drkJwe || !!zkDrkHash; | ||
| if (!hasZkArtifacts) { | ||
| sessionStorage.removeItem("zk_eph_priv_jwk"); | ||
| try { | ||
| history.replaceState(null, "", location.origin + location.pathname); | ||
| } | ||
| catch { } | ||
| setStoredIdToken(idToken); | ||
| localStorage.removeItem("drk_protected"); | ||
| if (refreshToken) | ||
| localStorage.setItem("refresh_token", refreshToken); | ||
| return { idToken, drk: EMPTY_DRK, refreshToken }; | ||
| } | ||
| if (!drkJwe || typeof drkJwe !== "string") | ||
| throw new Error("Missing DRK JWE from URL fragment"); | ||
| if (zkDrkHash) { | ||
| const hash = bytesToBase64Url(await sha256(new TextEncoder().encode(drkJwe))); | ||
| if (zkDrkHash !== hash) | ||
| throw new Error("DRK hash mismatch"); | ||
| } | ||
| const privateJwkString = sessionStorage.getItem("zk_eph_priv_jwk"); | ||
| if (!privateJwkString) | ||
| throw new Error("Missing ZK private key for callback"); | ||
| sessionStorage.removeItem("zk_eph_priv_jwk"); | ||
| const privateKey = await crypto.subtle.importKey("jwk", JSON.parse(privateJwkString), { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits", "deriveKey"]); | ||
| const { plaintext } = await compactDecrypt(drkJwe, privateKey); | ||
| const drk = new Uint8Array(plaintext); | ||
| try { | ||
@@ -174,31 +204,19 @@ history.replaceState(null, "", location.origin + location.pathname); | ||
| setStoredIdToken(idToken); | ||
| localStorage.removeItem("drk_protected"); | ||
| const obfuscatedDrk = obfuscateKey(drk); | ||
| localStorage.setItem("drk_protected", bytesToBase64Url(obfuscatedDrk)); | ||
| if (refreshToken) | ||
| localStorage.setItem("refresh_token", refreshToken); | ||
| return { idToken, drk: EMPTY_DRK, refreshToken }; | ||
| return { idToken, drk, refreshToken }; | ||
| })(); | ||
| callbackInFlight = exchangePromise; | ||
| callbackInFlightCode = code; | ||
| try { | ||
| return await exchangePromise; | ||
| } | ||
| if (!drkJwe || typeof drkJwe !== "string") | ||
| throw new Error("Missing DRK JWE from URL fragment"); | ||
| if (zkDrkHash) { | ||
| const hash = bytesToBase64Url(await sha256(new TextEncoder().encode(drkJwe))); | ||
| if (zkDrkHash !== hash) | ||
| throw new Error("DRK hash mismatch"); | ||
| finally { | ||
| if (callbackInFlight === exchangePromise) { | ||
| callbackInFlight = null; | ||
| callbackInFlightCode = null; | ||
| } | ||
| } | ||
| const privateJwkString = sessionStorage.getItem("zk_eph_priv_jwk"); | ||
| if (!privateJwkString) | ||
| throw new Error("Missing ZK private key for callback"); | ||
| sessionStorage.removeItem("zk_eph_priv_jwk"); | ||
| const privateKey = await crypto.subtle.importKey("jwk", JSON.parse(privateJwkString), { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits", "deriveKey"]); | ||
| const { plaintext } = await compactDecrypt(drkJwe, privateKey); | ||
| const drk = new Uint8Array(plaintext); | ||
| try { | ||
| history.replaceState(null, "", location.origin + location.pathname); | ||
| } | ||
| catch { } | ||
| setStoredIdToken(idToken); | ||
| const obfuscatedDrk = obfuscateKey(drk); | ||
| localStorage.setItem("drk_protected", bytesToBase64Url(obfuscatedDrk)); | ||
| if (refreshToken) | ||
| localStorage.setItem("refresh_token", refreshToken); | ||
| return { idToken, drk, refreshToken }; | ||
| } | ||
@@ -205,0 +223,0 @@ export function getStoredSession() { |
+6
-1
| { | ||
| "name": "@darkauth/client", | ||
| "version": "1.4.4", | ||
| "version": "1.5.4", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "directory": "packages/darkauth-client", | ||
| "type": "git", | ||
| "url": "https://github.com/puzed/darkauth" | ||
| }, | ||
| "type": "module", | ||
@@ -6,0 +11,0 @@ "main": "dist/index.js", |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
29091
3.09%501
3.73%2
-33.33%