@better-auth/electron 1.7.0-beta.4 → 1.7.0-beta.6

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.
@@ -35,13 +35,6 @@ interface ElectronOptions extends ElectronSharedOptions {
35
35
  * @default "better-auth"
36
36
  */
37
37
  cookiePrefix?: string | undefined;
38
- /**
39
- * Override the origin for Electron API routes.
40
- * Enable this if you're facing cors origin issues with Electron API routes.
41
- *
42
- * @default false
43
- */
44
- disableOriginOverride?: boolean | undefined;
45
38
  }
46
39
  //#endregion
47
40
  //#region src/preload.d.ts
@@ -324,11 +317,6 @@ declare const electronClient: <O extends ElectronClientOptions>(options: O) => {
324
317
  mode?: RequestMode | undefined;
325
318
  cache?: RequestCache | undefined;
326
319
  credentials?: RequestCredentials | undefined;
327
- headers?: (HeadersInit & (HeadersInit | {
328
- accept: "application/json" | "text/plain" | "application/octet-stream";
329
- "content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
330
- authorization: "Bearer" | "Basic";
331
- })) | undefined;
332
320
  integrity?: string | undefined;
333
321
  keepalive?: boolean | undefined;
334
322
  method?: string | undefined;
@@ -362,6 +350,12 @@ declare const electronClient: <O extends ElectronClientOptions>(options: O) => {
362
350
  prefix: string | (() => string | undefined) | undefined;
363
351
  value: string | (() => string | undefined) | undefined;
364
352
  }) | undefined;
353
+ headers?: {} | {
354
+ [x: string]: string | undefined;
355
+ accept?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream") | undefined;
356
+ "content-type"?: ((string & {}) | "application/x-www-form-urlencoded" | "application/json" | "text/plain" | "multipart/form-data" | "application/octet-stream") | undefined;
357
+ authorization?: ((string & {}) | `Bearer ${string}` | `Basic ${string}`) | undefined;
358
+ } | undefined;
365
359
  body?: any;
366
360
  query?: any;
367
361
  params?: any;
@@ -379,11 +373,6 @@ declare const electronClient: <O extends ElectronClientOptions>(options: O) => {
379
373
  mode?: RequestMode | undefined;
380
374
  cache?: RequestCache | undefined;
381
375
  credentials?: RequestCredentials | undefined;
382
- headers?: (HeadersInit & (HeadersInit | {
383
- accept: "application/json" | "text/plain" | "application/octet-stream";
384
- "content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
385
- authorization: "Bearer" | "Basic";
386
- })) | undefined;
387
376
  integrity?: string | undefined;
388
377
  keepalive?: boolean | undefined;
389
378
  method?: string | undefined;
@@ -417,6 +406,12 @@ declare const electronClient: <O extends ElectronClientOptions>(options: O) => {
417
406
  prefix: string | (() => string | undefined) | undefined;
418
407
  value: string | (() => string | undefined) | undefined;
419
408
  }) | undefined;
409
+ headers?: {} | {
410
+ [x: string]: string | undefined;
411
+ accept?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream") | undefined;
412
+ "content-type"?: ((string & {}) | "application/x-www-form-urlencoded" | "application/json" | "text/plain" | "multipart/form-data" | "application/octet-stream") | undefined;
413
+ authorization?: ((string & {}) | `Bearer ${string}` | `Basic ${string}`) | undefined;
414
+ } | undefined;
420
415
  body?: any;
421
416
  query?: any;
422
417
  params?: any;
