@darkauth/client 0.2.0 → 1.4.3

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 (3) hide show
  1. package/README.md +13 -5
  2. package/dist/index.js +42 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A TypeScript client library for DarkAuth - providing zero-knowledge authentication and client-side encryption capabilities for web applications.
4
4
 
5
+ The client supports both:
6
+ - ZK-enabled OAuth/OIDC flows
7
+ - Standard OAuth/OIDC flows without ZK delivery
8
+
5
9
  ## Features
6
10
 
7
11
  - **Zero-Knowledge Authentication**: Secure OAuth2/OIDC flow with PKCE and ephemeral key exchange
@@ -29,7 +33,7 @@ setConfig({
29
33
  issuer: 'https://auth.example.com',
30
34
  clientId: 'your-client-id',
31
35
  redirectUri: 'https://app.example.com/callback',
32
- zk: true // Enable zero-knowledge mode
36
+ zk: false // Optional: disable ZK request parameters for standard OIDC flows
33
37
  });
34
38
 
35
39
  // Start login flow
@@ -61,7 +65,7 @@ setConfig({
61
65
  issuer: 'https://auth.example.com', // DarkAuth server URL
62
66
  clientId: 'your-client-id', // Your application's client ID
63
67
  redirectUri: 'https://app.example.com/callback', // OAuth callback URL
64
- zk: true // Enable zero-knowledge mode (default: true)
68
+ zk: true // Optional. Default true. Set false for non-ZK flows.
65
69
  });
