UNPKG

@light-auth/nuxt

Version:

light auth framework for nuxt, using arctic

285 lines (277 loc) 12.9 kB
/*! @light-auth/nuxt v0.3.8 2026-04-03 */ 'use strict'; import { resolveBasePath, createHttpHandlerFunction, createSetUserServerFunction, createFetchSessionServerFunction, createSignoutServerFunction, createSigninServerFunction, getUserDirect, buildFullUrl, encryptJwt, buildSecret, DEFAULT_SESSION_NAME, decryptJwt } from '@light-auth/core'; import { getHeaders, parseCookies, setCookie, sendRedirect, deleteCookie, getCookie } from 'h3'; /** * createNextJsLightAuthSessionFunction is a function that creates a light session function for Next.js. * It takes the LightAuth createLightAuthSessionFunction base function and returns a user friendly function by * removing the req and res parameters, that are not needed in the Next.js context. */ const createGetAuthSession = (config) => { const getAuthSession = createFetchSessionServerFunction(config); return async (event) => await getAuthSession({ event }); }; const createSetAuthSession = (config) => { const setAuthSession = createFetchSessionServerFunction(config); return async (event, session) => await setAuthSession({ event, session }); }; /** * createNextJsLightAuthUserFunction is a function that creates a light user function for Next.js. * It takes the LightAuth createLightAuthUserFunction base function and returns a user friendly function by * removing the req and res parameters, that are not needed in the Next.js context. */ const createGetAuthUser = (config) => { return async (event, providerUserId) => await getUserDirect({ config, providerUserId, event }); }; const createSetAuthUser = (config) => { const setUser = createSetUserServerFunction(config); return async (event, user) => await setUser({ event, user }); }; /** * createNuxtJsSignIn is a function that creates a sign-in function for Nuxt.js. * It takes the LightAuth createSigninFunction base function and returns a user friendly function by * removing the req and res parameters, that are not needed in the Nuxt.js context. */ const createSignIn = (config) => { const signIn = createSigninServerFunction(config); return async (event, providerName, callbackUrl = "/") => { await signIn({ providerName, callbackUrl, event }); }; }; /** * createNuxtJsSignOut is a function that creates a sign-out function for Nuxt.js. * It takes the LightAuth createSignoutFunction base function and returns a user friendly function by * removing the req and res parameters, that are not needed in the Nuxt.js context. */ const createSignOut = (config) => { const signOut = createSignoutServerFunction(config); return async (event, revokeToken, callbackUrl = "/") => { await signOut({ revokeToken, callbackUrl, event }); }; }; /** * createNuxtJsLightAuthHandlerFunction is a function that creates the light auth handler for Nuxt.js. * It takes the LightAuth createHttpHandlerFunction base function and returns a user friendly function by * removing the req and res parameters, that are not needed in the Nuxt.js context. */ const createHandler = (config) => { const lightAuthHandler = createHttpHandlerFunction(config); const nuxtJsLightAuthHandler = async (event) => { const response = await lightAuthHandler({ event: event }); return response; }; return nuxtJsLightAuthHandler; }; /** * CreateLightAuth is a function that creates the LightAuth components for Nuxt.js. * It takes a LightAuthConfig object as a parameter and returns a LightAuthNuxtJsComponents object. * The function also sets default values for the userAdapter, router and cookieStore if they are not provided. */ function CreateLightAuth(config) { // check if we are on the server side const isServerSide = typeof window === "undefined"; if (!isServerSide) throw new Error("light-auth-nextjs: DO NOT use this function [CreateLightAuth] on the client side as you may expose sensitive data to the client."); if (!config.providers || config.providers.length === 0) throw new Error("light-auth: At least one provider is required"); // lazy import the user adapter to avoid circular dependencies // Using Promise.all to ensure all imports are resolved before any handler is called const pendingImports = []; if (!config.userAdapter && typeof window === "undefined") { pendingImports.push(import('@light-auth/core/adapters').then((module) => { config.userAdapter = module.createLightAuthUserAdapter({ base: "./users_db", isEncrypted: false }); })); } if (!config.sessionStore && typeof window === "undefined") { pendingImports.push(Promise.resolve().then(function () { return nuxtjsLightAuthSessionStore; }).then((module) => { config.sessionStore = module.nuxtJsLightAuthSessionStore; })); } if (!config.router && typeof window === "undefined") { pendingImports.push(Promise.resolve().then(function () { return nuxtjsLightAuthRouter; }).then((module) => { config.router = module.nuxtJsLightAuthRouter; })); } const importsReady = Promise.all(pendingImports); config.env = config.env || process.env; config.basePath = resolveBasePath(config.basePath, config.env); if (!config.env["LIGHT_AUTH_SECRET_VALUE"]) throw new Error("LIGHT_AUTH_SECRET_VALUE is required in environment variables"); const handler = createHandler(config); // Wrap all async functions to ensure dynamic imports are resolved before executing const ensureReady = (fn) => (async (...args) => { await importsReady; return fn(...args); }); return { providers: config.providers, handlers: { GET: ensureReady(handler.GET), POST: ensureReady(handler.POST), }, basePath: config.basePath, signIn: ensureReady(createSignIn(config)), signOut: ensureReady(createSignOut(config)), getAuthSession: ensureReady(createGetAuthSession(config)), setAuthSession: ensureReady(createSetAuthSession(config)), getUser: ensureReady(createGetAuthUser(config)), setUser: ensureReady(createSetAuthUser(config)), }; } /** * A concrete CookieStore implementation for Node.js server-side, * using the 'cookie' npm package and returning Response objects * with appropriate Set-Cookie headers. */ const nuxtJsLightAuthRouter = { async redirectTo({ url, event }) { if (!event) throw new Error("Event is required in redirectTo of nuxtJsLightAuthRouter."); const headersData = getHeaders(event); // { "content-type": "application/json", "x-custom-header": "value" } const incomingHeaders = new Headers(); if (headersData) { // Iterate over the headers object and append them to the new Headers object for (const key in headersData) { const value = headersData[key]; if (value) incomingHeaders.append(key, value); } } const fullUrl = buildFullUrl({ url, incomingHeaders }); await sendRedirect(event, fullUrl.toString()); }, async setCookies({ cookies, event }) { if (!event) throw new Error("Event is required in redirectTo of nuxtJsLightAuthRouter."); if (!cookies || cookies.length === 0) return; for (const cookie of cookies) { setCookie(event, cookie.name, cookie.value, { path: cookie.path, httpOnly: cookie.httpOnly, secure: cookie.secure, sameSite: cookie.sameSite, maxAge: cookie.maxAge, }); } }, async getCookies({ search, event }) { if (!event) throw new Error("Event is required in getCookies of nuxtJsLightAuthRouter."); const lightAuthCookies = []; // Convert search to RegExp if it's a string const regex = typeof search === "string" ? new RegExp(search) : search; const cookies = parseCookies(event); for (const [requestCookieKey, requestCookieValue] of Object.entries(cookies)) { if (!search || !regex || regex.test(requestCookieKey)) lightAuthCookies.push({ name: requestCookieKey, value: requestCookieValue }); } return lightAuthCookies; }, async getUrl({ endpoint, event }) { const url = endpoint ?? event?.node.req?.url; if (!url) throw new Error("light-auth: No url provided and no request object available in getUrl of nuxtJsLightAuthRouter."); if (url.startsWith("http")) return url; if (!event) return url; const headersData = getHeaders(event); const incomingHeaders = new Headers(); for (const [key, value] of Object.entries(headersData)) if (value) incomingHeaders.append(key, value); const fullUrl = buildFullUrl({ url, incomingHeaders }); return fullUrl.toString(); }, async getHeaders({ search, event }) { if (!event) return new Headers(); const headersData = getHeaders(event); // Convert search to RegExp if it's a string const regex = typeof search === "string" ? new RegExp(search) : search; // Create a new Headers object to hold filtered headers const filteredHeaders = new Headers(); // Iterate and filter headers whose names match the regex for (const [key, value] of Object.entries(headersData)) { if (!search || !regex || regex.test(key)) if (value) filteredHeaders.append(key, value); } return filteredHeaders; }, async getRequest({ env, basePath, event }) { if (!event) throw new Error("Event is required in getRequest of nuxtJsLightAuthRouter."); try { const url = await this.getUrl({ env, basePath, event }); const headers = await this.getHeaders({ env, basePath, event }); return new Request(url, { method: event.node.req.method, headers: headers }); } catch (error) { throw new Error(`light-auth: Error creating request object in getRequest of nuxtJsLightAuthRouter: ${error}`); } }, returnJson({ data, event, init }) { return Response.json(data, { ...(init ?? {}), status: init?.status ?? 200, }); }, }; var nuxtjsLightAuthRouter = /*#__PURE__*/Object.freeze({ __proto__: null, nuxtJsLightAuthRouter: nuxtJsLightAuthRouter }); /** * A concrete CookieStore implementation for Node.js server-side, * using the 'cookie' npm package and returning Response objects * with appropriate Set-Cookie headers. */ const nuxtJsLightAuthSessionStore = { async getSession({ env, event, sessionName, }) { if (!event) throw new Error("Event is required to get the session in nuxtJsLightAuthSessionStore."); const requestCookie = getCookie(event, sessionName ?? DEFAULT_SESSION_NAME); if (!requestCookie) return null; try { const decryptedSession = await decryptJwt(requestCookie, buildSecret(env)); return decryptedSession; } catch (error) { console.error("Failed to decrypt session cookie:", error); return null; } }, async deleteSession({ event, sessionName, }) { if (!event) throw new Error("Event is required to get the session in nuxtJsLightAuthSessionStore."); deleteCookie(event, sessionName ?? DEFAULT_SESSION_NAME); }, async setSession({ env, session, event, sessionName, }) { if (!event) throw new Error("Event is required to set the session in nuxtJsLightAuthSessionStore."); const value = await encryptJwt(session, buildSecret(env)); // Check the size of the cookie value in bytes const encoder = new TextEncoder(); const valueBytes = encoder.encode(value); if (valueBytes.length > 4096) throw new Error("light-auth: Cookie value exceeds 4096 bytes, which may not be supported by your browser."); // maxAge: Specifies the number (in seconds) to be the value for the `Max-Age` setCookie(event, sessionName ?? DEFAULT_SESSION_NAME, value, { httpOnly: true, secure: true, sameSite: "lax", path: "/", expires: new Date(session.expiresAt), }); return session; }, generateSessionId() { return Math.random().toString(36).slice(2); }, }; var nuxtjsLightAuthSessionStore = /*#__PURE__*/Object.freeze({ __proto__: null, nuxtJsLightAuthSessionStore: nuxtJsLightAuthSessionStore }); export { CreateLightAuth, nuxtJsLightAuthRouter, nuxtJsLightAuthSessionStore }; //# sourceMappingURL=index.mjs.map