@lovable.dev/cloud-auth-js 1.1.2 → 1.1.3-dev.16
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/README.md +45 -2
- package/dist/index.cjs +424 -41
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +422 -40
- package/dist/server.cjs +349 -0
- package/dist/server.d.cts +38 -0
- package/dist/server.d.ts +38 -0
- package/dist/server.js +322 -0
- package/package.json +18 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lovable.dev/cloud-auth-js
|
|
2
2
|
|
|
3
|
-
OAuth authentication library for Lovable projects
|
|
3
|
+
OAuth authentication library for Lovable projects.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,6 +10,49 @@ npm install @lovable.dev/cloud-auth-js
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
+
### Better Auth
|
|
14
|
+
|
|
15
|
+
Client setup:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { newAuthClient } from "@lovable.dev/cloud-auth-js";
|
|
19
|
+
|
|
20
|
+
export const authClient = newAuthClient();
|
|
21
|
+
export const { signIn, signOut } = authClient;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
TanStack Start route setup:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
28
|
+
import { registerAuthRoutes } from "@lovable.dev/cloud-auth-js/server";
|
|
29
|
+
|
|
30
|
+
export const Route = createFileRoute("/api/auth/$")(registerAuthRoutes());
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Sign in with the Lovable OAuth provider:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
await authClient.signInWithLovable({
|
|
37
|
+
// Redirects here after successful authentication.
|
|
38
|
+
callbackURL: "/account",
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`signInWithLovable` uses redirect mode outside an iframe and popup mode in live preview. Pass `mode: "redirect"` or `mode: "popup"` to force a flow.
|
|
43
|
+
|
|
44
|
+
Server env vars:
|
|
45
|
+
|
|
46
|
+
| Name | Description |
|
|
47
|
+
| ------------------------------ | -------------------------------------------- |
|
|
48
|
+
| `LOVABLE_TENANT_ID` | Lovable tenant ID. |
|
|
49
|
+
| `LOVABLE_OAUTH_CLIENT_ID` | Lovable OAuth client ID. |
|
|
50
|
+
| `LOVABLE_OAUTH_CLIENT_SECRET` | Lovable OAuth client secret. |
|
|
51
|
+
| `LOVABLE_AUTH_SECRET` | Secret used to sign auth cookies and tokens. |
|
|
52
|
+
| `LOVABLE_AUTH_TRUSTED_ORIGINS` | Comma-separated trusted origins. |
|
|
53
|
+
|
|
54
|
+
### Legacy OAuth broker
|
|
55
|
+
|
|
13
56
|
```typescript
|
|
14
57
|
import { createLovableAuth } from "@lovable.dev/cloud-auth-js";
|
|
15
58
|
import { supabase } from "./supabase/client";
|
|
@@ -32,7 +75,7 @@ if (result.error) {
|
|
|
32
75
|
await supabase.auth.setSession(result.tokens);
|
|
33
76
|
```
|
|
34
77
|
|
|
35
|
-
## API
|
|
78
|
+
## Legacy API
|
|
36
79
|
|
|
37
80
|
### `createLovableAuth(config?)`
|
|
38
81
|
|
package/dist/index.cjs
CHANGED
|
@@ -20,46 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
createLovableAuth: () => createLovableAuth
|
|
23
|
+
createLovableAuth: () => createLovableAuth,
|
|
24
|
+
newAuthClient: () => newAuthClient
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(src_exports);
|
|
26
27
|
|
|
27
|
-
// package.json
|
|
28
|
-
var package_default = {
|
|
29
|
-
name: "@lovable.dev/cloud-auth-js",
|
|
30
|
-
version: "1.1.2",
|
|
31
|
-
description: "Lovable Cloud Auth JS",
|
|
32
|
-
main: "./dist/index.cjs",
|
|
33
|
-
module: "./dist/index.js",
|
|
34
|
-
types: "./dist/index.d.ts",
|
|
35
|
-
type: "module",
|
|
36
|
-
files: [
|
|
37
|
-
"dist"
|
|
38
|
-
],
|
|
39
|
-
scripts: {
|
|
40
|
-
build: "tsup src/index.ts --format cjs,esm --dts --outDir dist",
|
|
41
|
-
dev: "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
42
|
-
test: "vitest run",
|
|
43
|
-
typecheck: "tsgo --noEmit",
|
|
44
|
-
prepublishOnly: "npm run clean && npm run build",
|
|
45
|
-
clean: "rimraf dist"
|
|
46
|
-
},
|
|
47
|
-
keywords: [
|
|
48
|
-
"lovable",
|
|
49
|
-
"oauth",
|
|
50
|
-
"authentication"
|
|
51
|
-
],
|
|
52
|
-
license: "MIT",
|
|
53
|
-
devDependencies: {
|
|
54
|
-
"@types/node": "^22",
|
|
55
|
-
jsdom: "^26",
|
|
56
|
-
rimraf: "^5.0.0",
|
|
57
|
-
tsup: "^7.2.0",
|
|
58
|
-
typescript: "^5",
|
|
59
|
-
vitest: "^4.1.0"
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
28
|
// src/auth.ts
|
|
64
29
|
var EXPECTED_MESSAGE_TYPE = "authorization_response";
|
|
65
30
|
var DEFAULT_OAUTH_BROKER_URL = "/~oauth/initiate";
|
|
@@ -230,14 +195,432 @@ function createAuth(config = {}) {
|
|
|
230
195
|
};
|
|
231
196
|
}
|
|
232
197
|
|
|
233
|
-
// src/
|
|
234
|
-
|
|
235
|
-
|
|
198
|
+
// src/client.ts
|
|
199
|
+
var import_plugins = require("better-auth/client/plugins");
|
|
200
|
+
var import_react = require("better-auth/react");
|
|
201
|
+
var DEFAULT_PROVIDER_ID = "lovable";
|
|
202
|
+
var POPUP_NAME = "lovable-oauth";
|
|
203
|
+
var POPUP_CHECK_INTERVAL_MS2 = 500;
|
|
204
|
+
var DEFAULT_POPUP_TIMEOUT_MS = 12e4;
|
|
205
|
+
var POPUP_MESSAGE_TYPE = "lovable:better-auth-popup-complete";
|
|
206
|
+
var POPUP_START_PATH = "lovable-popup/start";
|
|
207
|
+
var POPUP_COMPLETE_PATH = "lovable-popup/complete";
|
|
208
|
+
var SESSION_EVENT_TYPE = "lovable-auth:session";
|
|
209
|
+
function isLivePreview() {
|
|
210
|
+
if (typeof window === "undefined")
|
|
211
|
+
return false;
|
|
212
|
+
try {
|
|
213
|
+
return window.self !== window.top;
|
|
214
|
+
} catch {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function resolveSignInMode(mode) {
|
|
219
|
+
if (mode === "popup" || mode === "redirect")
|
|
220
|
+
return mode;
|
|
221
|
+
return isLivePreview() ? "popup" : "redirect";
|
|
222
|
+
}
|
|
223
|
+
function resolveBaseURL(configured) {
|
|
224
|
+
if (typeof window === "undefined")
|
|
225
|
+
return configured;
|
|
226
|
+
return window.location.origin;
|
|
227
|
+
}
|
|
228
|
+
function resolveAuthBasePath(configured) {
|
|
229
|
+
const basePath = typeof configured === "string" && configured ? configured : "/api/auth";
|
|
230
|
+
return `/${basePath.replace(/^\/+|\/+$/g, "")}`;
|
|
231
|
+
}
|
|
232
|
+
function resolveCallbackURL(callbackURL) {
|
|
233
|
+
if (typeof window === "undefined")
|
|
234
|
+
return callbackURL;
|
|
235
|
+
if (!callbackURL)
|
|
236
|
+
return window.location.href;
|
|
237
|
+
try {
|
|
238
|
+
return new URL(callbackURL, window.location.href).toString();
|
|
239
|
+
} catch {
|
|
240
|
+
return callbackURL;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function generatePopupId() {
|
|
244
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto)
|
|
245
|
+
return crypto.randomUUID();
|
|
246
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
247
|
+
}
|
|
248
|
+
function resolveSameOriginCallbackURL(callbackURL) {
|
|
249
|
+
const resolved = resolveCallbackURL(callbackURL);
|
|
250
|
+
if (typeof window === "undefined" || !resolved)
|
|
251
|
+
return resolved ?? null;
|
|
252
|
+
try {
|
|
253
|
+
const url = new URL(resolved);
|
|
254
|
+
if (url.origin !== window.location.origin)
|
|
255
|
+
return null;
|
|
256
|
+
return url.toString();
|
|
257
|
+
} catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function buildPopupCompleteURL(baseURL, basePath, popupId, returnTo) {
|
|
262
|
+
if (typeof window === "undefined")
|
|
263
|
+
return null;
|
|
264
|
+
try {
|
|
265
|
+
const url = new URL(`${basePath}/${POPUP_COMPLETE_PATH}`, baseURL ?? window.location.origin);
|
|
266
|
+
url.searchParams.set("popupId", popupId);
|
|
267
|
+
if (returnTo)
|
|
268
|
+
url.searchParams.set("returnTo", returnTo);
|
|
269
|
+
return url.toString();
|
|
270
|
+
} catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function buildPopupStartURL(baseURL, basePath, providerId, options, callbackURL, errorCallbackURL, newUserCallbackURL) {
|
|
275
|
+
if (typeof window === "undefined")
|
|
276
|
+
return null;
|
|
277
|
+
try {
|
|
278
|
+
const url = new URL(`${basePath}/${POPUP_START_PATH}`, baseURL ?? window.location.origin);
|
|
279
|
+
url.searchParams.set("providerId", providerId);
|
|
280
|
+
url.searchParams.set("callbackURL", callbackURL);
|
|
281
|
+
url.searchParams.set("errorCallbackURL", errorCallbackURL);
|
|
282
|
+
url.searchParams.set("newUserCallbackURL", newUserCallbackURL);
|
|
283
|
+
if (options.requestSignUp !== void 0)
|
|
284
|
+
url.searchParams.set("requestSignUp", String(options.requestSignUp));
|
|
285
|
+
for (const scope of options.scopes ?? []) {
|
|
286
|
+
url.searchParams.append("scope", scope);
|
|
287
|
+
}
|
|
288
|
+
return url.toString();
|
|
289
|
+
} catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function notifySessionChanged(client, session) {
|
|
294
|
+
const store = client.$store;
|
|
295
|
+
store?.notify?.("$sessionSignal");
|
|
296
|
+
if (typeof window === "undefined" || typeof CustomEvent === "undefined")
|
|
297
|
+
return;
|
|
298
|
+
window.dispatchEvent(new CustomEvent(SESSION_EVENT_TYPE, { detail: { session } }));
|
|
299
|
+
}
|
|
300
|
+
async function getSessionAfterPopupComplete(client, fallback) {
|
|
301
|
+
const session = await client.getSession?.();
|
|
302
|
+
const data = session?.data ?? fallback ?? null;
|
|
303
|
+
notifySessionChanged(client, data);
|
|
304
|
+
return data;
|
|
305
|
+
}
|
|
306
|
+
function toError(error, fallback) {
|
|
307
|
+
if (error instanceof Error)
|
|
308
|
+
return error;
|
|
309
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
310
|
+
const message = error.message;
|
|
311
|
+
if (typeof message === "string" && message)
|
|
312
|
+
return new Error(message);
|
|
313
|
+
}
|
|
314
|
+
if (typeof error === "string" && error)
|
|
315
|
+
return new Error(error);
|
|
316
|
+
return new Error(fallback);
|
|
236
317
|
}
|
|
318
|
+
function getRedirectUrl(data) {
|
|
319
|
+
if (!data || typeof data !== "object")
|
|
320
|
+
return null;
|
|
321
|
+
const fields = data;
|
|
322
|
+
for (const key of ["url", "redirectURL", "redirectUrl"]) {
|
|
323
|
+
const value = fields[key];
|
|
324
|
+
if (typeof value === "string" && value)
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
function getPopupFeatures(configuredFeatures) {
|
|
330
|
+
if (configuredFeatures)
|
|
331
|
+
return configuredFeatures;
|
|
332
|
+
const width = 520;
|
|
333
|
+
const height = 720;
|
|
334
|
+
const left = typeof window === "undefined" ? 0 : window.screenX + (window.outerWidth - width) / 2;
|
|
335
|
+
const top = typeof window === "undefined" ? 0 : window.screenY + (window.outerHeight - height) / 2;
|
|
336
|
+
return `popup=yes,width=${width},height=${height},left=${left},top=${top}`;
|
|
337
|
+
}
|
|
338
|
+
function openSyncPopup(features) {
|
|
339
|
+
if (typeof window === "undefined")
|
|
340
|
+
return null;
|
|
341
|
+
try {
|
|
342
|
+
return window.open("about:blank", POPUP_NAME, features);
|
|
343
|
+
} catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function navigatePopup(popup, url) {
|
|
348
|
+
try {
|
|
349
|
+
popup.location.href = url;
|
|
350
|
+
return true;
|
|
351
|
+
} catch {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function popupReturnedToOpenerOrigin(popup) {
|
|
356
|
+
if (typeof window === "undefined")
|
|
357
|
+
return false;
|
|
358
|
+
try {
|
|
359
|
+
const href = popup.location.href;
|
|
360
|
+
if (!href || href === "about:blank")
|
|
361
|
+
return false;
|
|
362
|
+
return new URL(href).origin === window.location.origin;
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function waitForPopupSession(client, popup, timeoutMs) {
|
|
368
|
+
const startedAt = Date.now();
|
|
369
|
+
return new Promise((resolve, reject) => {
|
|
370
|
+
const interval = setInterval(async () => {
|
|
371
|
+
if (popup.closed) {
|
|
372
|
+
clearInterval(interval);
|
|
373
|
+
reject(new Error("Sign in was cancelled"));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
377
|
+
clearInterval(interval);
|
|
378
|
+
popup.close();
|
|
379
|
+
reject(new Error("OAuth timed out waiting for session"));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (popupReturnedToOpenerOrigin(popup)) {
|
|
383
|
+
clearInterval(interval);
|
|
384
|
+
try {
|
|
385
|
+
const session = await client.getSession?.();
|
|
386
|
+
popup.close();
|
|
387
|
+
resolve(session?.data ?? null);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
popup.close();
|
|
390
|
+
reject(toError(error, "Failed to read session after popup completed"));
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
const session = await client.getSession?.();
|
|
396
|
+
if (session?.data) {
|
|
397
|
+
clearInterval(interval);
|
|
398
|
+
popup.close();
|
|
399
|
+
resolve(session.data);
|
|
400
|
+
}
|
|
401
|
+
} catch {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
}, POPUP_CHECK_INTERVAL_MS2);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function isPopupMessageData(data) {
|
|
408
|
+
return Boolean(data && typeof data === "object" && data.type === POPUP_MESSAGE_TYPE);
|
|
409
|
+
}
|
|
410
|
+
async function waitForPopupMessage(client, popup, popupId, timeoutMs) {
|
|
411
|
+
const startedAt = Date.now();
|
|
412
|
+
return new Promise((resolve, reject) => {
|
|
413
|
+
let interval;
|
|
414
|
+
let timeout;
|
|
415
|
+
const cleanup = () => {
|
|
416
|
+
window.removeEventListener("message", onMessage);
|
|
417
|
+
if (interval)
|
|
418
|
+
clearInterval(interval);
|
|
419
|
+
if (timeout)
|
|
420
|
+
clearTimeout(timeout);
|
|
421
|
+
};
|
|
422
|
+
const onMessage = (event) => {
|
|
423
|
+
if (event.origin !== window.location.origin)
|
|
424
|
+
return;
|
|
425
|
+
if (event.source && event.source !== popup)
|
|
426
|
+
return;
|
|
427
|
+
if (!isPopupMessageData(event.data) || event.data.popupId !== popupId)
|
|
428
|
+
return;
|
|
429
|
+
cleanup();
|
|
430
|
+
popup.close();
|
|
431
|
+
if (event.data.error) {
|
|
432
|
+
reject(new Error(event.data.error));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
client.getSession?.().then((session) => session?.data ?? event.data.session ?? null).then((session) => {
|
|
436
|
+
notifySessionChanged(client, session);
|
|
437
|
+
resolve(session);
|
|
438
|
+
}).catch((error) => {
|
|
439
|
+
reject(toError(error, "Failed to read session after popup completed"));
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
window.addEventListener("message", onMessage);
|
|
443
|
+
interval = setInterval(() => {
|
|
444
|
+
if (popup.closed) {
|
|
445
|
+
cleanup();
|
|
446
|
+
getSessionAfterPopupComplete(client, null).then((session) => {
|
|
447
|
+
if (session) {
|
|
448
|
+
resolve(session);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
reject(new Error("Sign in was cancelled"));
|
|
452
|
+
}).catch(() => {
|
|
453
|
+
reject(new Error("Sign in was cancelled"));
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}, POPUP_CHECK_INTERVAL_MS2);
|
|
457
|
+
timeout = setTimeout(
|
|
458
|
+
() => {
|
|
459
|
+
cleanup();
|
|
460
|
+
popup.close();
|
|
461
|
+
reject(new Error("OAuth timed out waiting for popup message"));
|
|
462
|
+
},
|
|
463
|
+
Math.max(0, timeoutMs - (Date.now() - startedAt))
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
function newAuthClient(options = {}) {
|
|
468
|
+
const {
|
|
469
|
+
providerId = DEFAULT_PROVIDER_ID,
|
|
470
|
+
signInMode = "auto",
|
|
471
|
+
popupFeatures,
|
|
472
|
+
popupTimeoutMs = DEFAULT_POPUP_TIMEOUT_MS,
|
|
473
|
+
...betterAuthOptions
|
|
474
|
+
} = options;
|
|
475
|
+
const baseURL = resolveBaseURL(betterAuthOptions.baseURL);
|
|
476
|
+
const basePath = resolveAuthBasePath(betterAuthOptions.basePath);
|
|
477
|
+
const client = (0, import_react.createAuthClient)({
|
|
478
|
+
...betterAuthOptions,
|
|
479
|
+
baseURL,
|
|
480
|
+
plugins: [(0, import_plugins.genericOAuthClient)(), ...betterAuthOptions.plugins ?? []]
|
|
481
|
+
});
|
|
482
|
+
const oauthClient = client;
|
|
483
|
+
const signInWithLovable = async (signInOptions = {}) => {
|
|
484
|
+
const mode = resolveSignInMode(signInOptions.mode ?? signInMode);
|
|
485
|
+
const usePopupMessage = mode === "popup" && isLivePreview();
|
|
486
|
+
if (mode === "popup" && typeof window === "undefined") {
|
|
487
|
+
return {
|
|
488
|
+
data: null,
|
|
489
|
+
error: new Error("Popup sign in requires a browser"),
|
|
490
|
+
mode
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const features = getPopupFeatures(signInOptions.popupFeatures ?? popupFeatures);
|
|
494
|
+
const popup = mode === "popup" ? openSyncPopup(features) : null;
|
|
495
|
+
if (mode === "popup" && !popup) {
|
|
496
|
+
return { data: null, error: new Error("Popup was blocked"), mode };
|
|
497
|
+
}
|
|
498
|
+
const popupId = usePopupMessage ? generatePopupId() : void 0;
|
|
499
|
+
const callbackURL = popupId ? resolveSameOriginCallbackURL(signInOptions.callbackURL) : resolveCallbackURL(signInOptions.callbackURL);
|
|
500
|
+
const errorCallbackURL = popupId && signInOptions.errorCallbackURL ? resolveSameOriginCallbackURL(signInOptions.errorCallbackURL) : resolveCallbackURL(signInOptions.errorCallbackURL) ?? callbackURL;
|
|
501
|
+
const newUserCallbackURL = popupId && signInOptions.newUserCallbackURL ? resolveSameOriginCallbackURL(signInOptions.newUserCallbackURL) : resolveCallbackURL(signInOptions.newUserCallbackURL) ?? callbackURL;
|
|
502
|
+
if (popupId && (!callbackURL || !errorCallbackURL || !newUserCallbackURL)) {
|
|
503
|
+
popup?.close();
|
|
504
|
+
return {
|
|
505
|
+
data: null,
|
|
506
|
+
error: new Error("Iframe popup sign in requires same-origin callback URLs"),
|
|
507
|
+
mode
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (popupId && callbackURL && errorCallbackURL && newUserCallbackURL && popup) {
|
|
511
|
+
const popupCallbackURL = buildPopupCompleteURL(baseURL, basePath, popupId, callbackURL);
|
|
512
|
+
const popupErrorCallbackURL = buildPopupCompleteURL(baseURL, basePath, popupId, errorCallbackURL);
|
|
513
|
+
const popupNewUserCallbackURL = buildPopupCompleteURL(baseURL, basePath, popupId, newUserCallbackURL);
|
|
514
|
+
const popupStartURL = popupCallbackURL && popupErrorCallbackURL && popupNewUserCallbackURL ? buildPopupStartURL(
|
|
515
|
+
baseURL,
|
|
516
|
+
basePath,
|
|
517
|
+
providerId,
|
|
518
|
+
signInOptions,
|
|
519
|
+
popupCallbackURL,
|
|
520
|
+
popupErrorCallbackURL,
|
|
521
|
+
popupNewUserCallbackURL
|
|
522
|
+
) : null;
|
|
523
|
+
if (!popupStartURL) {
|
|
524
|
+
popup.close();
|
|
525
|
+
return {
|
|
526
|
+
data: null,
|
|
527
|
+
error: new Error("Failed to build iframe popup sign-in URL"),
|
|
528
|
+
mode
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (!navigatePopup(popup, popupStartURL)) {
|
|
532
|
+
popup.close();
|
|
533
|
+
return {
|
|
534
|
+
data: null,
|
|
535
|
+
error: new Error("Failed to navigate popup"),
|
|
536
|
+
mode
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
const timeoutMs = signInOptions.popupTimeoutMs ?? popupTimeoutMs;
|
|
541
|
+
const session = await waitForPopupMessage(oauthClient, popup, popupId, timeoutMs);
|
|
542
|
+
return { data: session, error: null, mode };
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return { data: null, error: toError(error, "Sign in failed"), mode };
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const result = await oauthClient.signIn.oauth2({
|
|
548
|
+
providerId,
|
|
549
|
+
callbackURL,
|
|
550
|
+
errorCallbackURL: signInOptions.errorCallbackURL,
|
|
551
|
+
newUserCallbackURL: signInOptions.newUserCallbackURL,
|
|
552
|
+
scopes: signInOptions.scopes,
|
|
553
|
+
requestSignUp: signInOptions.requestSignUp,
|
|
554
|
+
disableRedirect: mode === "popup"
|
|
555
|
+
});
|
|
556
|
+
if (result.error) {
|
|
557
|
+
popup?.close();
|
|
558
|
+
return {
|
|
559
|
+
data: null,
|
|
560
|
+
error: toError(result.error, "Sign in failed"),
|
|
561
|
+
mode
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
if (mode === "redirect") {
|
|
565
|
+
return { data: result.data ?? null, error: null, mode };
|
|
566
|
+
}
|
|
567
|
+
const redirectUrl = getRedirectUrl(result.data);
|
|
568
|
+
if (!redirectUrl || !popup) {
|
|
569
|
+
popup?.close();
|
|
570
|
+
return {
|
|
571
|
+
data: null,
|
|
572
|
+
error: new Error("No OAuth redirect URL received"),
|
|
573
|
+
mode
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
if (!navigatePopup(popup, redirectUrl)) {
|
|
577
|
+
popup.close();
|
|
578
|
+
return {
|
|
579
|
+
data: null,
|
|
580
|
+
error: new Error("Failed to navigate popup"),
|
|
581
|
+
mode
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const timeoutMs = signInOptions.popupTimeoutMs ?? popupTimeoutMs;
|
|
586
|
+
const session = await waitForPopupSession(oauthClient, popup, timeoutMs);
|
|
587
|
+
return { data: session, error: null, mode };
|
|
588
|
+
} catch (error) {
|
|
589
|
+
return { data: null, error: toError(error, "Sign in failed"), mode };
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
return new Proxy(client, {
|
|
593
|
+
get(target, prop, receiver) {
|
|
594
|
+
if (prop === "signInWithLovable")
|
|
595
|
+
return signInWithLovable;
|
|
596
|
+
return Reflect.get(target, prop, receiver);
|
|
597
|
+
},
|
|
598
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
599
|
+
if (prop === "signInWithLovable") {
|
|
600
|
+
return {
|
|
601
|
+
configurable: true,
|
|
602
|
+
enumerable: true,
|
|
603
|
+
value: signInWithLovable,
|
|
604
|
+
writable: false
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
608
|
+
},
|
|
609
|
+
has(target, prop) {
|
|
610
|
+
return prop === "signInWithLovable" || Reflect.has(target, prop);
|
|
611
|
+
},
|
|
612
|
+
ownKeys(target) {
|
|
613
|
+
return [.../* @__PURE__ */ new Set([...Reflect.ownKeys(target), "signInWithLovable"])];
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/index.ts
|
|
237
619
|
function createLovableAuth(config = {}) {
|
|
238
620
|
return createAuth(config);
|
|
239
621
|
}
|
|
240
622
|
// Annotate the CommonJS export names for ESM import in node:
|
|
241
623
|
0 && (module.exports = {
|
|
242
|
-
createLovableAuth
|
|
624
|
+
createLovableAuth,
|
|
625
|
+
newAuthClient
|
|
243
626
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createAuthClient } from 'better-auth/react';
|
|
2
|
+
|
|
1
3
|
type OAuthProvider = "google" | "apple" | "microsoft" | "lovable";
|
|
2
4
|
interface LovableAuthConfig {
|
|
3
5
|
oauthBrokerUrl?: string;
|
|
@@ -28,6 +30,54 @@ interface LovableAuth {
|
|
|
28
30
|
signInWithOAuth: (provider: OAuthProvider, opts?: SignInWithOAuthOptions) => Promise<SignInWithOAuthResult>;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
type LovableAuthSignInMode = "auto" | "redirect" | "popup";
|
|
34
|
+
type BetterAuthClientOptions = NonNullable<Parameters<typeof createAuthClient>[0]>;
|
|
35
|
+
type NewAuthClientOptions = BetterAuthClientOptions & {
|
|
36
|
+
providerId?: string;
|
|
37
|
+
signInMode?: LovableAuthSignInMode;
|
|
38
|
+
popupFeatures?: string;
|
|
39
|
+
popupTimeoutMs?: number;
|
|
40
|
+
};
|
|
41
|
+
interface SignInWithLovableOptions {
|
|
42
|
+
callbackURL?: string;
|
|
43
|
+
errorCallbackURL?: string;
|
|
44
|
+
newUserCallbackURL?: string;
|
|
45
|
+
scopes?: string[];
|
|
46
|
+
requestSignUp?: boolean;
|
|
47
|
+
mode?: LovableAuthSignInMode;
|
|
48
|
+
popupFeatures?: string;
|
|
49
|
+
popupTimeoutMs?: number;
|
|
50
|
+
}
|
|
51
|
+
type BetterAuthResult<T = unknown> = {
|
|
52
|
+
data: T;
|
|
53
|
+
error: null;
|
|
54
|
+
mode: Exclude<LovableAuthSignInMode, "auto">;
|
|
55
|
+
} | {
|
|
56
|
+
data: null;
|
|
57
|
+
error: Error;
|
|
58
|
+
mode: Exclude<LovableAuthSignInMode, "auto">;
|
|
59
|
+
};
|
|
60
|
+
type OAuth2Client = {
|
|
61
|
+
signIn: {
|
|
62
|
+
oauth2: (body: Record<string, unknown>) => Promise<{
|
|
63
|
+
data?: unknown;
|
|
64
|
+
error?: unknown;
|
|
65
|
+
}>;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
};
|
|
68
|
+
getSession?: () => Promise<{
|
|
69
|
+
data?: unknown;
|
|
70
|
+
error?: unknown;
|
|
71
|
+
}>;
|
|
72
|
+
};
|
|
73
|
+
interface LovableBetterAuthClient extends OAuth2Client {
|
|
74
|
+
signInWithLovable: (options?: SignInWithLovableOptions) => Promise<BetterAuthResult>;
|
|
75
|
+
signOut?: (...args: unknown[]) => unknown;
|
|
76
|
+
useSession?: unknown;
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
declare function newAuthClient(options?: NewAuthClientOptions): LovableBetterAuthClient;
|
|
80
|
+
|
|
31
81
|
declare function createLovableAuth(config?: LovableAuthConfig): LovableAuth;
|
|
32
82
|
|
|
33
|
-
export { LovableAuth, LovableAuthConfig, OAuthProvider, OAuthTokens, SignInWithOAuthOptions, SignInWithOAuthResult, createLovableAuth };
|
|
83
|
+
export { type BetterAuthClientOptions, type BetterAuthResult, type LovableAuth, type LovableAuthConfig, type LovableAuthSignInMode, type LovableBetterAuthClient, type NewAuthClientOptions, type OAuthProvider, type OAuthTokens, type SignInWithLovableOptions, type SignInWithOAuthOptions, type SignInWithOAuthResult, createLovableAuth, newAuthClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createAuthClient } from 'better-auth/react';
|
|
2
|
+
|
|
1
3
|
type OAuthProvider = "google" | "apple" | "microsoft" | "lovable";
|
|
2
4
|
interface LovableAuthConfig {
|
|
3
5
|
oauthBrokerUrl?: string;
|
|
@@ -28,6 +30,54 @@ interface LovableAuth {
|
|
|
28
30
|
signInWithOAuth: (provider: OAuthProvider, opts?: SignInWithOAuthOptions) => Promise<SignInWithOAuthResult>;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
type LovableAuthSignInMode = "auto" | "redirect" | "popup";
|
|
34
|
+
type BetterAuthClientOptions = NonNullable<Parameters<typeof createAuthClient>[0]>;
|
|
35
|
+
type NewAuthClientOptions = BetterAuthClientOptions & {
|
|
36
|
+
providerId?: string;
|
|
37
|
+
signInMode?: LovableAuthSignInMode;
|
|
38
|
+
popupFeatures?: string;
|
|
39
|
+
popupTimeoutMs?: number;
|
|
40
|
+
};
|
|
41
|
+
interface SignInWithLovableOptions {
|
|
42
|
+
callbackURL?: string;
|
|
43
|
+
errorCallbackURL?: string;
|
|
44
|
+
newUserCallbackURL?: string;
|
|
45
|
+
scopes?: string[];
|
|
46
|
+
requestSignUp?: boolean;
|
|
47
|
+
mode?: LovableAuthSignInMode;
|
|
48
|
+
popupFeatures?: string;
|
|
49
|
+
popupTimeoutMs?: number;
|
|
50
|
+
}
|
|
51
|
+
type BetterAuthResult<T = unknown> = {
|
|
52
|
+
data: T;
|
|
53
|
+
error: null;
|
|
54
|
+
mode: Exclude<LovableAuthSignInMode, "auto">;
|
|
55
|
+
} | {
|
|
56
|
+
data: null;
|
|
57
|
+
error: Error;
|
|
58
|
+
mode: Exclude<LovableAuthSignInMode, "auto">;
|
|
59
|
+
};
|
|
60
|
+
type OAuth2Client = {
|
|
61
|
+
signIn: {
|
|
62
|
+
oauth2: (body: Record<string, unknown>) => Promise<{
|
|
63
|
+
data?: unknown;
|
|
64
|
+
error?: unknown;
|
|
65
|
+
}>;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
};
|
|
68
|
+
getSession?: () => Promise<{
|
|
69
|
+
data?: unknown;
|
|
70
|
+
error?: unknown;
|
|
71
|
+
}>;
|
|
72
|
+
};
|
|
73
|
+
interface LovableBetterAuthClient extends OAuth2Client {
|
|
74
|
+
signInWithLovable: (options?: SignInWithLovableOptions) => Promise<BetterAuthResult>;
|
|
75
|
+
signOut?: (...args: unknown[]) => unknown;
|
|
76
|
+
useSession?: unknown;
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
declare function newAuthClient(options?: NewAuthClientOptions): LovableBetterAuthClient;
|
|
80
|
+
|
|
31
81
|
declare function createLovableAuth(config?: LovableAuthConfig): LovableAuth;
|
|
32
82
|
|
|
33
|
-
export { LovableAuth, LovableAuthConfig, OAuthProvider, OAuthTokens, SignInWithOAuthOptions, SignInWithOAuthResult, createLovableAuth };
|
|
83
|
+
export { type BetterAuthClientOptions, type BetterAuthResult, type LovableAuth, type LovableAuthConfig, type LovableAuthSignInMode, type LovableBetterAuthClient, type NewAuthClientOptions, type OAuthProvider, type OAuthTokens, type SignInWithLovableOptions, type SignInWithOAuthOptions, type SignInWithOAuthResult, createLovableAuth, newAuthClient };
|