@auth0/auth0-fastify
Advanced tools
+55
-232
@@ -33,3 +33,3 @@ "use strict"; | ||
| __export(index_exports, { | ||
| CookieTransactionStore: () => CookieTransactionStore, | ||
| CookieTransactionStore: () => import_auth0_server_js2.CookieTransactionStore, | ||
| default: () => index_default | ||
@@ -39,7 +39,27 @@ }); | ||
| var import_fastify_plugin = __toESM(require("fastify-plugin"), 1); | ||
| var import_auth0_server_js4 = require("@auth0/auth0-server-js"); | ||
| // src/store/cookie-transaction-store.ts | ||
| var import_auth0_server_js = require("@auth0/auth0-server-js"); | ||
| // src/utils.ts | ||
| function ensureTrailingSlash(value) { | ||
| return value && !value.endsWith("/") ? `${value}/` : value; | ||
| } | ||
| function ensureNoLeadingSlash(value) { | ||
| return value && value.startsWith("/") ? value.substring(1, value.length) : value; | ||
| } | ||
| function createRouteUrl(url, base) { | ||
| return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base)); | ||
| } | ||
| function toSafeRedirect(dangerousRedirect, safeBaseUrl) { | ||
| let url; | ||
| try { | ||
| url = createRouteUrl(dangerousRedirect, safeBaseUrl); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| if (url.origin === new URL(safeBaseUrl).origin) { | ||
| return url.toString(); | ||
| } | ||
| return void 0; | ||
| } | ||
| // src/errors/index.ts | ||
@@ -54,233 +74,36 @@ var MissingStoreOptionsError = class extends Error { | ||
| // src/store/cookie-transaction-store.ts | ||
| var CookieTransactionStore = class extends import_auth0_server_js.AbstractTransactionStore { | ||
| async set(identifier, transactionData, removeIfExists, options) { | ||
| if (!options) { | ||
| // src/store/fastify-cookie-handler.ts | ||
| var FastifyCookieHandler = class { | ||
| setCookie(name, value, options, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const maxAge = 60 * 60; | ||
| const cookieOpts = { httpOnly: true, sameSite: "lax", path: "/", maxAge }; | ||
| const expiration = Math.floor(Date.now() / 1e3 + maxAge); | ||
| const encryptedStateData = await this.encrypt(identifier, transactionData, expiration); | ||
| options.reply.setCookie(identifier, encryptedStateData, cookieOpts); | ||
| storeOptions.reply.setCookie(name, value, options || {}); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| getCookie(name, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieValue = options.request.cookies[identifier]; | ||
| if (cookieValue) { | ||
| return await this.decrypt(identifier, cookieValue); | ||
| } | ||
| return storeOptions.request.cookies?.[name]; | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| getCookies(storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| options?.reply.clearCookie(identifier); | ||
| return storeOptions.request.cookies; | ||
| } | ||
| }; | ||
| // src/store/stateless-state-store.ts | ||
| var import_auth0_server_js3 = require("@auth0/auth0-server-js"); | ||
| // src/store/abstract-session-store.ts | ||
| var import_auth0_server_js2 = require("@auth0/auth0-server-js"); | ||
| var AbstractSessionStore = class extends import_auth0_server_js2.AbstractStateStore { | ||
| #rolling; | ||
| #absoluteDuration; | ||
| #inactivityDuration; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#rolling = options.rolling ?? true; | ||
| this.#absoluteDuration = options.absoluteDuration ?? 60 * 60 * 24 * 3; | ||
| this.#inactivityDuration = options.inactivityDuration ?? 60 * 60 * 24 * 1; | ||
| } | ||
| /** | ||
| * calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations. | ||
| */ | ||
| calculateMaxAge(createdAt) { | ||
| if (!this.#rolling) { | ||
| return this.#absoluteDuration; | ||
| } | ||
| const now = Date.now() / 1e3 | 0; | ||
| const expiresAt = Math.min(now + this.#inactivityDuration, createdAt + this.#absoluteDuration); | ||
| const maxAge = expiresAt - now; | ||
| return maxAge > 0 ? maxAge : 0; | ||
| } | ||
| }; | ||
| // src/store/stateless-state-store.ts | ||
| var StatelessStateStore = class extends AbstractSessionStore { | ||
| #cookieOptions; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#cookieOptions = options.cookie; | ||
| } | ||
| async set(identifier, stateData, removeIfExists, options) { | ||
| if (!options) { | ||
| deleteCookie(name, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const maxAge = this.calculateMaxAge(stateData.internal.createdAt); | ||
| const cookieOpts = { | ||
| httpOnly: true, | ||
| sameSite: this.#cookieOptions?.sameSite ?? "lax", | ||
| path: "/", | ||
| secure: this.#cookieOptions?.secure ?? "auto", | ||
| maxAge | ||
| }; | ||
| const expiration = Math.floor(Date.now() / 1e3 + maxAge); | ||
| const encryptedStateData = await this.encrypt(identifier, stateData, expiration); | ||
| const chunkSize = 3072; | ||
| const chunkCount = Math.ceil(encryptedStateData.length / chunkSize); | ||
| const chunks = [...Array(chunkCount).keys()].map((i) => ({ | ||
| value: encryptedStateData.substring(i * chunkSize, (i + 1) * chunkSize), | ||
| name: `${identifier}.${i}` | ||
| })); | ||
| chunks.forEach((chunk) => { | ||
| options.reply.setCookie(chunk.name, chunk.value, cookieOpts); | ||
| }); | ||
| const existingCookieKeys = this.getCookieKeys(identifier, options); | ||
| const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key)); | ||
| cookieKeysToRemove.forEach((key) => { | ||
| options.reply.clearCookie(key); | ||
| }); | ||
| storeOptions.reply.clearCookie(name); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieKeys = this.getCookieKeys(identifier, options); | ||
| const encryptedStateData = cookieKeys.map((key) => ({ index: parseInt(key.split(".")[1], 10), value: options.request.cookies[key] })).sort((a, b) => a.index - b.index).map((item) => item.value).join(""); | ||
| if (encryptedStateData) { | ||
| return await this.decrypt(identifier, encryptedStateData); | ||
| } | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieKeys = this.getCookieKeys(identifier, options); | ||
| for (const key of cookieKeys) { | ||
| options?.reply.clearCookie(key); | ||
| } | ||
| } | ||
| deleteByLogoutToken() { | ||
| throw new import_auth0_server_js3.BackchannelLogoutError( | ||
| "Backchannel logout is not available when using Stateless Storage. Use Stateful Storage by providing a `sessionStore`" | ||
| ); | ||
| } | ||
| getCookieKeys(identifier, options) { | ||
| return Object.keys(options.request.cookies).filter((key) => key.startsWith(identifier)); | ||
| } | ||
| }; | ||
| // src/store/stateful-state-store.ts | ||
| var generateId = () => { | ||
| const bytes = new Uint8Array(16); | ||
| crypto.getRandomValues(bytes); | ||
| return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""); | ||
| }; | ||
| var StatefulStateStore = class extends AbstractSessionStore { | ||
| #store; | ||
| #cookieOptions; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#store = options.store; | ||
| this.#cookieOptions = options.cookie; | ||
| } | ||
| async set(identifier, stateData, removeIfExists, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| let sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId && removeIfExists) { | ||
| await this.#store.delete(sessionId); | ||
| sessionId = generateId(); | ||
| } | ||
| if (!sessionId) { | ||
| sessionId = generateId(); | ||
| } | ||
| const maxAge = this.calculateMaxAge(stateData.internal.createdAt); | ||
| const cookieOpts = { | ||
| httpOnly: true, | ||
| sameSite: this.#cookieOptions?.sameSite ?? "lax", | ||
| path: "/", | ||
| secure: this.#cookieOptions?.secure ?? "auto", | ||
| maxAge | ||
| }; | ||
| const expiration = Date.now() / 1e3 + maxAge; | ||
| const encryptedStateData = await this.encrypt( | ||
| identifier, | ||
| { | ||
| id: sessionId | ||
| }, | ||
| expiration | ||
| ); | ||
| await this.#store.set(sessionId, stateData); | ||
| options.reply.setCookie(identifier, encryptedStateData, cookieOpts); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId) { | ||
| const stateData = await this.#store.get(sessionId); | ||
| if (!stateData) { | ||
| options?.reply.clearCookie(identifier); | ||
| } | ||
| return stateData; | ||
| } | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId) { | ||
| await this.#store.delete(sessionId); | ||
| } | ||
| options?.reply.clearCookie(identifier); | ||
| } | ||
| async getSessionId(identifier, options) { | ||
| const cookieValue = options.request.cookies[identifier]; | ||
| if (cookieValue) { | ||
| const sessionCookie = await this.decrypt(identifier, cookieValue); | ||
| return sessionCookie.id; | ||
| } | ||
| } | ||
| deleteByLogoutToken(claims, options) { | ||
| return this.#store.deleteByLogoutToken(claims, options); | ||
| } | ||
| }; | ||
| // src/utils.ts | ||
| function ensureTrailingSlash(value) { | ||
| return value && !value.endsWith("/") ? `${value}/` : value; | ||
| } | ||
| function ensureNoLeadingSlash(value) { | ||
| return value && value.startsWith("/") ? value.substring(1, value.length) : value; | ||
| } | ||
| function createRouteUrl(url, base) { | ||
| return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base)); | ||
| } | ||
| function toSafeRedirect(dangerousRedirect, safeBaseUrl) { | ||
| let url; | ||
| try { | ||
| url = createRouteUrl(dangerousRedirect, safeBaseUrl); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| if (url.origin === new URL(safeBaseUrl).origin) { | ||
| return url.toString(); | ||
| } | ||
| return void 0; | ||
| } | ||
| // src/index.ts | ||
| var import_auth0_server_js2 = require("@auth0/auth0-server-js"); | ||
| var index_default = (0, import_fastify_plugin.default)(async function auth0Fastify(fastify, options) { | ||
| const callbackPath = "/auth/callback"; | ||
| const callbackPath = options.routes?.callback ?? "/auth/callback"; | ||
| const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl); | ||
| const auth0Client = new import_auth0_server_js4.ServerClient({ | ||
| const auth0Client = new import_auth0_server_js.ServerClient({ | ||
| domain: options.domain, | ||
@@ -295,11 +118,11 @@ clientId: options.clientId, | ||
| }, | ||
| transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }), | ||
| stateStore: options.sessionStore ? new StatefulStateStore({ | ||
| transactionStore: new import_auth0_server_js.CookieTransactionStore({ secret: options.sessionSecret }, new FastifyCookieHandler()), | ||
| stateStore: options.sessionStore ? new import_auth0_server_js.StatefulStateStore({ | ||
| ...options.sessionConfiguration, | ||
| secret: options.sessionSecret, | ||
| store: options.sessionStore | ||
| }) : new StatelessStateStore({ | ||
| }, new FastifyCookieHandler()) : new import_auth0_server_js.StatelessStateStore({ | ||
| ...options.sessionConfiguration, | ||
| secret: options.sessionSecret | ||
| }), | ||
| }, new FastifyCookieHandler()), | ||
| stateIdentifier: options.sessionConfiguration?.cookie?.name, | ||
@@ -314,3 +137,3 @@ customFetch: options.customFetch | ||
| fastify.get( | ||
| "/auth/login", | ||
| options.routes?.login ?? "/auth/login", | ||
| async (request, reply) => { | ||
@@ -329,3 +152,3 @@ const dangerousReturnTo = request.query.returnTo; | ||
| ); | ||
| fastify.get("/auth/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.callback ?? "/auth/callback", async (request, reply) => { | ||
| const { appState } = await auth0Client.completeInteractiveLogin( | ||
@@ -337,3 +160,3 @@ createRouteUrl(request.url, options.appBaseUrl), | ||
| }); | ||
| fastify.get("/auth/logout", async (request, reply) => { | ||
| fastify.get(options.routes?.logout ?? "/auth/logout", async (request, reply) => { | ||
| const returnTo = options.appBaseUrl; | ||
@@ -344,3 +167,3 @@ const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply }); | ||
| fastify.post( | ||
| "/auth/backchannel-logout", | ||
| options.routes?.backchannelLogout ?? "/auth/backchannel-logout", | ||
| async (request, reply) => { | ||
@@ -363,3 +186,3 @@ const logoutToken = request.body.logout_token; | ||
| fastify.get( | ||
| "/auth/connect", | ||
| options.routes?.connect ?? "/auth/connect", | ||
| async (request, reply) => { | ||
@@ -375,3 +198,3 @@ const { connection, connectionScope, returnTo } = request.query; | ||
| const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || "/", options.appBaseUrl); | ||
| const callbackPath2 = "/auth/connect/callback"; | ||
| const callbackPath2 = options.routes?.connectCallback ?? "/auth/connect/callback"; | ||
| const redirectUri2 = createRouteUrl(callbackPath2, options.appBaseUrl); | ||
@@ -394,3 +217,3 @@ const linkUserUrl = await fastify.auth0Client.startLinkUser( | ||
| ); | ||
| fastify.get("/auth/connect/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.connectCallback ?? "/auth/connect/callback", async (request, reply) => { | ||
| const { appState } = await fastify.auth0Client.completeLinkUser( | ||
@@ -406,3 +229,3 @@ createRouteUrl(request.url, options.appBaseUrl), | ||
| fastify.get( | ||
| "/auth/unconnect", | ||
| options.routes?.unconnect ?? "/auth/unconnect", | ||
| async (request, reply) => { | ||
@@ -418,3 +241,3 @@ const { connection, returnTo } = request.query; | ||
| const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || "/", options.appBaseUrl); | ||
| const callbackPath2 = "/auth/unconnect/callback"; | ||
| const callbackPath2 = options.routes?.unconnectCallback ?? "/auth/unconnect/callback"; | ||
| const redirectUri2 = createRouteUrl(callbackPath2, options.appBaseUrl); | ||
@@ -436,3 +259,3 @@ const linkUserUrl = await fastify.auth0Client.startUnlinkUser( | ||
| ); | ||
| fastify.get("/auth/unconnect/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.unconnectCallback ?? "/auth/unconnect/callback", async (request, reply) => { | ||
| const { appState } = await fastify.auth0Client.completeUnlinkUser( | ||
@@ -439,0 +262,0 @@ createRouteUrl(request.url, options.appBaseUrl), |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/store/cookie-transaction-store.ts","../src/errors/index.ts","../src/store/stateless-state-store.ts","../src/store/abstract-session-store.ts","../src/store/stateful-state-store.ts","../src/utils.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { ServerClient } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, SessionStore, StoreOptions } from './types.js';\nimport { CookieTransactionStore } from './store/cookie-transaction-store.js';\nimport { StatelessStateStore } from './store/stateless-state-store.js';\nimport { StatefulStateStore } from './store/stateful-state-store.js';\nimport { createRouteUrl, toSafeRedirect } from './utils.js';\n\nexport * from './types.js';\nexport { CookieTransactionStore } from './store/cookie-transaction-store.js';\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n auth0Client: ServerClient<StoreOptions> | undefined;\n }\n}\n\nexport interface Auth0FastifyOptions {\n domain: string;\n clientId: string;\n clientSecret?: string;\n clientAssertionSigningKey?: string | CryptoKey;\n clientAssertionSigningAlg?: string;\n audience?: string;\n appBaseUrl: string;\n\n pushedAuthorizationRequests?: boolean;\n\n sessionSecret: string;\n sessionStore?: SessionStore;\n sessionConfiguration?: SessionConfiguration;\n /**\n * Whether to mount the default routes for login, logout, callback and profile.\n * Defaults to true.\n */\n mountRoutes?: boolean;\n /**\n * Whether to mount the routes for account linking and unlinking.\n * Defaults to false.\n */\n mountConnectRoutes?: boolean;\n /**\n * Optional, custom Fetch implementation to use.\n */\n customFetch?: typeof fetch;\n}\n\nexport default fp(async function auth0Fastify(fastify: FastifyInstance, options: Auth0FastifyOptions) {\n const callbackPath = '/auth/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n\n const auth0Client = new ServerClient<StoreOptions>({\n domain: options.domain,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n clientAssertionSigningKey: options.clientAssertionSigningKey,\n clientAssertionSigningAlg: options.clientAssertionSigningAlg,\n authorizationParams: {\n audience: options.audience,\n redirect_uri: redirectUri.toString(),\n },\n transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }),\n stateStore: options.sessionStore\n ? new StatefulStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n store: options.sessionStore,\n })\n : new StatelessStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n }),\n stateIdentifier: options.sessionConfiguration?.cookie?.name,\n customFetch: options.customFetch,\n });\n\n if (!fastify.hasReplyDecorator('cookie')) {\n fastify.register(import('@fastify/cookie'));\n }\n\n const shouldMountRoutes = options.mountRoutes ?? true;\n\n if (shouldMountRoutes) {\n fastify.get(\n '/auth/login',\n async (\n request: FastifyRequest<{\n Querystring: { returnTo?: string };\n }>,\n reply\n ) => {\n const dangerousReturnTo = request.query.returnTo;\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n\n const authorizationUrl = await auth0Client.startInteractiveLogin(\n {\n pushedAuthorizationRequests: options.pushedAuthorizationRequests,\n appState: { returnTo: sanitizedReturnTo },\n },\n { request, reply }\n );\n\n reply.redirect(authorizationUrl.href);\n }\n );\n\n fastify.get('/auth/callback', async (request, reply) => {\n const { appState } = await auth0Client.completeInteractiveLogin<{ returnTo: string } | undefined>(\n createRouteUrl(request.url, options.appBaseUrl),\n { request, reply }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get('/auth/logout', async (request, reply) => {\n const returnTo = options.appBaseUrl;\n const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply });\n\n reply.redirect(logoutUrl.href);\n });\n\n fastify.post(\n '/auth/backchannel-logout',\n async (\n request: FastifyRequest<{\n Body: { logout_token?: string };\n }>,\n reply\n ) => {\n const logoutToken = request.body.logout_token;\n\n if (!logoutToken) {\n reply.code(400).send('Missing `logout_token` in the request body.');\n\n return;\n }\n\n try {\n await auth0Client.handleBackchannelLogout(logoutToken, { request, reply });\n reply.code(204).send(null);\n } catch (e) {\n reply.code(400).send((e as Error).message);\n }\n }\n );\n\n const shouldMountConnectRoutes = options.mountConnectRoutes ?? false;\n\n if (shouldMountConnectRoutes) {\n fastify.get(\n '/auth/connect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; connectionScope: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, connectionScope, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = '/auth/connect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startLinkUser(\n {\n connection: connection,\n connectionScope: connectionScope,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get('/auth/connect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeLinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(\n '/auth/unconnect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = '/auth/unconnect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startUnlinkUser(\n {\n connection: connection,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get('/auth/unconnect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeUnlinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n }\n }\n\n fastify.decorate('auth0Client', auth0Client);\n});\n","import { CookieSerializeOptions } from '@fastify/cookie';\nimport { TransactionData, AbstractTransactionStore } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport { StoreOptions } from '../types.js';\n\nexport class CookieTransactionStore extends AbstractTransactionStore<StoreOptions> {\n async set(\n identifier: string,\n transactionData: TransactionData,\n removeIfExists?: boolean,\n options?: StoreOptions\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const maxAge = 60 * 60;\n const cookieOpts: CookieSerializeOptions = { httpOnly: true, sameSite: 'lax', path: '/', maxAge };\n const expiration = Math.floor(Date.now() / 1000 + maxAge);\n const encryptedStateData = await this.encrypt(identifier, transactionData, expiration);\n \n options.reply.setCookie(identifier, encryptedStateData, cookieOpts);\n }\n\n async get(identifier: string, options?: StoreOptions): Promise<TransactionData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieValue = options.request.cookies[identifier];\n\n if (cookieValue) {\n return await this.decrypt(identifier, cookieValue);\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n options?.reply.clearCookie(identifier);\n }\n}\n","export class MissingStoreOptionsError extends Error {\n public code: string = 'missing_store_options_error';\n\n constructor(message?: string) {\n super(message ?? 'The store options are missing, making it impossible to interact with the store.');\n this.name = 'MissingStoreOptionsError';\n }\n}\n","import type { CookieSerializeOptions } from '@fastify/cookie';\nimport { BackchannelLogoutError, EncryptedStoreOptions, StateData } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport type { SessionConfiguration, SessionCookieOptions, StoreOptions } from '../types.js';\nimport { AbstractSessionStore } from './abstract-session-store.js';\n\nexport class StatelessStateStore extends AbstractSessionStore {\n readonly #cookieOptions: SessionCookieOptions | undefined;\n\n constructor(options: SessionConfiguration & EncryptedStoreOptions) {\n super(options);\n\n this.#cookieOptions = options.cookie;\n }\n\n async set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const maxAge = this.calculateMaxAge(stateData.internal.createdAt);\n const cookieOpts: CookieSerializeOptions = {\n httpOnly: true,\n sameSite: this.#cookieOptions?.sameSite ?? 'lax',\n path: '/',\n secure: this.#cookieOptions?.secure ?? 'auto',\n maxAge,\n };\n const expiration = Math.floor(Date.now() / 1000 + maxAge);\n const encryptedStateData = await this.encrypt(identifier, stateData, expiration);\n\n const chunkSize = 3072;\n const chunkCount = Math.ceil(encryptedStateData.length / chunkSize);\n const chunks = [...Array(chunkCount).keys()].map((i) => ({\n value: encryptedStateData.substring(i * chunkSize, (i + 1) * chunkSize),\n name: `${identifier}.${i}`,\n }));\n\n chunks.forEach((chunk) => {\n options.reply.setCookie(chunk.name, chunk.value, cookieOpts);\n });\n\n const existingCookieKeys = this.getCookieKeys(identifier, options);\n const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key));\n cookieKeysToRemove.forEach((key) => {\n options.reply.clearCookie(key);\n });\n }\n\n async get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieKeys = this.getCookieKeys(identifier, options);\n const encryptedStateData = cookieKeys\n .map((key) => ({ index: parseInt(key.split('.')[1] as string, 10), value: options.request.cookies[key] }))\n .sort((a, b) => a.index - b.index)\n .map((item) => item.value)\n .join('');\n\n if (encryptedStateData) {\n return (await this.decrypt(identifier, encryptedStateData)) as StateData;\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieKeys = this.getCookieKeys(identifier, options);\n for (const key of cookieKeys) {\n options?.reply.clearCookie(key);\n }\n }\n\n deleteByLogoutToken(): Promise<void> {\n throw new BackchannelLogoutError(\n 'Backchannel logout is not available when using Stateless Storage. Use Stateful Storage by providing a `sessionStore`'\n );\n }\n\n private getCookieKeys(identifier: string, options: StoreOptions): string[] {\n return Object.keys(options.request.cookies).filter((key) => key.startsWith(identifier));\n }\n}\n","import { AbstractStateStore, EncryptedStoreOptions, LogoutTokenClaims, StateData } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, StoreOptions } from '../types.js';\n\nexport abstract class AbstractSessionStore extends AbstractStateStore<StoreOptions> {\n readonly #rolling: boolean;\n readonly #absoluteDuration: number;\n readonly #inactivityDuration: number;\n\n constructor(options: SessionConfiguration & EncryptedStoreOptions) {\n super(options);\n\n this.#rolling = options.rolling ?? true;\n this.#absoluteDuration = options.absoluteDuration ?? 60 * 60 * 24 * 3;\n this.#inactivityDuration = options.inactivityDuration ?? 60 * 60 * 24 * 1;\n }\n\n abstract set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void>;\n\n abstract get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined>;\n\n abstract delete(identifier: string, options?: StoreOptions | undefined): Promise<void>;\n\n abstract deleteByLogoutToken(claims: LogoutTokenClaims, options?: StoreOptions | undefined): Promise<void>;\n\n /**\n * calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations.\n */\n protected calculateMaxAge(createdAt: number) {\n if (!this.#rolling) {\n return this.#absoluteDuration;\n }\n\n const now = (Date.now() / 1000) | 0;\n const expiresAt = Math.min(now + this.#inactivityDuration, createdAt + this.#absoluteDuration);\n const maxAge = expiresAt - now;\n\n return maxAge > 0 ? maxAge : 0;\n }\n}\n","import type { CookieSerializeOptions } from '@fastify/cookie';\nimport { EncryptedStoreOptions, LogoutTokenClaims, StateData } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport type { SessionConfiguration, SessionCookieOptions, SessionStore, StoreOptions } from '../types.js';\nimport { AbstractSessionStore } from './abstract-session-store.js';\n\nexport interface StatefulStateStoreOptions extends EncryptedStoreOptions {\n store: SessionStore;\n}\n\nconst generateId = () => {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n};\n\nexport class StatefulStateStore extends AbstractSessionStore {\n readonly #store: SessionStore;\n readonly #cookieOptions: SessionCookieOptions | undefined;\n\n constructor(options: StatefulStateStoreOptions & SessionConfiguration) {\n super(options);\n\n this.#store = options.store;\n this.#cookieOptions = options.cookie;\n }\n\n async set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n let sessionId = await this.getSessionId(identifier, options);\n\n // if this is a new session created by a new login we need to remove the old session\n // from the store and regenerate the session ID to prevent session fixation.\n if (sessionId && removeIfExists) {\n await this.#store.delete(sessionId);\n sessionId = generateId();\n }\n\n if (!sessionId) {\n sessionId = generateId();\n }\n\n const maxAge = this.calculateMaxAge(stateData.internal.createdAt);\n const cookieOpts: CookieSerializeOptions = {\n httpOnly: true,\n sameSite: this.#cookieOptions?.sameSite ?? 'lax',\n path: '/',\n secure: this.#cookieOptions?.secure ?? 'auto',\n maxAge,\n };\n const expiration = Date.now() / 1000 + maxAge;\n const encryptedStateData = await this.encrypt<{ id: string }>(\n identifier,\n {\n id: sessionId,\n },\n expiration\n );\n\n await this.#store.set(sessionId, stateData);\n\n options.reply.setCookie(identifier, encryptedStateData, cookieOpts);\n }\n\n async get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const sessionId = await this.getSessionId(identifier, options);\n\n if (sessionId) {\n const stateData = await this.#store.get(sessionId);\n\n // If we have a session cookie, but no `stateData`, we should remove the cookie.\n if (!stateData) {\n options?.reply.clearCookie(identifier);\n }\n\n return stateData;\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const sessionId = await this.getSessionId(identifier, options);\n\n if (sessionId) {\n await this.#store.delete(sessionId);\n }\n\n options?.reply.clearCookie(identifier);\n }\n\n private async getSessionId(identifier: string, options: StoreOptions) {\n const cookieValue = options.request.cookies[identifier];\n if (cookieValue) {\n const sessionCookie = await this.decrypt<{ id: string }>(identifier, cookieValue);\n return sessionCookie.id;\n }\n }\n\n deleteByLogoutToken(claims: LogoutTokenClaims, options?: StoreOptions | undefined): Promise<void> {\n return this.#store.deleteByLogoutToken(claims, options);\n }\n}\n","/**\n * Ensures the value has a trailing slash.\n * If it does not, it will append one.\n * @param value The value to ensure has a trailing slash.\n * @returns The value with a trailing slash.\n */\nfunction ensureTrailingSlash(value: string) {\n return value && !value.endsWith('/') ? `${value}/` : value;\n}\n\n/**\n * Ensures the value does not have a leading slash.\n * If it does, it will trim it.\n * @param value The value to ensure has no leading slash.\n * @returns The value without a leading slash.\n */\nfunction ensureNoLeadingSlash(value: string) {\n return value && value.startsWith('/') ? value.substring(1, value.length) : value;\n}\n\n/**\n * Utility function to ensure Route URLs are created correctly when using both the root and subpath as base URL.\n * @param url The URL to use.\n * @param base The base URL to use.\n * @returns A URL object, combining the base and url.\n */\nexport function createRouteUrl(url: string, base: string) {\n return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base));\n}\n\n/**\n * Function to ensure a redirect URL is safe to use, as in, it has the same origin as the safeBaseUrl.\n * @param dangerousRedirect The redirect URL to check.\n * @param safeBaseUrl The base URL to check against.\n * @returns A safe redirect URL or undefined if the redirect URL is not safe.\n */\nexport function toSafeRedirect(dangerousRedirect: string, safeBaseUrl: string): string | undefined {\n let url: URL;\n\n try {\n url = createRouteUrl(dangerousRedirect, safeBaseUrl);\n } catch {\n return undefined;\n }\n\n if (url.origin === new URL(safeBaseUrl).origin) {\n return url.toString();\n }\n\n return undefined;\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,4BAAe;AACf,IAAAA,0BAA6B;;;ACD7B,6BAA0D;;;ACDnD,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAC3C,OAAe;AAAA,EAEtB,YAAY,SAAkB;AAC5B,UAAM,WAAW,iFAAiF;AAClG,SAAK,OAAO;AAAA,EACd;AACF;;;ADFO,IAAM,yBAAN,cAAqC,gDAAuC;AAAA,EACjF,MAAM,IACJ,YACA,iBACA,gBACA,SACe;AAEf,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,SAAS,KAAK;AACpB,UAAM,aAAqC,EAAE,UAAU,MAAM,UAAU,OAAO,MAAM,KAAK,OAAO;AAChG,UAAM,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,MAAO,MAAM;AACxD,UAAM,qBAAqB,MAAM,KAAK,QAAQ,YAAY,iBAAiB,UAAU;AAErF,YAAQ,MAAM,UAAU,YAAY,oBAAoB,UAAU;AAAA,EACpE;AAAA,EAEA,MAAM,IAAI,YAAoB,SAA8D;AAE1F,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,cAAc,QAAQ,QAAQ,QAAQ,UAAU;AAEtD,QAAI,aAAa;AACf,aAAO,MAAM,KAAK,QAAQ,YAAY,WAAW;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,YAAoB,SAAmD;AAElF,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,aAAS,MAAM,YAAY,UAAU;AAAA,EACvC;AACF;;;AE7CA,IAAAC,0BAAyE;;;ACDzE,IAAAC,0BAAwF;AAGjF,IAAe,uBAAf,cAA4C,2CAAiC;AAAA,EACzE;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,SAAuD;AACjE,UAAM,OAAO;AAEb,SAAK,WAAW,QAAQ,WAAW;AACnC,SAAK,oBAAoB,QAAQ,oBAAoB,KAAK,KAAK,KAAK;AACpE,SAAK,sBAAsB,QAAQ,sBAAsB,KAAK,KAAK,KAAK;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAkBU,gBAAgB,WAAmB;AAC3C,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,MAAO,KAAK,IAAI,IAAI,MAAQ;AAClC,UAAM,YAAY,KAAK,IAAI,MAAM,KAAK,qBAAqB,YAAY,KAAK,iBAAiB;AAC7F,UAAM,SAAS,YAAY;AAE3B,WAAO,SAAS,IAAI,SAAS;AAAA,EAC/B;AACF;;;ADrCO,IAAM,sBAAN,cAAkC,qBAAqB;AAAA,EACnD;AAAA,EAET,YAAY,SAAuD;AACjE,UAAM,OAAO;AAEb,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,IACJ,YACA,WACA,gBACA,SACe;AAEf,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,SAAS,KAAK,gBAAgB,UAAU,SAAS,SAAS;AAChE,UAAM,aAAqC;AAAA,MACzC,UAAU;AAAA,MACV,UAAU,KAAK,gBAAgB,YAAY;AAAA,MAC3C,MAAM;AAAA,MACN,QAAQ,KAAK,gBAAgB,UAAU;AAAA,MACvC;AAAA,IACF;AACA,UAAM,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,MAAO,MAAM;AACxD,UAAM,qBAAqB,MAAM,KAAK,QAAQ,YAAY,WAAW,UAAU;AAE/E,UAAM,YAAY;AAClB,UAAM,aAAa,KAAK,KAAK,mBAAmB,SAAS,SAAS;AAClE,UAAM,SAAS,CAAC,GAAG,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,MACvD,OAAO,mBAAmB,UAAU,IAAI,YAAY,IAAI,KAAK,SAAS;AAAA,MACtE,MAAM,GAAG,UAAU,IAAI,CAAC;AAAA,IAC1B,EAAE;AAEF,WAAO,QAAQ,CAAC,UAAU;AACxB,cAAQ,MAAM,UAAU,MAAM,MAAM,MAAM,OAAO,UAAU;AAAA,IAC7D,CAAC;AAED,UAAM,qBAAqB,KAAK,cAAc,YAAY,OAAO;AACjE,UAAM,qBAAqB,mBAAmB,OAAO,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,GAAG,CAAC;AACzG,uBAAmB,QAAQ,CAAC,QAAQ;AAClC,cAAQ,MAAM,YAAY,GAAG;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,YAAoB,SAAoE;AAEhG,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,aAAa,KAAK,cAAc,YAAY,OAAO;AACzD,UAAM,qBAAqB,WACxB,IAAI,CAAC,SAAS,EAAE,OAAO,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,GAAa,EAAE,GAAG,OAAO,QAAQ,QAAQ,QAAQ,GAAG,EAAE,EAAE,EACxG,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,IAAI,CAAC,SAAS,KAAK,KAAK,EACxB,KAAK,EAAE;AAEV,QAAI,oBAAoB;AACtB,aAAQ,MAAM,KAAK,QAAQ,YAAY,kBAAkB;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,YAAoB,SAAmD;AAElF,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,aAAa,KAAK,cAAc,YAAY,OAAO;AACzD,eAAW,OAAO,YAAY;AAC5B,eAAS,MAAM,YAAY,GAAG;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,sBAAqC;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,YAAoB,SAAiC;AACzE,WAAO,OAAO,KAAK,QAAQ,QAAQ,OAAO,EAAE,OAAO,CAAC,QAAQ,IAAI,WAAW,UAAU,CAAC;AAAA,EACxF;AACF;;;AEpFA,IAAM,aAAa,MAAM;AACvB,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,MAAM,KAAK,KAAK,EACpB,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AACZ;AAEO,IAAM,qBAAN,cAAiC,qBAAqB;AAAA,EAClD;AAAA,EACA;AAAA,EAET,YAAY,SAA2D;AACrE,UAAM,OAAO;AAEb,SAAK,SAAS,QAAQ;AACtB,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,IACJ,YACA,WACA,gBACA,SACe;AAEf,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,QAAI,YAAY,MAAM,KAAK,aAAa,YAAY,OAAO;AAI3D,QAAI,aAAa,gBAAgB;AAC/B,YAAM,KAAK,OAAO,OAAO,SAAS;AAClC,kBAAY,WAAW;AAAA,IACzB;AAEA,QAAI,CAAC,WAAW;AACd,kBAAY,WAAW;AAAA,IACzB;AAEA,UAAM,SAAS,KAAK,gBAAgB,UAAU,SAAS,SAAS;AAChE,UAAM,aAAqC;AAAA,MACzC,UAAU;AAAA,MACV,UAAU,KAAK,gBAAgB,YAAY;AAAA,MAC3C,MAAM;AAAA,MACN,QAAQ,KAAK,gBAAgB,UAAU;AAAA,MACvC;AAAA,IACF;AACA,UAAM,aAAa,KAAK,IAAI,IAAI,MAAO;AACvC,UAAM,qBAAqB,MAAM,KAAK;AAAA,MACpC;AAAA,MACA;AAAA,QACE,IAAI;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAK,OAAO,IAAI,WAAW,SAAS;AAE1C,YAAQ,MAAM,UAAU,YAAY,oBAAoB,UAAU;AAAA,EACpE;AAAA,EAEA,MAAM,IAAI,YAAoB,SAAoE;AAEhG,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,YAAY,MAAM,KAAK,aAAa,YAAY,OAAO;AAE7D,QAAI,WAAW;AACb,YAAM,YAAY,MAAM,KAAK,OAAO,IAAI,SAAS;AAGjD,UAAI,CAAC,WAAW;AACd,iBAAS,MAAM,YAAY,UAAU;AAAA,MACvC;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,YAAoB,SAAmD;AAElF,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,UAAM,YAAY,MAAM,KAAK,aAAa,YAAY,OAAO;AAE7D,QAAI,WAAW;AACb,YAAM,KAAK,OAAO,OAAO,SAAS;AAAA,IACpC;AAEA,aAAS,MAAM,YAAY,UAAU;AAAA,EACvC;AAAA,EAEA,MAAc,aAAa,YAAoB,SAAuB;AACpE,UAAM,cAAc,QAAQ,QAAQ,QAAQ,UAAU;AACtD,QAAI,aAAa;AACf,YAAM,gBAAgB,MAAM,KAAK,QAAwB,YAAY,WAAW;AAChF,aAAO,cAAc;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,oBAAoB,QAA2B,SAAmD;AAChG,WAAO,KAAK,OAAO,oBAAoB,QAAQ,OAAO;AAAA,EACxD;AACF;;;ACnHA,SAAS,oBAAoB,OAAe;AAC1C,SAAO,SAAS,CAAC,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,MAAM;AACvD;AAQA,SAAS,qBAAqB,OAAe;AAC3C,SAAO,SAAS,MAAM,WAAW,GAAG,IAAI,MAAM,UAAU,GAAG,MAAM,MAAM,IAAI;AAC7E;AAQO,SAAS,eAAe,KAAa,MAAc;AACxD,SAAO,IAAI,IAAI,qBAAqB,GAAG,GAAG,oBAAoB,IAAI,CAAC;AACrE;AAQO,SAAS,eAAe,mBAA2B,aAAyC;AACjG,MAAI;AAEJ,MAAI;AACF,UAAM,eAAe,mBAAmB,WAAW;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,IAAI,WAAW,IAAI,IAAI,WAAW,EAAE,QAAQ;AAC9C,WAAO,IAAI,SAAS;AAAA,EACtB;AAEA,SAAO;AACT;;;ANFA,IAAO,oBAAQ,sBAAAC,SAAG,eAAe,aAAa,SAA0B,SAA8B;AACpG,QAAM,eAAe;AACrB,QAAM,cAAc,eAAe,cAAc,QAAQ,UAAU;AAEnE,QAAM,cAAc,IAAI,qCAA2B;AAAA,IACjD,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,cAAc,QAAQ;AAAA,IACtB,2BAA2B,QAAQ;AAAA,IACnC,2BAA2B,QAAQ;AAAA,IACnC,qBAAqB;AAAA,MACnB,UAAU,QAAQ;AAAA,MAClB,cAAc,YAAY,SAAS;AAAA,IACrC;AAAA,IACA,kBAAkB,IAAI,uBAAuB,EAAE,QAAQ,QAAQ,cAAc,CAAC;AAAA,IAC9E,YAAY,QAAQ,eAChB,IAAI,mBAAmB;AAAA,MACrB,GAAG,QAAQ;AAAA,MACX,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,IACjB,CAAC,IACD,IAAI,oBAAoB;AAAA,MACtB,GAAG,QAAQ;AAAA,MACX,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAAA,IACL,iBAAiB,QAAQ,sBAAsB,QAAQ;AAAA,IACvD,aAAa,QAAQ;AAAA,EACvB,CAAC;AAED,MAAI,CAAC,QAAQ,kBAAkB,QAAQ,GAAG;AACxC,YAAQ,SAAS,OAAO,iBAAiB,CAAC;AAAA,EAC5C;AAEA,QAAM,oBAAoB,QAAQ,eAAe;AAEjD,MAAI,mBAAmB;AACrB,YAAQ;AAAA,MACN;AAAA,MACA,OACE,SAGA,UACG;AACH,cAAM,oBAAoB,QAAQ,MAAM;AACxC,cAAM,oBAAoB,eAAe,qBAAqB,KAAK,QAAQ,UAAU;AAErF,cAAM,mBAAmB,MAAM,YAAY;AAAA,UACzC;AAAA,YACE,6BAA6B,QAAQ;AAAA,YACrC,UAAU,EAAE,UAAU,kBAAkB;AAAA,UAC1C;AAAA,UACA,EAAE,SAAS,MAAM;AAAA,QACnB;AAEA,cAAM,SAAS,iBAAiB,IAAI;AAAA,MACtC;AAAA,IACF;AAEA,YAAQ,IAAI,kBAAkB,OAAO,SAAS,UAAU;AACtD,YAAM,EAAE,SAAS,IAAI,MAAM,YAAY;AAAA,QACrC,eAAe,QAAQ,KAAK,QAAQ,UAAU;AAAA,QAC9C,EAAE,SAAS,MAAM;AAAA,MACnB;AAEA,YAAM,SAAS,UAAU,YAAY,QAAQ,UAAU;AAAA,IACzD,CAAC;AAED,YAAQ,IAAI,gBAAgB,OAAO,SAAS,UAAU;AACpD,YAAM,WAAW,QAAQ;AACzB,YAAM,YAAY,MAAM,YAAY,OAAO,EAAE,UAAU,SAAS,SAAS,EAAE,GAAG,EAAE,SAAS,MAAM,CAAC;AAEhG,YAAM,SAAS,UAAU,IAAI;AAAA,IAC/B,CAAC;AAED,YAAQ;AAAA,MACN;AAAA,MACA,OACE,SAGA,UACG;AACH,cAAM,cAAc,QAAQ,KAAK;AAEjC,YAAI,CAAC,aAAa;AAChB,gBAAM,KAAK,GAAG,EAAE,KAAK,6CAA6C;AAElE;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,YAAY,wBAAwB,aAAa,EAAE,SAAS,MAAM,CAAC;AACzE,gBAAM,KAAK,GAAG,EAAE,KAAK,IAAI;AAAA,QAC3B,SAAS,GAAG;AACV,gBAAM,KAAK,GAAG,EAAE,KAAM,EAAY,OAAO;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAEA,UAAM,2BAA2B,QAAQ,sBAAsB;AAE/D,QAAI,0BAA0B;AAC5B,cAAQ;AAAA,QACN;AAAA,QACA,OACE,SAGA,UACG;AACH,gBAAM,EAAE,YAAY,iBAAiB,SAAS,IAAI,QAAQ;AAC1D,gBAAM,oBAAoB;AAE1B,cAAI,CAAC,YAAY;AACf,mBAAO,MAAM,KAAK,GAAG,EAAE,KAAK;AAAA,cAC1B,OAAO;AAAA,cACP,mBAAmB;AAAA,YACrB,CAAC;AAAA,UACH;AAEA,gBAAM,oBAAoB,eAAe,qBAAqB,KAAK,QAAQ,UAAU;AACrF,gBAAMC,gBAAe;AACrB,gBAAMC,eAAc,eAAeD,eAAc,QAAQ,UAAU;AACnE,gBAAM,cAAc,MAAM,QAAQ,YAAa;AAAA,YAC7C;AAAA,cACE;AAAA,cACA;AAAA,cACA,qBAAqB;AAAA,gBACnB,cAAcC,aAAY,SAAS;AAAA,cACrC;AAAA,cACA,UAAU;AAAA,gBACR,UAAU;AAAA,cACZ;AAAA,YACF;AAAA,YACA,EAAE,SAAS,MAAM;AAAA,UACnB;AAEA,gBAAM,SAAS,YAAY,IAAI;AAAA,QACjC;AAAA,MACF;AAEA,cAAQ,IAAI,0BAA0B,OAAO,SAAS,UAAU;AAC9D,cAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,YAAa;AAAA,UAC9C,eAAe,QAAQ,KAAK,QAAQ,UAAU;AAAA,UAC9C;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAAS,UAAU,YAAY,QAAQ,UAAU;AAAA,MACzD,CAAC;AAED,cAAQ;AAAA,QACN;AAAA,QACA,OACE,SAGA,UACG;AACH,gBAAM,EAAE,YAAY,SAAS,IAAI,QAAQ;AACzC,gBAAM,oBAAoB;AAE1B,cAAI,CAAC,YAAY;AACf,mBAAO,MAAM,KAAK,GAAG,EAAE,KAAK;AAAA,cAC1B,OAAO;AAAA,cACP,mBAAmB;AAAA,YACrB,CAAC;AAAA,UACH;AAEA,gBAAM,oBAAoB,eAAe,qBAAqB,KAAK,QAAQ,UAAU;AACrF,gBAAMD,gBAAe;AACrB,gBAAMC,eAAc,eAAeD,eAAc,QAAQ,UAAU;AACnE,gBAAM,cAAc,MAAM,QAAQ,YAAa;AAAA,YAC7C;AAAA,cACE;AAAA,cACA,qBAAqB;AAAA,gBACnB,cAAcC,aAAY,SAAS;AAAA,cACrC;AAAA,cACA,UAAU;AAAA,gBACR,UAAU;AAAA,cACZ;AAAA,YACF;AAAA,YACA,EAAE,SAAS,MAAM;AAAA,UACnB;AAEA,gBAAM,SAAS,YAAY,IAAI;AAAA,QACjC;AAAA,MACF;AAEA,cAAQ,IAAI,4BAA4B,OAAO,SAAS,UAAU;AAChE,cAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,YAAa;AAAA,UAC9C,eAAe,QAAQ,KAAK,QAAQ,UAAU;AAAA,UAC9C;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAAS,UAAU,YAAY,QAAQ,UAAU;AAAA,MACzD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,UAAQ,SAAS,eAAe,WAAW;AAC7C,CAAC;","names":["import_auth0_server_js","import_auth0_server_js","import_auth0_server_js","fp","callbackPath","redirectUri"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/errors/index.ts","../src/store/fastify-cookie-handler.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { CookieTransactionStore, ServerClient, StatelessStateStore, StatefulStateStore } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, SessionStore, StoreOptions } from './types.js';\nimport { createRouteUrl, toSafeRedirect } from './utils.js';\nimport { FastifyCookieHandler } from './store/fastify-cookie-handler.js';\n\nexport * from './types.js';\nexport { CookieTransactionStore } from '@auth0/auth0-server-js';\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n auth0Client: ServerClient<StoreOptions> | undefined;\n }\n}\n\nexport interface Auth0FastifyOptions {\n domain: string;\n clientId: string;\n clientSecret?: string;\n clientAssertionSigningKey?: string | CryptoKey;\n clientAssertionSigningAlg?: string;\n audience?: string;\n appBaseUrl: string;\n\n pushedAuthorizationRequests?: boolean;\n\n sessionSecret: string;\n sessionStore?: SessionStore;\n sessionConfiguration?: SessionConfiguration;\n /**\n * Whether to mount the default routes for login, logout, callback and profile.\n * Defaults to true.\n */\n mountRoutes?: boolean;\n /**\n * Whether to mount the routes for account linking and unlinking.\n * Defaults to false.\n */\n mountConnectRoutes?: boolean;\n /**\n * Optional, custom Fetch implementation to use.\n */\n customFetch?: typeof fetch;\n\n routes?: {\n login?: string;\n callback?: string;\n logout?: string;\n backchannelLogout?: string;\n connect?: string;\n connectCallback?: string;\n unconnect?: string;\n unconnectCallback?: string;\n }\n}\n\nexport default fp(async function auth0Fastify(fastify: FastifyInstance, options: Auth0FastifyOptions) {\n const callbackPath = options.routes?.callback ?? '/auth/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n\n const auth0Client = new ServerClient<StoreOptions>({\n domain: options.domain,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n clientAssertionSigningKey: options.clientAssertionSigningKey,\n clientAssertionSigningAlg: options.clientAssertionSigningAlg,\n authorizationParams: {\n audience: options.audience,\n redirect_uri: redirectUri.toString(),\n },\n transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }, new FastifyCookieHandler()),\n stateStore: options.sessionStore\n ? new StatefulStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n store: options.sessionStore,\n }, new FastifyCookieHandler())\n : new StatelessStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n }, new FastifyCookieHandler()),\n stateIdentifier: options.sessionConfiguration?.cookie?.name,\n customFetch: options.customFetch,\n });\n\n if (!fastify.hasReplyDecorator('cookie')) {\n fastify.register(import('@fastify/cookie'));\n }\n\n const shouldMountRoutes = options.mountRoutes ?? true;\n\n if (shouldMountRoutes) {\n fastify.get(\n options.routes?.login ?? '/auth/login',\n async (\n request: FastifyRequest<{\n Querystring: { returnTo?: string };\n }>,\n reply\n ) => {\n const dangerousReturnTo = request.query.returnTo;\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n\n const authorizationUrl = await auth0Client.startInteractiveLogin(\n {\n pushedAuthorizationRequests: options.pushedAuthorizationRequests,\n appState: { returnTo: sanitizedReturnTo },\n },\n { request, reply }\n );\n\n reply.redirect(authorizationUrl.href);\n }\n );\n\n fastify.get(options.routes?.callback ?? '/auth/callback', async (request, reply) => {\n const { appState } = await auth0Client.completeInteractiveLogin<{ returnTo: string } | undefined>(\n createRouteUrl(request.url, options.appBaseUrl),\n { request, reply }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(options.routes?.logout ?? '/auth/logout', async (request, reply) => {\n const returnTo = options.appBaseUrl;\n const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply });\n\n reply.redirect(logoutUrl.href);\n });\n\n fastify.post(\n options.routes?.backchannelLogout ?? '/auth/backchannel-logout',\n async (\n request: FastifyRequest<{\n Body: { logout_token?: string };\n }>,\n reply\n ) => {\n const logoutToken = request.body.logout_token;\n\n if (!logoutToken) {\n reply.code(400).send('Missing `logout_token` in the request body.');\n\n return;\n }\n\n try {\n await auth0Client.handleBackchannelLogout(logoutToken, { request, reply });\n reply.code(204).send(null);\n } catch (e) {\n reply.code(400).send((e as Error).message);\n }\n }\n );\n\n const shouldMountConnectRoutes = options.mountConnectRoutes ?? false;\n\n if (shouldMountConnectRoutes) {\n fastify.get(\n options.routes?.connect ?? '/auth/connect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; connectionScope: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, connectionScope, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = options.routes?.connectCallback ?? '/auth/connect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startLinkUser(\n {\n connection: connection,\n connectionScope: connectionScope,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get(options.routes?.connectCallback ?? '/auth/connect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeLinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(\n options.routes?.unconnect ?? '/auth/unconnect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = options.routes?.unconnectCallback ?? '/auth/unconnect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startUnlinkUser(\n {\n connection: connection,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get(options.routes?.unconnectCallback ?? '/auth/unconnect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeUnlinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n }\n }\n\n fastify.decorate('auth0Client', auth0Client);\n});\n","/**\n * Ensures the value has a trailing slash.\n * If it does not, it will append one.\n * @param value The value to ensure has a trailing slash.\n * @returns The value with a trailing slash.\n */\nfunction ensureTrailingSlash(value: string) {\n return value && !value.endsWith('/') ? `${value}/` : value;\n}\n\n/**\n * Ensures the value does not have a leading slash.\n * If it does, it will trim it.\n * @param value The value to ensure has no leading slash.\n * @returns The value without a leading slash.\n */\nfunction ensureNoLeadingSlash(value: string) {\n return value && value.startsWith('/') ? value.substring(1, value.length) : value;\n}\n\n/**\n * Utility function to ensure Route URLs are created correctly when using both the root and subpath as base URL.\n * @param url The URL to use.\n * @param base The base URL to use.\n * @returns A URL object, combining the base and url.\n */\nexport function createRouteUrl(url: string, base: string) {\n return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base));\n}\n\n/**\n * Function to ensure a redirect URL is safe to use, as in, it has the same origin as the safeBaseUrl.\n * @param dangerousRedirect The redirect URL to check.\n * @param safeBaseUrl The base URL to check against.\n * @returns A safe redirect URL or undefined if the redirect URL is not safe.\n */\nexport function toSafeRedirect(dangerousRedirect: string, safeBaseUrl: string): string | undefined {\n let url: URL;\n\n try {\n url = createRouteUrl(dangerousRedirect, safeBaseUrl);\n } catch {\n return undefined;\n }\n\n if (url.origin === new URL(safeBaseUrl).origin) {\n return url.toString();\n }\n\n return undefined;\n}","export class MissingStoreOptionsError extends Error {\n public code: string = 'missing_store_options_error';\n\n constructor(message?: string) {\n super(message ?? 'The store options are missing, making it impossible to interact with the store.');\n this.name = 'MissingStoreOptionsError';\n }\n}\n","import { CookieHandler, CookieSerializeOptions } from '@auth0/auth0-server-js';\nimport { StoreOptions } from '../types.js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\n\nexport class FastifyCookieHandler implements CookieHandler<StoreOptions> {\n setCookie(\n name: string,\n value: string,\n options?: CookieSerializeOptions,\n storeOptions?: StoreOptions,\n ): void {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n storeOptions.reply.setCookie(name, value, options || {});\n }\n\n getCookie(name: string, storeOptions?: StoreOptions): string | undefined {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n return storeOptions.request.cookies?.[name];\n }\n\n getCookies(storeOptions?: StoreOptions): Record<string, string> {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n return storeOptions.request.cookies as Record<string, string>;\n }\n\n deleteCookie(name: string, storeOptions?: StoreOptions): void {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n storeOptions.reply.clearCookie(name);\n }\n}\n"],"mappings":"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","names":["import_auth0_server_js","fp","callbackPath","redirectUri"]} |
+13
-8
| import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'; | ||
| import { StateData, LogoutTokenClaims, AbstractTransactionStore, TransactionData, ServerClient } from '@auth0/auth0-server-js'; | ||
| import { StateData, LogoutTokenClaims, ServerClient } from '@auth0/auth0-server-js'; | ||
| export { CookieTransactionStore } from '@auth0/auth0-server-js'; | ||
@@ -66,8 +67,2 @@ interface StoreOptions { | ||
| declare class CookieTransactionStore extends AbstractTransactionStore<StoreOptions> { | ||
| set(identifier: string, transactionData: TransactionData, removeIfExists?: boolean, options?: StoreOptions): Promise<void>; | ||
| get(identifier: string, options?: StoreOptions): Promise<TransactionData | undefined>; | ||
| delete(identifier: string, options?: StoreOptions | undefined): Promise<void>; | ||
| } | ||
| declare module 'fastify' { | ||
@@ -104,5 +99,15 @@ interface FastifyInstance { | ||
| customFetch?: typeof fetch; | ||
| routes?: { | ||
| login?: string; | ||
| callback?: string; | ||
| logout?: string; | ||
| backchannelLogout?: string; | ||
| connect?: string; | ||
| connectCallback?: string; | ||
| unconnect?: string; | ||
| unconnectCallback?: string; | ||
| }; | ||
| } | ||
| declare const _default: (fastify: FastifyInstance, options: Auth0FastifyOptions) => Promise<void>; | ||
| export { type Auth0FastifyOptions, CookieTransactionStore, type SessionConfiguration, type SessionCookieOptions, type SessionStore, type StoreOptions, _default as default }; | ||
| export { type Auth0FastifyOptions, type SessionConfiguration, type SessionCookieOptions, type SessionStore, type StoreOptions, _default as default }; |
+13
-8
| import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'; | ||
| import { StateData, LogoutTokenClaims, AbstractTransactionStore, TransactionData, ServerClient } from '@auth0/auth0-server-js'; | ||
| import { StateData, LogoutTokenClaims, ServerClient } from '@auth0/auth0-server-js'; | ||
| export { CookieTransactionStore } from '@auth0/auth0-server-js'; | ||
@@ -66,8 +67,2 @@ interface StoreOptions { | ||
| declare class CookieTransactionStore extends AbstractTransactionStore<StoreOptions> { | ||
| set(identifier: string, transactionData: TransactionData, removeIfExists?: boolean, options?: StoreOptions): Promise<void>; | ||
| get(identifier: string, options?: StoreOptions): Promise<TransactionData | undefined>; | ||
| delete(identifier: string, options?: StoreOptions | undefined): Promise<void>; | ||
| } | ||
| declare module 'fastify' { | ||
@@ -104,5 +99,15 @@ interface FastifyInstance { | ||
| customFetch?: typeof fetch; | ||
| routes?: { | ||
| login?: string; | ||
| callback?: string; | ||
| logout?: string; | ||
| backchannelLogout?: string; | ||
| connect?: string; | ||
| connectCallback?: string; | ||
| unconnect?: string; | ||
| unconnectCallback?: string; | ||
| }; | ||
| } | ||
| declare const _default: (fastify: FastifyInstance, options: Auth0FastifyOptions) => Promise<void>; | ||
| export { type Auth0FastifyOptions, CookieTransactionStore, type SessionConfiguration, type SessionCookieOptions, type SessionStore, type StoreOptions, _default as default }; | ||
| export { type Auth0FastifyOptions, type SessionConfiguration, type SessionCookieOptions, type SessionStore, type StoreOptions, _default as default }; |
+53
-230
| // src/index.ts | ||
| import fp from "fastify-plugin"; | ||
| import { ServerClient } from "@auth0/auth0-server-js"; | ||
| import { CookieTransactionStore, ServerClient, StatelessStateStore, StatefulStateStore } from "@auth0/auth0-server-js"; | ||
| // src/store/cookie-transaction-store.ts | ||
| import { AbstractTransactionStore } from "@auth0/auth0-server-js"; | ||
| // src/utils.ts | ||
| function ensureTrailingSlash(value) { | ||
| return value && !value.endsWith("/") ? `${value}/` : value; | ||
| } | ||
| function ensureNoLeadingSlash(value) { | ||
| return value && value.startsWith("/") ? value.substring(1, value.length) : value; | ||
| } | ||
| function createRouteUrl(url, base) { | ||
| return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base)); | ||
| } | ||
| function toSafeRedirect(dangerousRedirect, safeBaseUrl) { | ||
| let url; | ||
| try { | ||
| url = createRouteUrl(dangerousRedirect, safeBaseUrl); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| if (url.origin === new URL(safeBaseUrl).origin) { | ||
| return url.toString(); | ||
| } | ||
| return void 0; | ||
| } | ||
@@ -17,231 +37,34 @@ // src/errors/index.ts | ||
| // src/store/cookie-transaction-store.ts | ||
| var CookieTransactionStore = class extends AbstractTransactionStore { | ||
| async set(identifier, transactionData, removeIfExists, options) { | ||
| if (!options) { | ||
| // src/store/fastify-cookie-handler.ts | ||
| var FastifyCookieHandler = class { | ||
| setCookie(name, value, options, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const maxAge = 60 * 60; | ||
| const cookieOpts = { httpOnly: true, sameSite: "lax", path: "/", maxAge }; | ||
| const expiration = Math.floor(Date.now() / 1e3 + maxAge); | ||
| const encryptedStateData = await this.encrypt(identifier, transactionData, expiration); | ||
| options.reply.setCookie(identifier, encryptedStateData, cookieOpts); | ||
| storeOptions.reply.setCookie(name, value, options || {}); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| getCookie(name, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieValue = options.request.cookies[identifier]; | ||
| if (cookieValue) { | ||
| return await this.decrypt(identifier, cookieValue); | ||
| } | ||
| return storeOptions.request.cookies?.[name]; | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| getCookies(storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| options?.reply.clearCookie(identifier); | ||
| return storeOptions.request.cookies; | ||
| } | ||
| }; | ||
| // src/store/stateless-state-store.ts | ||
| import { BackchannelLogoutError } from "@auth0/auth0-server-js"; | ||
| // src/store/abstract-session-store.ts | ||
| import { AbstractStateStore } from "@auth0/auth0-server-js"; | ||
| var AbstractSessionStore = class extends AbstractStateStore { | ||
| #rolling; | ||
| #absoluteDuration; | ||
| #inactivityDuration; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#rolling = options.rolling ?? true; | ||
| this.#absoluteDuration = options.absoluteDuration ?? 60 * 60 * 24 * 3; | ||
| this.#inactivityDuration = options.inactivityDuration ?? 60 * 60 * 24 * 1; | ||
| } | ||
| /** | ||
| * calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations. | ||
| */ | ||
| calculateMaxAge(createdAt) { | ||
| if (!this.#rolling) { | ||
| return this.#absoluteDuration; | ||
| } | ||
| const now = Date.now() / 1e3 | 0; | ||
| const expiresAt = Math.min(now + this.#inactivityDuration, createdAt + this.#absoluteDuration); | ||
| const maxAge = expiresAt - now; | ||
| return maxAge > 0 ? maxAge : 0; | ||
| } | ||
| }; | ||
| // src/store/stateless-state-store.ts | ||
| var StatelessStateStore = class extends AbstractSessionStore { | ||
| #cookieOptions; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#cookieOptions = options.cookie; | ||
| } | ||
| async set(identifier, stateData, removeIfExists, options) { | ||
| if (!options) { | ||
| deleteCookie(name, storeOptions) { | ||
| if (!storeOptions) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const maxAge = this.calculateMaxAge(stateData.internal.createdAt); | ||
| const cookieOpts = { | ||
| httpOnly: true, | ||
| sameSite: this.#cookieOptions?.sameSite ?? "lax", | ||
| path: "/", | ||
| secure: this.#cookieOptions?.secure ?? "auto", | ||
| maxAge | ||
| }; | ||
| const expiration = Math.floor(Date.now() / 1e3 + maxAge); | ||
| const encryptedStateData = await this.encrypt(identifier, stateData, expiration); | ||
| const chunkSize = 3072; | ||
| const chunkCount = Math.ceil(encryptedStateData.length / chunkSize); | ||
| const chunks = [...Array(chunkCount).keys()].map((i) => ({ | ||
| value: encryptedStateData.substring(i * chunkSize, (i + 1) * chunkSize), | ||
| name: `${identifier}.${i}` | ||
| })); | ||
| chunks.forEach((chunk) => { | ||
| options.reply.setCookie(chunk.name, chunk.value, cookieOpts); | ||
| }); | ||
| const existingCookieKeys = this.getCookieKeys(identifier, options); | ||
| const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key)); | ||
| cookieKeysToRemove.forEach((key) => { | ||
| options.reply.clearCookie(key); | ||
| }); | ||
| storeOptions.reply.clearCookie(name); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieKeys = this.getCookieKeys(identifier, options); | ||
| const encryptedStateData = cookieKeys.map((key) => ({ index: parseInt(key.split(".")[1], 10), value: options.request.cookies[key] })).sort((a, b) => a.index - b.index).map((item) => item.value).join(""); | ||
| if (encryptedStateData) { | ||
| return await this.decrypt(identifier, encryptedStateData); | ||
| } | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const cookieKeys = this.getCookieKeys(identifier, options); | ||
| for (const key of cookieKeys) { | ||
| options?.reply.clearCookie(key); | ||
| } | ||
| } | ||
| deleteByLogoutToken() { | ||
| throw new BackchannelLogoutError( | ||
| "Backchannel logout is not available when using Stateless Storage. Use Stateful Storage by providing a `sessionStore`" | ||
| ); | ||
| } | ||
| getCookieKeys(identifier, options) { | ||
| return Object.keys(options.request.cookies).filter((key) => key.startsWith(identifier)); | ||
| } | ||
| }; | ||
| // src/store/stateful-state-store.ts | ||
| var generateId = () => { | ||
| const bytes = new Uint8Array(16); | ||
| crypto.getRandomValues(bytes); | ||
| return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""); | ||
| }; | ||
| var StatefulStateStore = class extends AbstractSessionStore { | ||
| #store; | ||
| #cookieOptions; | ||
| constructor(options) { | ||
| super(options); | ||
| this.#store = options.store; | ||
| this.#cookieOptions = options.cookie; | ||
| } | ||
| async set(identifier, stateData, removeIfExists, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| let sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId && removeIfExists) { | ||
| await this.#store.delete(sessionId); | ||
| sessionId = generateId(); | ||
| } | ||
| if (!sessionId) { | ||
| sessionId = generateId(); | ||
| } | ||
| const maxAge = this.calculateMaxAge(stateData.internal.createdAt); | ||
| const cookieOpts = { | ||
| httpOnly: true, | ||
| sameSite: this.#cookieOptions?.sameSite ?? "lax", | ||
| path: "/", | ||
| secure: this.#cookieOptions?.secure ?? "auto", | ||
| maxAge | ||
| }; | ||
| const expiration = Date.now() / 1e3 + maxAge; | ||
| const encryptedStateData = await this.encrypt( | ||
| identifier, | ||
| { | ||
| id: sessionId | ||
| }, | ||
| expiration | ||
| ); | ||
| await this.#store.set(sessionId, stateData); | ||
| options.reply.setCookie(identifier, encryptedStateData, cookieOpts); | ||
| } | ||
| async get(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId) { | ||
| const stateData = await this.#store.get(sessionId); | ||
| if (!stateData) { | ||
| options?.reply.clearCookie(identifier); | ||
| } | ||
| return stateData; | ||
| } | ||
| } | ||
| async delete(identifier, options) { | ||
| if (!options) { | ||
| throw new MissingStoreOptionsError(); | ||
| } | ||
| const sessionId = await this.getSessionId(identifier, options); | ||
| if (sessionId) { | ||
| await this.#store.delete(sessionId); | ||
| } | ||
| options?.reply.clearCookie(identifier); | ||
| } | ||
| async getSessionId(identifier, options) { | ||
| const cookieValue = options.request.cookies[identifier]; | ||
| if (cookieValue) { | ||
| const sessionCookie = await this.decrypt(identifier, cookieValue); | ||
| return sessionCookie.id; | ||
| } | ||
| } | ||
| deleteByLogoutToken(claims, options) { | ||
| return this.#store.deleteByLogoutToken(claims, options); | ||
| } | ||
| }; | ||
| // src/utils.ts | ||
| function ensureTrailingSlash(value) { | ||
| return value && !value.endsWith("/") ? `${value}/` : value; | ||
| } | ||
| function ensureNoLeadingSlash(value) { | ||
| return value && value.startsWith("/") ? value.substring(1, value.length) : value; | ||
| } | ||
| function createRouteUrl(url, base) { | ||
| return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base)); | ||
| } | ||
| function toSafeRedirect(dangerousRedirect, safeBaseUrl) { | ||
| let url; | ||
| try { | ||
| url = createRouteUrl(dangerousRedirect, safeBaseUrl); | ||
| } catch { | ||
| return void 0; | ||
| } | ||
| if (url.origin === new URL(safeBaseUrl).origin) { | ||
| return url.toString(); | ||
| } | ||
| return void 0; | ||
| } | ||
| // src/index.ts | ||
| import { CookieTransactionStore as CookieTransactionStore2 } from "@auth0/auth0-server-js"; | ||
| var index_default = fp(async function auth0Fastify(fastify, options) { | ||
| const callbackPath = "/auth/callback"; | ||
| const callbackPath = options.routes?.callback ?? "/auth/callback"; | ||
| const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl); | ||
@@ -258,3 +81,3 @@ const auth0Client = new ServerClient({ | ||
| }, | ||
| transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }), | ||
| transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }, new FastifyCookieHandler()), | ||
| stateStore: options.sessionStore ? new StatefulStateStore({ | ||
@@ -264,6 +87,6 @@ ...options.sessionConfiguration, | ||
| store: options.sessionStore | ||
| }) : new StatelessStateStore({ | ||
| }, new FastifyCookieHandler()) : new StatelessStateStore({ | ||
| ...options.sessionConfiguration, | ||
| secret: options.sessionSecret | ||
| }), | ||
| }, new FastifyCookieHandler()), | ||
| stateIdentifier: options.sessionConfiguration?.cookie?.name, | ||
@@ -278,3 +101,3 @@ customFetch: options.customFetch | ||
| fastify.get( | ||
| "/auth/login", | ||
| options.routes?.login ?? "/auth/login", | ||
| async (request, reply) => { | ||
@@ -293,3 +116,3 @@ const dangerousReturnTo = request.query.returnTo; | ||
| ); | ||
| fastify.get("/auth/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.callback ?? "/auth/callback", async (request, reply) => { | ||
| const { appState } = await auth0Client.completeInteractiveLogin( | ||
@@ -301,3 +124,3 @@ createRouteUrl(request.url, options.appBaseUrl), | ||
| }); | ||
| fastify.get("/auth/logout", async (request, reply) => { | ||
| fastify.get(options.routes?.logout ?? "/auth/logout", async (request, reply) => { | ||
| const returnTo = options.appBaseUrl; | ||
@@ -308,3 +131,3 @@ const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply }); | ||
| fastify.post( | ||
| "/auth/backchannel-logout", | ||
| options.routes?.backchannelLogout ?? "/auth/backchannel-logout", | ||
| async (request, reply) => { | ||
@@ -327,3 +150,3 @@ const logoutToken = request.body.logout_token; | ||
| fastify.get( | ||
| "/auth/connect", | ||
| options.routes?.connect ?? "/auth/connect", | ||
| async (request, reply) => { | ||
@@ -339,3 +162,3 @@ const { connection, connectionScope, returnTo } = request.query; | ||
| const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || "/", options.appBaseUrl); | ||
| const callbackPath2 = "/auth/connect/callback"; | ||
| const callbackPath2 = options.routes?.connectCallback ?? "/auth/connect/callback"; | ||
| const redirectUri2 = createRouteUrl(callbackPath2, options.appBaseUrl); | ||
@@ -358,3 +181,3 @@ const linkUserUrl = await fastify.auth0Client.startLinkUser( | ||
| ); | ||
| fastify.get("/auth/connect/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.connectCallback ?? "/auth/connect/callback", async (request, reply) => { | ||
| const { appState } = await fastify.auth0Client.completeLinkUser( | ||
@@ -370,3 +193,3 @@ createRouteUrl(request.url, options.appBaseUrl), | ||
| fastify.get( | ||
| "/auth/unconnect", | ||
| options.routes?.unconnect ?? "/auth/unconnect", | ||
| async (request, reply) => { | ||
@@ -382,3 +205,3 @@ const { connection, returnTo } = request.query; | ||
| const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || "/", options.appBaseUrl); | ||
| const callbackPath2 = "/auth/unconnect/callback"; | ||
| const callbackPath2 = options.routes?.unconnectCallback ?? "/auth/unconnect/callback"; | ||
| const redirectUri2 = createRouteUrl(callbackPath2, options.appBaseUrl); | ||
@@ -400,3 +223,3 @@ const linkUserUrl = await fastify.auth0Client.startUnlinkUser( | ||
| ); | ||
| fastify.get("/auth/unconnect/callback", async (request, reply) => { | ||
| fastify.get(options.routes?.unconnectCallback ?? "/auth/unconnect/callback", async (request, reply) => { | ||
| const { appState } = await fastify.auth0Client.completeUnlinkUser( | ||
@@ -416,5 +239,5 @@ createRouteUrl(request.url, options.appBaseUrl), | ||
| export { | ||
| CookieTransactionStore, | ||
| CookieTransactionStore2 as CookieTransactionStore, | ||
| index_default as default | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/store/cookie-transaction-store.ts","../src/errors/index.ts","../src/store/stateless-state-store.ts","../src/store/abstract-session-store.ts","../src/store/stateful-state-store.ts","../src/utils.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { ServerClient } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, SessionStore, StoreOptions } from './types.js';\nimport { CookieTransactionStore } from './store/cookie-transaction-store.js';\nimport { StatelessStateStore } from './store/stateless-state-store.js';\nimport { StatefulStateStore } from './store/stateful-state-store.js';\nimport { createRouteUrl, toSafeRedirect } from './utils.js';\n\nexport * from './types.js';\nexport { CookieTransactionStore } from './store/cookie-transaction-store.js';\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n auth0Client: ServerClient<StoreOptions> | undefined;\n }\n}\n\nexport interface Auth0FastifyOptions {\n domain: string;\n clientId: string;\n clientSecret?: string;\n clientAssertionSigningKey?: string | CryptoKey;\n clientAssertionSigningAlg?: string;\n audience?: string;\n appBaseUrl: string;\n\n pushedAuthorizationRequests?: boolean;\n\n sessionSecret: string;\n sessionStore?: SessionStore;\n sessionConfiguration?: SessionConfiguration;\n /**\n * Whether to mount the default routes for login, logout, callback and profile.\n * Defaults to true.\n */\n mountRoutes?: boolean;\n /**\n * Whether to mount the routes for account linking and unlinking.\n * Defaults to false.\n */\n mountConnectRoutes?: boolean;\n /**\n * Optional, custom Fetch implementation to use.\n */\n customFetch?: typeof fetch;\n}\n\nexport default fp(async function auth0Fastify(fastify: FastifyInstance, options: Auth0FastifyOptions) {\n const callbackPath = '/auth/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n\n const auth0Client = new ServerClient<StoreOptions>({\n domain: options.domain,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n clientAssertionSigningKey: options.clientAssertionSigningKey,\n clientAssertionSigningAlg: options.clientAssertionSigningAlg,\n authorizationParams: {\n audience: options.audience,\n redirect_uri: redirectUri.toString(),\n },\n transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }),\n stateStore: options.sessionStore\n ? new StatefulStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n store: options.sessionStore,\n })\n : new StatelessStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n }),\n stateIdentifier: options.sessionConfiguration?.cookie?.name,\n customFetch: options.customFetch,\n });\n\n if (!fastify.hasReplyDecorator('cookie')) {\n fastify.register(import('@fastify/cookie'));\n }\n\n const shouldMountRoutes = options.mountRoutes ?? true;\n\n if (shouldMountRoutes) {\n fastify.get(\n '/auth/login',\n async (\n request: FastifyRequest<{\n Querystring: { returnTo?: string };\n }>,\n reply\n ) => {\n const dangerousReturnTo = request.query.returnTo;\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n\n const authorizationUrl = await auth0Client.startInteractiveLogin(\n {\n pushedAuthorizationRequests: options.pushedAuthorizationRequests,\n appState: { returnTo: sanitizedReturnTo },\n },\n { request, reply }\n );\n\n reply.redirect(authorizationUrl.href);\n }\n );\n\n fastify.get('/auth/callback', async (request, reply) => {\n const { appState } = await auth0Client.completeInteractiveLogin<{ returnTo: string } | undefined>(\n createRouteUrl(request.url, options.appBaseUrl),\n { request, reply }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get('/auth/logout', async (request, reply) => {\n const returnTo = options.appBaseUrl;\n const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply });\n\n reply.redirect(logoutUrl.href);\n });\n\n fastify.post(\n '/auth/backchannel-logout',\n async (\n request: FastifyRequest<{\n Body: { logout_token?: string };\n }>,\n reply\n ) => {\n const logoutToken = request.body.logout_token;\n\n if (!logoutToken) {\n reply.code(400).send('Missing `logout_token` in the request body.');\n\n return;\n }\n\n try {\n await auth0Client.handleBackchannelLogout(logoutToken, { request, reply });\n reply.code(204).send(null);\n } catch (e) {\n reply.code(400).send((e as Error).message);\n }\n }\n );\n\n const shouldMountConnectRoutes = options.mountConnectRoutes ?? false;\n\n if (shouldMountConnectRoutes) {\n fastify.get(\n '/auth/connect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; connectionScope: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, connectionScope, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = '/auth/connect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startLinkUser(\n {\n connection: connection,\n connectionScope: connectionScope,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get('/auth/connect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeLinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(\n '/auth/unconnect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = '/auth/unconnect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startUnlinkUser(\n {\n connection: connection,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get('/auth/unconnect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeUnlinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n }\n }\n\n fastify.decorate('auth0Client', auth0Client);\n});\n","import { CookieSerializeOptions } from '@fastify/cookie';\nimport { TransactionData, AbstractTransactionStore } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport { StoreOptions } from '../types.js';\n\nexport class CookieTransactionStore extends AbstractTransactionStore<StoreOptions> {\n async set(\n identifier: string,\n transactionData: TransactionData,\n removeIfExists?: boolean,\n options?: StoreOptions\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const maxAge = 60 * 60;\n const cookieOpts: CookieSerializeOptions = { httpOnly: true, sameSite: 'lax', path: '/', maxAge };\n const expiration = Math.floor(Date.now() / 1000 + maxAge);\n const encryptedStateData = await this.encrypt(identifier, transactionData, expiration);\n \n options.reply.setCookie(identifier, encryptedStateData, cookieOpts);\n }\n\n async get(identifier: string, options?: StoreOptions): Promise<TransactionData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieValue = options.request.cookies[identifier];\n\n if (cookieValue) {\n return await this.decrypt(identifier, cookieValue);\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n options?.reply.clearCookie(identifier);\n }\n}\n","export class MissingStoreOptionsError extends Error {\n public code: string = 'missing_store_options_error';\n\n constructor(message?: string) {\n super(message ?? 'The store options are missing, making it impossible to interact with the store.');\n this.name = 'MissingStoreOptionsError';\n }\n}\n","import type { CookieSerializeOptions } from '@fastify/cookie';\nimport { BackchannelLogoutError, EncryptedStoreOptions, StateData } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport type { SessionConfiguration, SessionCookieOptions, StoreOptions } from '../types.js';\nimport { AbstractSessionStore } from './abstract-session-store.js';\n\nexport class StatelessStateStore extends AbstractSessionStore {\n readonly #cookieOptions: SessionCookieOptions | undefined;\n\n constructor(options: SessionConfiguration & EncryptedStoreOptions) {\n super(options);\n\n this.#cookieOptions = options.cookie;\n }\n\n async set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const maxAge = this.calculateMaxAge(stateData.internal.createdAt);\n const cookieOpts: CookieSerializeOptions = {\n httpOnly: true,\n sameSite: this.#cookieOptions?.sameSite ?? 'lax',\n path: '/',\n secure: this.#cookieOptions?.secure ?? 'auto',\n maxAge,\n };\n const expiration = Math.floor(Date.now() / 1000 + maxAge);\n const encryptedStateData = await this.encrypt(identifier, stateData, expiration);\n\n const chunkSize = 3072;\n const chunkCount = Math.ceil(encryptedStateData.length / chunkSize);\n const chunks = [...Array(chunkCount).keys()].map((i) => ({\n value: encryptedStateData.substring(i * chunkSize, (i + 1) * chunkSize),\n name: `${identifier}.${i}`,\n }));\n\n chunks.forEach((chunk) => {\n options.reply.setCookie(chunk.name, chunk.value, cookieOpts);\n });\n\n const existingCookieKeys = this.getCookieKeys(identifier, options);\n const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key));\n cookieKeysToRemove.forEach((key) => {\n options.reply.clearCookie(key);\n });\n }\n\n async get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieKeys = this.getCookieKeys(identifier, options);\n const encryptedStateData = cookieKeys\n .map((key) => ({ index: parseInt(key.split('.')[1] as string, 10), value: options.request.cookies[key] }))\n .sort((a, b) => a.index - b.index)\n .map((item) => item.value)\n .join('');\n\n if (encryptedStateData) {\n return (await this.decrypt(identifier, encryptedStateData)) as StateData;\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const cookieKeys = this.getCookieKeys(identifier, options);\n for (const key of cookieKeys) {\n options?.reply.clearCookie(key);\n }\n }\n\n deleteByLogoutToken(): Promise<void> {\n throw new BackchannelLogoutError(\n 'Backchannel logout is not available when using Stateless Storage. Use Stateful Storage by providing a `sessionStore`'\n );\n }\n\n private getCookieKeys(identifier: string, options: StoreOptions): string[] {\n return Object.keys(options.request.cookies).filter((key) => key.startsWith(identifier));\n }\n}\n","import { AbstractStateStore, EncryptedStoreOptions, LogoutTokenClaims, StateData } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, StoreOptions } from '../types.js';\n\nexport abstract class AbstractSessionStore extends AbstractStateStore<StoreOptions> {\n readonly #rolling: boolean;\n readonly #absoluteDuration: number;\n readonly #inactivityDuration: number;\n\n constructor(options: SessionConfiguration & EncryptedStoreOptions) {\n super(options);\n\n this.#rolling = options.rolling ?? true;\n this.#absoluteDuration = options.absoluteDuration ?? 60 * 60 * 24 * 3;\n this.#inactivityDuration = options.inactivityDuration ?? 60 * 60 * 24 * 1;\n }\n\n abstract set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void>;\n\n abstract get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined>;\n\n abstract delete(identifier: string, options?: StoreOptions | undefined): Promise<void>;\n\n abstract deleteByLogoutToken(claims: LogoutTokenClaims, options?: StoreOptions | undefined): Promise<void>;\n\n /**\n * calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations.\n */\n protected calculateMaxAge(createdAt: number) {\n if (!this.#rolling) {\n return this.#absoluteDuration;\n }\n\n const now = (Date.now() / 1000) | 0;\n const expiresAt = Math.min(now + this.#inactivityDuration, createdAt + this.#absoluteDuration);\n const maxAge = expiresAt - now;\n\n return maxAge > 0 ? maxAge : 0;\n }\n}\n","import type { CookieSerializeOptions } from '@fastify/cookie';\nimport { EncryptedStoreOptions, LogoutTokenClaims, StateData } from '@auth0/auth0-server-js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\nimport type { SessionConfiguration, SessionCookieOptions, SessionStore, StoreOptions } from '../types.js';\nimport { AbstractSessionStore } from './abstract-session-store.js';\n\nexport interface StatefulStateStoreOptions extends EncryptedStoreOptions {\n store: SessionStore;\n}\n\nconst generateId = () => {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n};\n\nexport class StatefulStateStore extends AbstractSessionStore {\n readonly #store: SessionStore;\n readonly #cookieOptions: SessionCookieOptions | undefined;\n\n constructor(options: StatefulStateStoreOptions & SessionConfiguration) {\n super(options);\n\n this.#store = options.store;\n this.#cookieOptions = options.cookie;\n }\n\n async set(\n identifier: string,\n stateData: StateData,\n removeIfExists?: boolean,\n options?: StoreOptions | undefined\n ): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n let sessionId = await this.getSessionId(identifier, options);\n\n // if this is a new session created by a new login we need to remove the old session\n // from the store and regenerate the session ID to prevent session fixation.\n if (sessionId && removeIfExists) {\n await this.#store.delete(sessionId);\n sessionId = generateId();\n }\n\n if (!sessionId) {\n sessionId = generateId();\n }\n\n const maxAge = this.calculateMaxAge(stateData.internal.createdAt);\n const cookieOpts: CookieSerializeOptions = {\n httpOnly: true,\n sameSite: this.#cookieOptions?.sameSite ?? 'lax',\n path: '/',\n secure: this.#cookieOptions?.secure ?? 'auto',\n maxAge,\n };\n const expiration = Date.now() / 1000 + maxAge;\n const encryptedStateData = await this.encrypt<{ id: string }>(\n identifier,\n {\n id: sessionId,\n },\n expiration\n );\n\n await this.#store.set(sessionId, stateData);\n\n options.reply.setCookie(identifier, encryptedStateData, cookieOpts);\n }\n\n async get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const sessionId = await this.getSessionId(identifier, options);\n\n if (sessionId) {\n const stateData = await this.#store.get(sessionId);\n\n // If we have a session cookie, but no `stateData`, we should remove the cookie.\n if (!stateData) {\n options?.reply.clearCookie(identifier);\n }\n\n return stateData;\n }\n }\n\n async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {\n // We can not handle cookies in Fastify when the `StoreOptions` are not provided.\n if (!options) {\n throw new MissingStoreOptionsError();\n }\n\n const sessionId = await this.getSessionId(identifier, options);\n\n if (sessionId) {\n await this.#store.delete(sessionId);\n }\n\n options?.reply.clearCookie(identifier);\n }\n\n private async getSessionId(identifier: string, options: StoreOptions) {\n const cookieValue = options.request.cookies[identifier];\n if (cookieValue) {\n const sessionCookie = await this.decrypt<{ id: string }>(identifier, cookieValue);\n return sessionCookie.id;\n }\n }\n\n deleteByLogoutToken(claims: LogoutTokenClaims, options?: StoreOptions | undefined): Promise<void> {\n return this.#store.deleteByLogoutToken(claims, options);\n }\n}\n","/**\n * Ensures the value has a trailing slash.\n * If it does not, it will append one.\n * @param value The value to ensure has a trailing slash.\n * @returns The value with a trailing slash.\n */\nfunction ensureTrailingSlash(value: string) {\n return value && !value.endsWith('/') ? `${value}/` : value;\n}\n\n/**\n * Ensures the value does not have a leading slash.\n * If it does, it will trim it.\n * @param value The value to ensure has no leading slash.\n * @returns The value without a leading slash.\n */\nfunction ensureNoLeadingSlash(value: string) {\n return value && value.startsWith('/') ? value.substring(1, value.length) : value;\n}\n\n/**\n * Utility function to ensure Route URLs are created correctly when using both the root and subpath as base URL.\n * @param url The URL to use.\n * @param base The base URL to use.\n * @returns A URL object, combining the base and url.\n */\nexport function createRouteUrl(url: string, base: string) {\n return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base));\n}\n\n/**\n * Function to ensure a redirect URL is safe to use, as in, it has the same origin as the safeBaseUrl.\n * @param dangerousRedirect The redirect URL to check.\n * @param safeBaseUrl The base URL to check against.\n * @returns A safe redirect URL or undefined if the redirect URL is not safe.\n */\nexport function toSafeRedirect(dangerousRedirect: string, safeBaseUrl: string): string | undefined {\n let url: URL;\n\n try {\n url = createRouteUrl(dangerousRedirect, safeBaseUrl);\n } catch {\n return undefined;\n }\n\n if (url.origin === new URL(safeBaseUrl).origin) {\n return url.toString();\n }\n\n return undefined;\n}"],"mappings":"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","names":["callbackPath","redirectUri"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/errors/index.ts","../src/store/fastify-cookie-handler.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { CookieTransactionStore, ServerClient, StatelessStateStore, StatefulStateStore } from '@auth0/auth0-server-js';\nimport type { SessionConfiguration, SessionStore, StoreOptions } from './types.js';\nimport { createRouteUrl, toSafeRedirect } from './utils.js';\nimport { FastifyCookieHandler } from './store/fastify-cookie-handler.js';\n\nexport * from './types.js';\nexport { CookieTransactionStore } from '@auth0/auth0-server-js';\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n auth0Client: ServerClient<StoreOptions> | undefined;\n }\n}\n\nexport interface Auth0FastifyOptions {\n domain: string;\n clientId: string;\n clientSecret?: string;\n clientAssertionSigningKey?: string | CryptoKey;\n clientAssertionSigningAlg?: string;\n audience?: string;\n appBaseUrl: string;\n\n pushedAuthorizationRequests?: boolean;\n\n sessionSecret: string;\n sessionStore?: SessionStore;\n sessionConfiguration?: SessionConfiguration;\n /**\n * Whether to mount the default routes for login, logout, callback and profile.\n * Defaults to true.\n */\n mountRoutes?: boolean;\n /**\n * Whether to mount the routes for account linking and unlinking.\n * Defaults to false.\n */\n mountConnectRoutes?: boolean;\n /**\n * Optional, custom Fetch implementation to use.\n */\n customFetch?: typeof fetch;\n\n routes?: {\n login?: string;\n callback?: string;\n logout?: string;\n backchannelLogout?: string;\n connect?: string;\n connectCallback?: string;\n unconnect?: string;\n unconnectCallback?: string;\n }\n}\n\nexport default fp(async function auth0Fastify(fastify: FastifyInstance, options: Auth0FastifyOptions) {\n const callbackPath = options.routes?.callback ?? '/auth/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n\n const auth0Client = new ServerClient<StoreOptions>({\n domain: options.domain,\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n clientAssertionSigningKey: options.clientAssertionSigningKey,\n clientAssertionSigningAlg: options.clientAssertionSigningAlg,\n authorizationParams: {\n audience: options.audience,\n redirect_uri: redirectUri.toString(),\n },\n transactionStore: new CookieTransactionStore({ secret: options.sessionSecret }, new FastifyCookieHandler()),\n stateStore: options.sessionStore\n ? new StatefulStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n store: options.sessionStore,\n }, new FastifyCookieHandler())\n : new StatelessStateStore({\n ...options.sessionConfiguration,\n secret: options.sessionSecret,\n }, new FastifyCookieHandler()),\n stateIdentifier: options.sessionConfiguration?.cookie?.name,\n customFetch: options.customFetch,\n });\n\n if (!fastify.hasReplyDecorator('cookie')) {\n fastify.register(import('@fastify/cookie'));\n }\n\n const shouldMountRoutes = options.mountRoutes ?? true;\n\n if (shouldMountRoutes) {\n fastify.get(\n options.routes?.login ?? '/auth/login',\n async (\n request: FastifyRequest<{\n Querystring: { returnTo?: string };\n }>,\n reply\n ) => {\n const dangerousReturnTo = request.query.returnTo;\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n\n const authorizationUrl = await auth0Client.startInteractiveLogin(\n {\n pushedAuthorizationRequests: options.pushedAuthorizationRequests,\n appState: { returnTo: sanitizedReturnTo },\n },\n { request, reply }\n );\n\n reply.redirect(authorizationUrl.href);\n }\n );\n\n fastify.get(options.routes?.callback ?? '/auth/callback', async (request, reply) => {\n const { appState } = await auth0Client.completeInteractiveLogin<{ returnTo: string } | undefined>(\n createRouteUrl(request.url, options.appBaseUrl),\n { request, reply }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(options.routes?.logout ?? '/auth/logout', async (request, reply) => {\n const returnTo = options.appBaseUrl;\n const logoutUrl = await auth0Client.logout({ returnTo: returnTo.toString() }, { request, reply });\n\n reply.redirect(logoutUrl.href);\n });\n\n fastify.post(\n options.routes?.backchannelLogout ?? '/auth/backchannel-logout',\n async (\n request: FastifyRequest<{\n Body: { logout_token?: string };\n }>,\n reply\n ) => {\n const logoutToken = request.body.logout_token;\n\n if (!logoutToken) {\n reply.code(400).send('Missing `logout_token` in the request body.');\n\n return;\n }\n\n try {\n await auth0Client.handleBackchannelLogout(logoutToken, { request, reply });\n reply.code(204).send(null);\n } catch (e) {\n reply.code(400).send((e as Error).message);\n }\n }\n );\n\n const shouldMountConnectRoutes = options.mountConnectRoutes ?? false;\n\n if (shouldMountConnectRoutes) {\n fastify.get(\n options.routes?.connect ?? '/auth/connect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; connectionScope: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, connectionScope, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = options.routes?.connectCallback ?? '/auth/connect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startLinkUser(\n {\n connection: connection,\n connectionScope: connectionScope,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get(options.routes?.connectCallback ?? '/auth/connect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeLinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n\n fastify.get(\n options.routes?.unconnect ?? '/auth/unconnect',\n async (\n request: FastifyRequest<{\n Querystring: { connection: string; returnTo?: string };\n }>,\n reply\n ) => {\n const { connection, returnTo } = request.query;\n const dangerousReturnTo = returnTo;\n\n if (!connection) {\n return reply.code(400).send({\n error: 'invalid_request',\n error_description: 'connection is required',\n });\n }\n\n const sanitizedReturnTo = toSafeRedirect(dangerousReturnTo || '/', options.appBaseUrl);\n const callbackPath = options.routes?.unconnectCallback ?? '/auth/unconnect/callback';\n const redirectUri = createRouteUrl(callbackPath, options.appBaseUrl);\n const linkUserUrl = await fastify.auth0Client!.startUnlinkUser(\n {\n connection: connection,\n authorizationParams: {\n redirect_uri: redirectUri.toString(),\n },\n appState: {\n returnTo: sanitizedReturnTo,\n },\n },\n { request, reply }\n );\n\n reply.redirect(linkUserUrl.href);\n }\n );\n\n fastify.get(options.routes?.unconnectCallback ?? '/auth/unconnect/callback', async (request, reply) => {\n const { appState } = await fastify.auth0Client!.completeUnlinkUser<{ returnTo: string }>(\n createRouteUrl(request.url, options.appBaseUrl),\n {\n request,\n reply,\n }\n );\n\n reply.redirect(appState?.returnTo ?? options.appBaseUrl);\n });\n }\n }\n\n fastify.decorate('auth0Client', auth0Client);\n});\n","/**\n * Ensures the value has a trailing slash.\n * If it does not, it will append one.\n * @param value The value to ensure has a trailing slash.\n * @returns The value with a trailing slash.\n */\nfunction ensureTrailingSlash(value: string) {\n return value && !value.endsWith('/') ? `${value}/` : value;\n}\n\n/**\n * Ensures the value does not have a leading slash.\n * If it does, it will trim it.\n * @param value The value to ensure has no leading slash.\n * @returns The value without a leading slash.\n */\nfunction ensureNoLeadingSlash(value: string) {\n return value && value.startsWith('/') ? value.substring(1, value.length) : value;\n}\n\n/**\n * Utility function to ensure Route URLs are created correctly when using both the root and subpath as base URL.\n * @param url The URL to use.\n * @param base The base URL to use.\n * @returns A URL object, combining the base and url.\n */\nexport function createRouteUrl(url: string, base: string) {\n return new URL(ensureNoLeadingSlash(url), ensureTrailingSlash(base));\n}\n\n/**\n * Function to ensure a redirect URL is safe to use, as in, it has the same origin as the safeBaseUrl.\n * @param dangerousRedirect The redirect URL to check.\n * @param safeBaseUrl The base URL to check against.\n * @returns A safe redirect URL or undefined if the redirect URL is not safe.\n */\nexport function toSafeRedirect(dangerousRedirect: string, safeBaseUrl: string): string | undefined {\n let url: URL;\n\n try {\n url = createRouteUrl(dangerousRedirect, safeBaseUrl);\n } catch {\n return undefined;\n }\n\n if (url.origin === new URL(safeBaseUrl).origin) {\n return url.toString();\n }\n\n return undefined;\n}","export class MissingStoreOptionsError extends Error {\n public code: string = 'missing_store_options_error';\n\n constructor(message?: string) {\n super(message ?? 'The store options are missing, making it impossible to interact with the store.');\n this.name = 'MissingStoreOptionsError';\n }\n}\n","import { CookieHandler, CookieSerializeOptions } from '@auth0/auth0-server-js';\nimport { StoreOptions } from '../types.js';\nimport { MissingStoreOptionsError } from '../errors/index.js';\n\nexport class FastifyCookieHandler implements CookieHandler<StoreOptions> {\n setCookie(\n name: string,\n value: string,\n options?: CookieSerializeOptions,\n storeOptions?: StoreOptions,\n ): void {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n storeOptions.reply.setCookie(name, value, options || {});\n }\n\n getCookie(name: string, storeOptions?: StoreOptions): string | undefined {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n return storeOptions.request.cookies?.[name];\n }\n\n getCookies(storeOptions?: StoreOptions): Record<string, string> {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n return storeOptions.request.cookies as Record<string, string>;\n }\n\n deleteCookie(name: string, storeOptions?: StoreOptions): void {\n if (!storeOptions) {\n throw new MissingStoreOptionsError();\n }\n\n storeOptions.reply.clearCookie(name);\n }\n}\n"],"mappings":"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","names":["CookieTransactionStore","callbackPath","redirectUri"]} |
+9
-8
| { | ||
| "name": "@auth0/auth0-fastify", | ||
| "version": "1.0.2", | ||
| "version": "1.1.0", | ||
| "description": "Auth0 Authentication SDK for Fastify Applications on JavaScript runtimes", | ||
@@ -30,2 +30,3 @@ "author": "Auth0", | ||
| "eslint": "^9.20.1", | ||
| "msw": "^2.11.3", | ||
| "tsup": "^8.4.0", | ||
@@ -37,5 +38,5 @@ "typescript": "^5.7.3", | ||
| "dependencies": { | ||
| "@auth0/auth0-server-js": "^1.0.0", | ||
| "@auth0/auth0-server-js": "^1.1.0", | ||
| "@fastify/cookie": "^11.0.2", | ||
| "fastify": "^5.2.1", | ||
| "fastify": "^5.3.2", | ||
| "fastify-plugin": "^5.0.1" | ||
@@ -47,15 +48,15 @@ }, | ||
| "publishConfig": { | ||
| "access": "public" | ||
| "access": "public" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git://github.com/auth0/auth0-fastify.git" | ||
| "type": "git", | ||
| "url": "git://github.com/auth0/auth0-fastify.git" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/auth0/auth0-fastify/issues" | ||
| "url": "https://github.com/auth0/auth0-fastify/issues" | ||
| }, | ||
| "homepage": "https://github.com/auth0/auth0-fastify#readme", | ||
| "keywords": [ | ||
| "auth0" | ||
| "auth0" | ||
| ] | ||
| } |
71099
-35.1%8
14.29%590
-36.42%Updated