@lovable.dev/cloud-auth-js 1.1.3-dev.16 → 1.1.3-dev.17

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/dist/server.js DELETED
@@ -1,322 +0,0 @@
1
- // src/server.ts
2
- import { betterAuth } from "better-auth";
3
- import { memoryAdapter } from "better-auth/adapters/memory";
4
- import { genericOAuth } from "better-auth/plugins";
5
- import { createRemoteJWKSet, jwtVerify } from "jose";
6
- var DEFAULT_PROVIDER_ID = "lovable";
7
- var DEFAULT_SCOPES = ["openid", "offline", "email", "profile"];
8
- var POPUP_MESSAGE_TYPE = "lovable:better-auth-popup-complete";
9
- var POPUP_START_SUFFIX = "/lovable-popup/start";
10
- var POPUP_COMPLETE_SUFFIX = "/lovable-popup/complete";
11
- var globalForDb = globalThis;
12
- function getMemoryDb() {
13
- globalForDb.__lovableCloudAuthMemoryDb ?? (globalForDb.__lovableCloudAuthMemoryDb = {
14
- user: [],
15
- session: [],
16
- account: [],
17
- verification: []
18
- });
19
- return globalForDb.__lovableCloudAuthMemoryDb;
20
- }
21
- function getEnv() {
22
- if (typeof process === "undefined")
23
- return {};
24
- return process.env;
25
- }
26
- function parseList(value) {
27
- const entries = value?.split(",").map((entry) => entry.trim()).filter(Boolean);
28
- return entries && entries.length > 0 ? entries : void 0;
29
- }
30
- function required(name, value) {
31
- if (value)
32
- return value;
33
- throw new Error(`Missing required env var: ${name}`);
34
- }
35
- function resolveTrustedOrigins(options) {
36
- const env = options.env ?? getEnv();
37
- return options.trustedOrigins ?? parseList(env.LOVABLE_AUTH_TRUSTED_ORIGINS) ?? [];
38
- }
39
- function originToHost(origin) {
40
- try {
41
- return new URL(origin).host;
42
- } catch {
43
- return void 0;
44
- }
45
- }
46
- function resolveAuthBaseURL(authBaseURL, fallbackBaseURL, trustedOrigins) {
47
- if (authBaseURL !== void 0)
48
- return authBaseURL;
49
- const allowedHosts = trustedOrigins.map(originToHost).filter((host) => Boolean(host));
50
- if (allowedHosts.length === 0)
51
- return fallbackBaseURL;
52
- return {
53
- allowedHosts,
54
- ...fallbackBaseURL ? { fallback: fallbackBaseURL } : {}
55
- };
56
- }
57
- function isDynamicAuthBaseURL(baseURL) {
58
- return typeof baseURL === "object" && baseURL !== null && "allowedHosts" in baseURL;
59
- }
60
- function resolveDefaultCookieAttributes(isIframe, configured) {
61
- return {
62
- sameSite: isIframe ? "none" : "lax",
63
- secure: true,
64
- ...configured
65
- };
66
- }
67
- function getIdToken(tokens) {
68
- if (!tokens || typeof tokens !== "object")
69
- return void 0;
70
- const fields = tokens;
71
- if (typeof fields.idToken === "string")
72
- return fields.idToken;
73
- if (typeof fields.id_token === "string")
74
- return fields.id_token;
75
- if (typeof fields.raw?.id_token === "string")
76
- return fields.raw.id_token;
77
- return void 0;
78
- }
79
- async function fetchOpenIdConfiguration(discoveryUrl) {
80
- const response = await fetch(discoveryUrl);
81
- if (!response.ok)
82
- throw new Error(`Failed to fetch OAuth discovery document: ${response.status}`);
83
- const json = await response.json();
84
- if (typeof json.issuer !== "string" || typeof json.jwks_uri !== "string") {
85
- throw new Error("Invalid OAuth discovery document");
86
- }
87
- return { issuer: json.issuer, jwksUri: json.jwks_uri };
88
- }
89
- function getStringClaim(payload, name) {
90
- const value = payload[name];
91
- return typeof value === "string" ? value : void 0;
92
- }
93
- function getBooleanClaim(payload, name) {
94
- const value = payload[name];
95
- return typeof value === "boolean" ? value : void 0;
96
- }
97
- function toVerifiedIdTokenClaims(payload) {
98
- const sub = getStringClaim(payload, "sub");
99
- if (!sub)
100
- throw new Error("Invalid id_token subject");
101
- return {
102
- sub,
103
- email: getStringClaim(payload, "email"),
104
- email_verified: getBooleanClaim(payload, "email_verified"),
105
- name: getStringClaim(payload, "name"),
106
- picture: getStringClaim(payload, "picture")
107
- };
108
- }
109
- function createIdTokenVerifier(discoveryUrl, clientId) {
110
- let discoveryPromise;
111
- let jwks;
112
- return async (idToken) => {
113
- discoveryPromise ?? (discoveryPromise = fetchOpenIdConfiguration(discoveryUrl));
114
- const discovery = await discoveryPromise;
115
- jwks ?? (jwks = createRemoteJWKSet(new URL(discovery.jwksUri)));
116
- const { payload } = await jwtVerify(idToken, jwks, {
117
- issuer: discovery.issuer,
118
- audience: clientId,
119
- requiredClaims: ["sub"]
120
- });
121
- return toVerifiedIdTokenClaims(payload);
122
- };
123
- }
124
- function newAuthServer(options = {}) {
125
- const env = options.env ?? getEnv();
126
- const tenantId = required("LOVABLE_TENANT_ID", options.tenantId ?? env.LOVABLE_TENANT_ID);
127
- const clientId = required("LOVABLE_OAUTH_CLIENT_ID", options.clientId ?? env.LOVABLE_OAUTH_CLIENT_ID);
128
- const clientSecret = required("LOVABLE_OAUTH_CLIENT_SECRET", options.clientSecret ?? env.LOVABLE_OAUTH_CLIENT_SECRET);
129
- const oauthUpstream = options.oauthUpstream ?? `https://${tenantId}.auth.lovable.app`;
130
- const trustedOrigins = resolveTrustedOrigins({ env, trustedOrigins: options.trustedOrigins });
131
- const fallbackBaseURL = options.fallbackBaseURL ?? trustedOrigins[0];
132
- const authOptions = options.authOptions ?? {};
133
- const discoveryUrl = `${oauthUpstream}/.well-known/openid-configuration`;
134
- const verifyIdToken = createIdTokenVerifier(discoveryUrl, clientId);
135
- const baseURL = resolveAuthBaseURL(authOptions.baseURL, fallbackBaseURL, trustedOrigins);
136
- const trustedProxyHeaders = authOptions.advanced?.trustedProxyHeaders ?? isDynamicAuthBaseURL(baseURL);
137
- const defaultCookieAttributes = resolveDefaultCookieAttributes(
138
- options.isIframe,
139
- authOptions.advanced?.defaultCookieAttributes
140
- );
141
- return betterAuth({
142
- ...authOptions,
143
- ...baseURL ? { baseURL } : {},
144
- trustedOrigins: authOptions.trustedOrigins ?? trustedOrigins,
145
- advanced: {
146
- ...authOptions.advanced,
147
- defaultCookieAttributes,
148
- trustedProxyHeaders
149
- },
150
- secret: authOptions.secret ?? env.LOVABLE_AUTH_SECRET ?? "dev-only-better-auth-secret",
151
- database: authOptions.database ?? memoryAdapter(getMemoryDb()),
152
- session: authOptions.session ?? {
153
- cookieCache: { enabled: true, maxAge: 60 * 5 }
154
- },
155
- plugins: [
156
- genericOAuth({
157
- config: [
158
- {
159
- providerId: options.providerId ?? DEFAULT_PROVIDER_ID,
160
- clientId,
161
- clientSecret,
162
- discoveryUrl,
163
- scopes: options.scopes ?? DEFAULT_SCOPES,
164
- pkce: true,
165
- responseType: "code",
166
- authentication: "basic",
167
- getUserInfo: async (tokens) => {
168
- const idToken = getIdToken(tokens);
169
- if (!idToken)
170
- throw new Error("No id_token returned from Lovable");
171
- const claims = await verifyIdToken(idToken);
172
- return {
173
- id: claims.sub,
174
- email: claims.email ?? `${claims.sub}@users.lovable.app`,
175
- emailVerified: claims.email_verified ?? false,
176
- name: claims.name ?? claims.email ?? claims.sub,
177
- image: claims.picture,
178
- createdAt: /* @__PURE__ */ new Date(),
179
- updatedAt: /* @__PURE__ */ new Date()
180
- };
181
- }
182
- }
183
- ]
184
- }),
185
- ...authOptions.plugins ?? []
186
- ]
187
- });
188
- }
189
- function isLovablePopupStartRequest(request) {
190
- if (request.method !== "GET")
191
- return false;
192
- return new URL(request.url).pathname.endsWith(POPUP_START_SUFFIX);
193
- }
194
- function isLovablePopupCompleteRequest(request) {
195
- if (request.method !== "GET")
196
- return false;
197
- return new URL(request.url).pathname.endsWith(POPUP_COMPLETE_SUFFIX);
198
- }
199
- function popupStartAuthBasePath(pathname) {
200
- return pathname.slice(0, -POPUP_START_SUFFIX.length) || "/api/auth";
201
- }
202
- function popupStartBooleanParam(value) {
203
- if (value === null)
204
- return void 0;
205
- return value === "true";
206
- }
207
- function popupStartSignInBody(url) {
208
- const scopes = url.searchParams.getAll("scope");
209
- return {
210
- providerId: url.searchParams.get("providerId") || DEFAULT_PROVIDER_ID,
211
- callbackURL: url.searchParams.get("callbackURL") || void 0,
212
- errorCallbackURL: url.searchParams.get("errorCallbackURL") || void 0,
213
- newUserCallbackURL: url.searchParams.get("newUserCallbackURL") || void 0,
214
- scopes: scopes.length > 0 ? scopes : void 0,
215
- requestSignUp: popupStartBooleanParam(url.searchParams.get("requestSignUp")),
216
- disableRedirect: true
217
- };
218
- }
219
- function getSetCookieHeaders(headers) {
220
- const getSetCookie = headers.getSetCookie;
221
- if (getSetCookie)
222
- return getSetCookie.call(headers);
223
- const setCookie = headers.get("set-cookie");
224
- return setCookie ? [setCookie] : [];
225
- }
226
- function getOAuthRedirectUrl(data) {
227
- if (typeof data.url === "string")
228
- return data.url;
229
- if (typeof data.redirectURL === "string")
230
- return data.redirectURL;
231
- if (typeof data.redirectUrl === "string")
232
- return data.redirectUrl;
233
- return void 0;
234
- }
235
- function scriptJson(value) {
236
- return JSON.stringify(value).replace(/</g, "\\u003c");
237
- }
238
- function handleLovablePopupComplete(request) {
239
- const url = new URL(request.url);
240
- const error = url.searchParams.get("error_description") || url.searchParams.get("error") || void 0;
241
- const message = {
242
- type: POPUP_MESSAGE_TYPE,
243
- popupId: url.searchParams.get("popupId") || "",
244
- ...error ? { error } : {},
245
- ...url.searchParams.get("returnTo") ? { returnTo: url.searchParams.get("returnTo") } : {}
246
- };
247
- const html = `<!doctype html>
248
- <html>
249
- <head><meta charset="utf-8"><title>Authentication complete</title></head>
250
- <body>
251
- <script>
252
- (() => {
253
- const message = ${scriptJson(message)};
254
- const targetOrigin = ${scriptJson(url.origin)};
255
- if (window.opener && message.popupId) window.opener.postMessage(message, targetOrigin);
256
- window.close();
257
- setTimeout(() => {
258
- if (!window.closed && message.returnTo) window.location.replace(message.returnTo);
259
- }, 250);
260
- })();
261
- </script>
262
- </body>
263
- </html>`;
264
- return new Response(html, {
265
- headers: {
266
- "content-type": "text/html; charset=utf-8",
267
- "cache-control": "no-store"
268
- }
269
- });
270
- }
271
- async function handleLovablePopupStart(auth, request) {
272
- const url = new URL(request.url);
273
- const authBasePath = popupStartAuthBasePath(url.pathname);
274
- const signInUrl = new URL(request.url);
275
- signInUrl.pathname = `${authBasePath}/sign-in/oauth2`;
276
- signInUrl.search = "";
277
- const headers = new Headers(request.headers);
278
- headers.set("content-type", "application/json");
279
- const response = await auth.handler(
280
- new Request(signInUrl.toString(), {
281
- method: "POST",
282
- headers,
283
- body: JSON.stringify(popupStartSignInBody(url))
284
- })
285
- );
286
- if (!response.ok)
287
- return response;
288
- const data = await response.json();
289
- const redirectUrl = getOAuthRedirectUrl(data);
290
- if (!redirectUrl)
291
- return new Response("No OAuth redirect URL received", { status: 500 });
292
- const redirectHeaders = new Headers({ location: redirectUrl });
293
- for (const setCookie of getSetCookieHeaders(response.headers)) {
294
- redirectHeaders.append("set-cookie", setCookie);
295
- }
296
- return new Response(null, { status: 302, headers: redirectHeaders });
297
- }
298
- function createAuthRouteHandler(auth = newAuthServer()) {
299
- return ({ request }) => {
300
- if (isLovablePopupCompleteRequest(request))
301
- return handleLovablePopupComplete(request);
302
- if (isLovablePopupStartRequest(request))
303
- return handleLovablePopupStart(auth, request);
304
- return auth.handler(request);
305
- };
306
- }
307
- function registerAuthRoutes(options = {}) {
308
- const handler = createAuthRouteHandler(options.auth ?? newAuthServer(options));
309
- return {
310
- server: {
311
- handlers: {
312
- GET: handler,
313
- POST: handler
314
- }
315
- }
316
- };
317
- }
318
- export {
319
- createAuthRouteHandler,
320
- newAuthServer,
321
- registerAuthRoutes
322
- };