66
70
  ```
67
71
 
@@ -80,20 +84,24 @@ Starts the OAuth2/OIDC login flow with PKCE. Redirects the user to the DarkAuth
80
84
 
81
85
  Processes the OAuth callback after successful authentication. Returns an `AuthSession` object containing:
82
86
  - `idToken`: JWT ID token
83
- - `drk`: Derived Root Key for encryption operations
87
+ - `drk`: Derived Root Key for encryption operations. In non-ZK flows this is an empty `Uint8Array`.
84
88
  - `refreshToken?`: Optional refresh token
85
89
 
90
+ Behavior:
91
+ - If ZK artifacts are present in the callback/token response, ZK validation and DRK decryption are enforced.
92
+ - If no ZK artifacts are present, callback still succeeds as a standard OIDC flow.
93
+
86
94
  #### `logout(): void`
87
95
 
88
96
  Clears all authentication data from storage.
89
97
 
90
98
  #### `getStoredSession(): AuthSession | null`
91
99
 
92
- Retrieves the current session from storage if valid.
100
+ Retrieves the current session from storage if valid. For non-ZK sessions, returns `drk` as an empty `Uint8Array`.
93
101
 
94
102
  #### `refreshSession(): Promise<AuthSession | null>`
95
103
 
96
- Refreshes the current session using the stored refresh token.
104
+ Refreshes the current session using the stored refresh token. For non-ZK sessions, returns `drk` as an empty `Uint8Array`.
97
105
 
98
106
  ### User Information
99
107
 
package/dist/index.js CHANGED
@@ -19,17 +19,29 @@ let cfg = {
19
19
  clientId: (typeof window !== "undefined" && window.__APP_CONFIG__?.clientId) ||
20
20
  viteEnvGet("VITE_CLIENT_ID") ||
21
21
  (typeof process !== "undefined" ? process.env.DARKAUTH_CLIENT_ID : undefined) ||
22
- "app-web",
22
+ "demo-public-client",
23
23
  redirectUri: (typeof window !== "undefined" && window.__APP_CONFIG__?.redirectUri) ||
24
24
  viteEnvGet("VITE_REDIRECT_URI") ||
25
25
  (typeof window !== "undefined"
26
26
  ? `${window.location.origin}/callback`
27
27
  : "http://localhost:5173/callback"),
28
+ zk: true,
28
29
  };
29
30
  const OBFUSCATION_KEY = "DarkAuth-Storage-Protection-2025";
31
+ const EMPTY_DRK = new Uint8Array(0);
32
+ const ID_TOKEN_KEY = "id_token";
30
33
  export function setConfig(next) {
31
34
  cfg = { ...cfg, ...next };
32
35
  }
36
+ function setStoredIdToken(token) {
37
+ localStorage.setItem(ID_TOKEN_KEY, token);
38
+ }
39
+ function getStoredIdToken() {
40
+ return localStorage.getItem(ID_TOKEN_KEY);
41
+ }
42
+ function clearStoredIdToken() {
43
+ localStorage.removeItem(ID_TOKEN_KEY);
44
+ }
33
45
  function bytesToBase64Url(bytes) {
34
46
  let s = "";
35
47
  for (const b of bytes)
@@ -92,7 +104,7 @@ export function isTokenValid(token) {
92
104
  return claims.exp * 1000 > Date.now() + 5000;
93
105
  }
94
106
  export async function initiateLogin() {
95
- const zkEnabled = cfg.zk === true;
107
+ const zkEnabled = cfg.zk !== false;
96
108
  let zkPubParam;
97
109
  if (zkEnabled) {
98
110
  const keyPair = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, [
@@ -146,27 +158,41 @@ export async function handleCallback() {
146
158
  const tokenResponse = await response.json();
147
159
  const fragmentParams = parseFragmentParams(location.hash || "");
148
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) {
166
+ sessionStorage.removeItem("zk_eph_priv_jwk");
167
+ try {
168
+ history.replaceState(null, "", location.origin + location.pathname);
169
+ }
170
+ catch { }
171
+ setStoredIdToken(idToken);
172
+ localStorage.removeItem("drk_protected");
173
+ if (refreshToken)
174
+ localStorage.setItem("refresh_token", refreshToken);
175
+ return { idToken, drk: EMPTY_DRK, refreshToken };
176
+ }
149
177
  if (!drkJwe || typeof drkJwe !== "string")
150
178
  throw new Error("Missing DRK JWE from URL fragment");
151
- if (tokenResponse.zk_drk_hash) {
179
+ if (zkDrkHash) {
152
180
  const hash = bytesToBase64Url(await sha256(new TextEncoder().encode(drkJwe)));
153
- if (tokenResponse.zk_drk_hash !== hash)
181
+ if (zkDrkHash !== hash)
154
182
  throw new Error("DRK hash mismatch");
155
183
  }
156
184
  const privateJwkString = sessionStorage.getItem("zk_eph_priv_jwk");
157
185
  if (!privateJwkString)
158
- return null;
186
+ throw new Error("Missing ZK private key for callback");
159
187
  sessionStorage.removeItem("zk_eph_priv_jwk");
160
188
  const privateKey = await crypto.subtle.importKey("jwk", JSON.parse(privateJwkString), { name: "ECDH", namedCurve: "P-256" }, true, ["deriveBits", "deriveKey"]);
161
189
  const { plaintext } = await compactDecrypt(drkJwe, privateKey);
162
190
  const drk = new Uint8Array(plaintext);
163
- const idToken = tokenResponse.id_token;
164
- const refreshToken = tokenResponse.refresh_token;
165
191
  try {
166
192
  history.replaceState(null, "", location.origin + location.pathname);
167
193
  }
168
194
  catch { }
169
- sessionStorage.setItem("id_token", idToken);
195
+ setStoredIdToken(idToken);
170
196
  const obfuscatedDrk = obfuscateKey(drk);
171
197
  localStorage.setItem("drk_protected", bytesToBase64Url(obfuscatedDrk));
172
198
  if (refreshToken)
@@ -174,12 +200,14 @@ export async function handleCallback() {
174
200
  return { idToken, drk, refreshToken };
175
201
  }
176
202
  export function getStoredSession() {
177
- const idToken = sessionStorage.getItem("id_token");
203
+ const idToken = getStoredIdToken();
178
204
  const obfuscatedDrkBase64 = localStorage.getItem("drk_protected");
179
- if (!idToken || !obfuscatedDrkBase64)
205
+ if (!idToken)
180
206
  return null;
181
207
  if (!isTokenValid(idToken))
182
208
  return null;
209
+ if (!obfuscatedDrkBase64)
210
+ return { idToken, drk: EMPTY_DRK };
183
211
  try {
184
212
  const obfuscatedDrk = base64UrlToBytes(obfuscatedDrkBase64);
185
213
  const drk = deobfuscateKey(obfuscatedDrk);
@@ -213,25 +241,25 @@ export async function refreshSession() {
213
241
  const tokenResponse = await response.json();
214
242
  const idToken = tokenResponse.id_token;
215
243
  const newRefreshToken = tokenResponse.refresh_token;
216
- sessionStorage.setItem("id_token", idToken);
244
+ setStoredIdToken(idToken);
217
245
  if (newRefreshToken)
218
246
  localStorage.setItem("refresh_token", newRefreshToken);
219
247
  const obfuscatedDrkBase64 = localStorage.getItem("drk_protected");
220
248
  if (!obfuscatedDrkBase64)
221
- return null;
249
+ return { idToken, drk: EMPTY_DRK, refreshToken: newRefreshToken || refreshToken };
222
250
  const obfuscatedDrk = base64UrlToBytes(obfuscatedDrkBase64);
223
251
  const drk = deobfuscateKey(obfuscatedDrk);
224
252
  return { idToken, drk, refreshToken: newRefreshToken || refreshToken };
225
253
  }
226
254
  export function logout() {
227
- sessionStorage.removeItem("id_token");
255
+ clearStoredIdToken();
228
256
  localStorage.removeItem("drk_protected");
229
257
  sessionStorage.removeItem("zk_eph_priv_jwk");
230
258
  sessionStorage.removeItem("pkce_verifier");
231
259
  localStorage.removeItem("refresh_token");
232
260
  }
233
261
  export function getCurrentUser() {
234
- const idToken = sessionStorage.getItem("id_token");
262
+ const idToken = getStoredIdToken();
235
263
  if (!idToken)
236
264
  return null;
237
265
  return parseJwt(idToken);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darkauth/client",
3
- "version": "0.2.0",
3
+ "version": "1.4.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",