package/dist/client.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as setupRenderer, a as authenticate, c as ElectronClientOptions, d as Storage, f as FetchUserImageResult, g as SetupRendererConfig, h as ExposedBridges, i as ElectronRequestAuthOptions, l as ElectronProxyClientOptions, m as normalizeUserOutput, n as handleDeepLink, o as kElectron, p as fetchUserImage, r as ElectronAuthenticateOptions, s as requestAuth, t as electronClient, u as ElectronSharedClientOptions, y as ElectronSharedOptions } from "./client-DIzl-aw0.mjs";
1
+ import { _ as setupRenderer, a as authenticate, c as ElectronClientOptions, d as Storage, f as FetchUserImageResult, g as SetupRendererConfig, h as ExposedBridges, i as ElectronRequestAuthOptions, l as ElectronProxyClientOptions, m as normalizeUserOutput, n as handleDeepLink, o as kElectron, p as fetchUserImage, r as ElectronAuthenticateOptions, s as requestAuth, t as electronClient, u as ElectronSharedClientOptions, y as ElectronSharedOptions } from "./client-D5QKMJbp.mjs";
2
2
  export { ElectronAuthenticateOptions, ElectronClientOptions, ElectronProxyClientOptions, ElectronRequestAuthOptions, ElectronSharedClientOptions, ElectronSharedOptions, ExposedBridges, FetchUserImageResult, SetupRendererConfig, Storage, authenticate, electronClient, fetchUserImage, handleDeepLink, kElectron, normalizeUserOutput, requestAuth, setupRenderer };
package/dist/client.mjs CHANGED
@@ -1,14 +1,14 @@
1
- import { t as PACKAGE_VERSION } from "./version-5EiO_U3Z.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-BTlyLl-N.mjs";
2
2
  import { n as isProcessType, r as parseProtocolScheme, t as getChannelPrefixWithDelimiter } from "./utils-DxDKRT6e.mjs";
3
3
  import { BetterAuthError } from "@better-auth/core/error";
4
4
  import { base64, base64Url } from "@better-auth/utils/base64";
5
5
  import { APIError as APIError$1, getBaseURL, isDevelopment, isTest, safeJSONParse } from "better-auth";
6
6
  import { generateRandomString } from "better-auth/crypto";
7
7
  import * as z from "zod";
8
- import { Buffer } from "node:buffer";
9
- import { createHash } from "@better-auth/utils/hash";
10
8
  import { signInSocial } from "better-auth/api";
11
9
  import { cookieNameRegex, parseSetCookieHeader } from "better-auth/cookies";
10
+ import { generateCodeChallenge } from "better-auth/oauth2";
11
+ import { Buffer } from "node:buffer";
12
12
  import electron, { shell } from "electron";
13
13
  import { isDevelopment as isDevelopment$1 } from "@better-auth/core/env";
14
14
  import { isPublicRoutableHost } from "@better-auth/core/utils/host";
@@ -150,8 +150,8 @@ async function requestAuth(clientOptions, options, cfg) {
150
150
  if (!isProcessType("browser")) throw new BetterAuthError("`requestAuth` can only be called in the main process");
151
151
  const { randomBytes } = await import("node:crypto");
152
152
  const state = generateRandomString(16, "A-Z", "a-z", "0-9");
153
- const codeVerifier = base64Url.encode(randomBytes(32));
154
- const codeChallenge = base64Url.encode(await createHash("SHA-256").digest(codeVerifier));
153
+ const codeVerifier = base64Url.encode(randomBytes(32), { padding: false });
154
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
155
155
  (globalThis[kElectron] ??= /* @__PURE__ */ new Map()).set(state, codeVerifier);
156
156
  let url = null;
157
157
  if (cfg?.provider) {
@@ -168,7 +168,6 @@ async function requestAuth(clientOptions, options, cfg) {
168
168
  } else url = new URL(options.signInURL);
169
169
  url.searchParams.set("client_id", options.clientID || "electron");
170
170
  url.searchParams.set("code_challenge", codeChallenge);
171
- url.searchParams.set("code_challenge_method", "S256");
172
171
  url.searchParams.set("state", state);
173
172
  await shell.openExternal(url.toString(), { activate: true });
174
173
  }
@@ -579,6 +578,16 @@ const electronClient = (options) => {
579
578
  const cookieName = `${opts.storagePrefix}.cookie`;
580
579
  const localCacheName = `${opts.storagePrefix}.local_cache`;
581
580
  const { getDecrypted, setEncrypted } = storageAdapter(opts.storage, new Set([cookieName, localCacheName]));
581
+ const clearSessionCache = () => {
582
+ setEncrypted(cookieName, "{}");
583
+ store?.atoms.session?.set({
584
+ ...store.atoms.session.get(),
585
+ data: null,
586
+ error: null,
587
+ isPending: false
588
+ });
589
+ setEncrypted(localCacheName, "{}");
590
+ };
582
591
  if ((isDevelopment() || isTest()) && /^(?!\.)(?!.*\.\.)(?!.*\.$)[^.]+\.[^.]+$/.test(scheme)) console.warn("The provided scheme does not follow the reverse domain name notation. For example: `app.example.com` -> `com.example.app`.");
583
592
  return {
584
593
  id: "electron",
@@ -592,22 +601,13 @@ const electronClient = (options) => {
592
601
  options ||= {};
593
602
  options.credentials = "omit";
594
603
  options.headers = {
604
+ origin: `${scheme}:/`,
595
605
  ...options.headers,
596
606
  cookie,
597
607
  "user-agent": app.userAgentFallback,
598
- "electron-origin": `${scheme}:/`,
599
608
  "x-skip-oauth-proxy": "true"
600
609
  };
601
- if (url.endsWith("/sign-out")) {
602
- setEncrypted(cookieName, "{}");
603
- store?.atoms.session?.set({
604
- ...store.atoms.session.get(),
605
- data: null,
606
- error: null,
607
- isPending: false
608
- });
609
- setEncrypted(localCacheName, "{}");
610
- }
610
+ if (url.endsWith("/sign-out")) clearSessionCache();
611
611
  return {
612
612
  url,
613
613
  options
@@ -630,6 +630,7 @@ const electronClient = (options) => {
630
630
  const data = context.data;
631
631
  setEncrypted(localCacheName, JSON.stringify(data));
632
632
  }
633
+ if (context.request.url.toString().includes("/sign-out")) clearSessionCache();
633
634
  },
634
635
  onError: async (context) => {
635
636
  webContents.getFocusedWebContents()?.send(`${getChannelPrefixWithDelimiter(opts.channelPrefix)}error`, {
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { f as FetchUserImageResult, m as normalizeUserOutput, p as fetchUserImage, v as ElectronOptions, y as ElectronSharedOptions } from "./client-DIzl-aw0.mjs";
1
+ import { f as FetchUserImageResult, m as normalizeUserOutput, p as fetchUserImage, v as ElectronOptions, y as ElectronSharedOptions } from "./client-D5QKMJbp.mjs";
2
2
  import * as better_auth0 from "better-auth";
3
3
  import * as z from "zod";
4
4
  import * as _better_auth_core0 from "@better-auth/core";
@@ -16,9 +16,6 @@ declare module "@better-auth/core" {
16
16
  declare const electron: (options?: ElectronOptions | undefined) => {
17
17
  id: "electron";
18
18
  version: string;
19
- onRequest(request: Request, _ctx: better_auth0.AuthContext): Promise<{
20
- request: Request;
21
- } | undefined>;
22
19
  hooks: {
23
20
  after: ({
24
21
  matcher: (ctx: HookEndpointContext) => boolean;
@@ -102,7 +99,7 @@ declare const electron: (options?: ElectronOptions | undefined) => {
102
99
  image?: string | null | undefined;
103
100
  } & Record<string, any>;
104
101
  } | null) => void;
105
- socialProviders: better_auth0.OAuthProvider[];
102
+ socialProviders: better_auth0.UpstreamProvider[];
106
103
  authCookies: better_auth0.BetterAuthCookies;
107
104
  logger: ReturnType<typeof better_auth0.createLogger>;
108
105
  rateLimit: {
@@ -205,7 +202,6 @@ declare const electron: (options?: ElectronOptions | undefined) => {
205
202
  provider: z.ZodString;
206
203
  state: z.ZodString;
207
204
  code_challenge: z.ZodString;
208
- code_challenge_method: z.ZodOptional<z.ZodString>;
209
205
  }, z.core.$strip>;
210
206
  metadata: {
211
207
  scope: "http";
@@ -255,7 +251,6 @@ declare const electron: (options?: ElectronOptions | undefined) => {
255
251
  client_id: z.ZodString;
256
252
  state: z.ZodString;
257
253
  code_challenge: z.ZodString;
258
- code_challenge_method: z.ZodOptional<z.ZodString>;
259
254
  }, z.core.$strip>;
260
255
  body: z.ZodObject<{
261
256
  callbackURL: z.ZodOptional<z.ZodString>;
@@ -325,7 +320,6 @@ declare const electron: (options?: ElectronOptions | undefined) => {
325
320
  codeExpiresIn: number;
326
321
  redirectCookieExpiresIn: number;
327
322
  cookiePrefix: string;
328
- disableOriginOverride?: boolean | undefined;
329
323
  clientID: string;
330
324
  };
331
325
  $ERROR_CODES: {
package/dist/index.mjs CHANGED
@@ -1,18 +1,16 @@
1
- import { t as PACKAGE_VERSION } from "./version-5EiO_U3Z.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-BTlyLl-N.mjs";
2
2
  import { createAuthMiddleware } from "@better-auth/core/api";
3
3
  import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
4
4
  import { base64Url } from "@better-auth/utils/base64";
5
5
  import { defineErrorCodes, safeJSONParse } from "better-auth";
6
6
  import { generateRandomString } from "better-auth/crypto";
7
7
  import * as z from "zod";
8
- import { Buffer } from "node:buffer";
9
- import { timingSafeEqual } from "node:crypto";
10
8
  import { safeJSONParse as safeJSONParse$1 } from "@better-auth/core/utils/json";
11
- import { createHash } from "@better-auth/utils/hash";
12
9
  import { betterFetch } from "@better-fetch/fetch";
13
10
  import { createAuthEndpoint, sessionMiddleware } from "better-auth/api";
14
11
  import { setSessionCookie } from "better-auth/cookies";
15
12
  import { parseUserOutput } from "better-auth/db";
13
+ import { generateCodeChallenge } from "better-auth/oauth2";
16
14
  //#region src/error-codes.ts
17
15
  const ELECTRON_ERROR_CODES = defineErrorCodes({
18
16
  INVALID_CLIENT_ID: "Invalid client ID",
@@ -55,18 +53,13 @@ const electronToken = (_opts) => createAuthEndpoint("/electron/token", {
55
53
  }
56
54
  }
57
55
  }, async (ctx) => {
58
- const token = await ctx.context.internalAdapter.findVerificationValue(`electron:${ctx.body.token}`);
59
- if (!token || token.expiresAt < /* @__PURE__ */ new Date()) throw APIError.from("NOT_FOUND", ELECTRON_ERROR_CODES.INVALID_TOKEN);
56
+ const token = await ctx.context.internalAdapter.consumeVerificationValue(`electron:${ctx.body.token}`);
57
+ if (!token) throw APIError.from("NOT_FOUND", ELECTRON_ERROR_CODES.INVALID_TOKEN);
60
58
  const tokenRecord = safeJSONParse$1(token.value);
61
59
  if (!tokenRecord) throw APIError.from("INTERNAL_SERVER_ERROR", ELECTRON_ERROR_CODES.INVALID_TOKEN);
62
60
  if (tokenRecord.state !== ctx.body.state) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.STATE_MISMATCH);
63
61
  if (!tokenRecord.codeChallenge) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.MISSING_CODE_CHALLENGE);
64
- if (tokenRecord.codeChallengeMethod === "s256") {
65
- const codeChallenge = Buffer.from(base64Url.decode(tokenRecord.codeChallenge));
66
- const codeVerifier = Buffer.from(await createHash("SHA-256").digest(ctx.body.code_verifier));
67
- if (codeChallenge.length !== codeVerifier.length || !timingSafeEqual(codeChallenge, codeVerifier)) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.INVALID_CODE_VERIFIER);
68
- } else if (tokenRecord.codeChallenge !== ctx.body.code_verifier) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.INVALID_CODE_VERIFIER);
69
- await ctx.context.internalAdapter.deleteVerificationByIdentifier(`electron:${ctx.body.token}`);
62
+ if (await generateCodeChallenge(ctx.body.code_verifier) !== tokenRecord.codeChallenge) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.INVALID_CODE_VERIFIER);
70
63
  const user = await ctx.context.internalAdapter.findUserById(tokenRecord.userId);
71
64
  if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.USER_NOT_FOUND);
72
65
  const session = await ctx.context.internalAdapter.createSession(user.id);
@@ -83,8 +76,7 @@ const electronToken = (_opts) => createAuthEndpoint("/electron/token", {
83
76
  const electronInitOAuthProxyQuerySchema = z.object({
84
77
  provider: z.string().nonempty(),
85
78
  state: z.string(),
86
- code_challenge: z.string(),
87
- code_challenge_method: z.string().optional()
79
+ code_challenge: z.string()
88
80
  });
89
81
  const electronInitOAuthProxy = (opts) => createAuthEndpoint("/electron/init-oauth-proxy", {
90
82
  method: "GET",
@@ -122,7 +114,6 @@ const electronInitOAuthProxy = (opts) => createAuthEndpoint("/electron/init-oaut
122
114
  const searchParams = new URLSearchParams();
123
115
  searchParams.set("client_id", opts.clientID || "electron");
124
116
  searchParams.set("code_challenge", ctx.query.code_challenge);
125
- searchParams.set("code_challenge_method", ctx.query.code_challenge_method || "plain");
126
117
  searchParams.set("state", ctx.query.state);
127
118
  const res = await betterFetch(`/sign-in/social?${searchParams.toString()}`, {
128
119
  baseURL: ctx.context.baseURL,
@@ -145,8 +136,7 @@ const electronInitOAuthProxy = (opts) => createAuthEndpoint("/electron/init-oaut
145
136
  const electronTransferUserQuerySchema = z.object({
146
137
  client_id: z.string(),
147
138
  state: z.string(),
148
- code_challenge: z.string(),
149
- code_challenge_method: z.string().optional()
139
+ code_challenge: z.string()
150
140
  });
151
141
  const electronTransferUserBodySchema = z.object({ callbackURL: z.string().optional() });
152
142
  const electronTransferUser = (_opts, { handleTransfer }) => createAuthEndpoint("/electron/transfer-user", {
@@ -201,7 +191,7 @@ const electron = (options) => {
201
191
  return !!(ctx.path?.startsWith("/sign-in") || ctx.path?.startsWith("/sign-up") || ctx.path?.startsWith("/callback") || ctx.path?.startsWith("/magic-link/verify") || ctx.path?.startsWith("/email-otp/verify-email") || ctx.path?.startsWith("/verify-email") || ctx.path?.startsWith("/one-tap/callback") || ctx.path?.startsWith("/passkey/verify-authentication") || ctx.path?.startsWith("/phone-number/verify"));
202
192
  };
203
193
  const handleTransfer = async (ctx, payload) => {
204
- const { client_id, state, code_challenge, code_challenge_method = "plain" } = payload;
194
+ const { client_id, state, code_challenge } = payload;
205
195
  const userId = ctx.context.session?.user.id || ctx.context.newSession?.user.id;
206
196
  if (!userId || client_id !== opts.clientID) return null;
207
197
  if (!state) throw APIError.from("BAD_REQUEST", ELECTRON_ERROR_CODES.MISSING_STATE);
@@ -215,7 +205,6 @@ const electron = (options) => {
215
205
  value: JSON.stringify({
216
206
  userId,
217
207
  codeChallenge: code_challenge,
218
- codeChallengeMethod: code_challenge_method.toLowerCase(),
219
208
  state
220
209
  }),
221
210
  expiresAt
@@ -234,14 +223,6 @@ const electron = (options) => {
234
223
  return {
235
224
  id: "electron",
236
225
  version: PACKAGE_VERSION,
237
- async onRequest(request, _ctx) {
238
- if (opts.disableOriginOverride || request.headers.get("origin")) return;
239
- const electronOrigin = request.headers.get("electron-origin");
240
- if (!electronOrigin) return;
241
- const req = request.clone();
242
- req.headers.set("origin", electronOrigin);
243
- return { request: req };
244
- },
245
226
  hooks: { after: [{
246
227
  matcher: (ctx) => !hookMatcher(ctx),
247
228
  handler: createAuthMiddleware(async (ctx) => {
@@ -256,7 +237,6 @@ const electron = (options) => {
256
237
  const querySchema = z.object({
257
238
  client_id: z.string(),
258
239
  code_challenge: z.string().nonempty(),
259
- code_challenge_method: z.string().optional().default("plain"),
260
240
  state: z.string().nonempty()
261
241
  });
262
242
  const cookie = ctx.context.createAuthCookie("transfer_token", { maxAge: opts.codeExpiresIn });