better-auth-cookie-consent 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as cookieConsentPlugin, o as Consent } from "./index-SuR6zFkq.mjs";
1
+ import { n as cookieConsentPlugin, o as Consent } from "./index-B8ovmV25.mjs";
2
2
  import * as better_auth0 from "better-auth";
3
3
  import * as nanostores from "nanostores";
4
4
  import * as better_auth_client0 from "better-auth/client";
@@ -101,13 +101,6 @@ declare const cookieConsentClient: <TConsent extends Consent = Consent>() => {
101
101
  }>;
102
102
  };
103
103
  };
104
- $ERROR_CODES: {
105
- MISSING_ANONYMOUS_ID: better_auth0.RawError<"MISSING_ANONYMOUS_ID">;
106
- CONSENT_NOT_FOUND: better_auth0.RawError<"CONSENT_NOT_FOUND">;
107
- INVALID_CONSENT: better_auth0.RawError<"INVALID_CONSENT">;
108
- VERSION_MISMATCH: better_auth0.RawError<"VERSION_MISMATCH">;
109
- AUTHENTICATION_REQUIRED: better_auth0.RawError<"AUTHENTICATION_REQUIRED">;
110
- };
111
104
  };
112
105
  //#endregion
113
106
  export { ConsentState, cookieConsentClient };
package/dist/client.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { t as COOKIE_CONSENT_ERROR_CODES } from "./error-codes-D3VdAX9J.mjs";
2
1
  import { atom } from "nanostores";
3
2
  //#region src/client.ts
4
3
  /**
@@ -69,8 +68,7 @@ const cookieConsentClient = () => {
69
68
  return res;
70
69
  }
71
70
  } };
72
- },
73
- $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES
71
+ }
74
72
  };
75
73
  };
76
74
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from 'better-auth/client';\nimport { atom } from 'nanostores';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type { Consent, cookieConsentPlugin } from './index';\n\n/**\n * Client-side consent state, kept in sync with the server.\n */\nexport interface ConsentState<TConsent extends Consent = Consent> {\n consent: TConsent | null;\n consentVersion: string | null;\n versionMatch: boolean;\n}\n\n/**\n * Client plugin for cookie consent management.\n *\n * @typeParam TConsent - The consent shape, typically `z.infer<typeof yourConsentSchema>`.\n *\n * @example\n * ```ts\n * import { defaultConsentSchema } from 'better-auth-cookie-consent';\n * import { cookieConsentClient } from 'better-auth-cookie-consent/client';\n * import type { z } from 'zod';\n *\n * const authClient = createAuthClient({\n * plugins: [cookieConsentClient<z.infer<typeof defaultConsentSchema>>()],\n * });\n * ```\n */\nexport const cookieConsentClient = <TConsent extends Consent = Consent>() => {\n return {\n id: 'cookie-consent',\n $InferServerPlugin: {} as ReturnType<typeof cookieConsentPlugin>,\n\n getAtoms($fetch) {\n const $consent = atom<ConsentState<TConsent>>({\n consent: null,\n consentVersion: null,\n versionMatch: false,\n });\n return { $consent };\n },\n\n getActions($fetch, $store) {\n const consentAtom = $store.atoms.$consent as ReturnType<typeof atom<ConsentState<TConsent>>>;\n\n async function syncFromServer(anonymousId?: string) {\n let path = '/cookie-consent/get';\n if (anonymousId) {\n const params = new URLSearchParams({ anonymousId });\n path = `${path}?${params.toString()}`;\n }\n const res = await $fetch<{\n consent: {\n id: string;\n userId?: string | null;\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n timestamp: string;\n } | null;\n versionMatch: boolean;\n }>(`${path}`, { method: 'GET' });\n if (res.data) {\n consentAtom.set({\n consent: res.data.consent?.consent ?? null,\n consentVersion: res.data.consent?.consentVersion ?? null,\n versionMatch: res.data.versionMatch,\n });\n }\n return res;\n }\n\n return {\n cookieConsent: {\n /**\n * Set consent preferences on the server.\n * Also used for accept-all / reject-all by passing the\n * appropriate consent object (all `true` or all `false`).\n */\n setConsent: async (data: {\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n }) => {\n const res = await $fetch<{ status: boolean }>('/cookie-consent/set', {\n method: 'POST',\n body: data,\n });\n if (res.data?.status) {\n consentAtom.set({\n consent: data.consent,\n consentVersion: data.consentVersion,\n versionMatch: true,\n });\n }\n return res;\n },\n\n /**\n * Retrieve consent from the server.\n */\n getConsent: async (anonymousId?: string) => {\n return syncFromServer(anonymousId);\n },\n\n /**\n * Merge anonymous consent into the authenticated user's record.\n */\n mergeConsent: async (anonymousId: string) => {\n const res = await $fetch<{ status: boolean; merged: boolean }>(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: { anonymousId },\n },\n );\n if (res.data?.merged) {\n await syncFromServer(anonymousId);\n }\n return res;\n },\n },\n };\n },\n\n $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA+BA,MAAa,4BAAgE;AAC3E,QAAO;EACL,IAAI;EACJ,oBAAoB,EAAE;EAEtB,SAAS,QAAQ;AAMf,UAAO,EAAE,UALQ,KAA6B;IAC5C,SAAS;IACT,gBAAgB;IAChB,cAAc;IACf,CAAC,EACiB;;EAGrB,WAAW,QAAQ,QAAQ;GACzB,MAAM,cAAc,OAAO,MAAM;GAEjC,eAAe,eAAe,aAAsB;IAClD,IAAI,OAAO;AACX,QAAI,aAAa;KACf,MAAM,SAAS,IAAI,gBAAgB,EAAE,aAAa,CAAC;AACnD,YAAO,GAAG,KAAK,GAAG,OAAO,UAAU;;IAErC,MAAM,MAAM,MAAM,OAUf,GAAG,QAAQ,EAAE,QAAQ,OAAO,CAAC;AAChC,QAAI,IAAI,KACN,aAAY,IAAI;KACd,SAAS,IAAI,KAAK,SAAS,WAAW;KACtC,gBAAgB,IAAI,KAAK,SAAS,kBAAkB;KACpD,cAAc,IAAI,KAAK;KACxB,CAAC;AAEJ,WAAO;;AAGT,UAAO,EACL,eAAe;IAMb,YAAY,OAAO,SAIb;KACJ,MAAM,MAAM,MAAM,OAA4B,uBAAuB;MACnE,QAAQ;MACR,MAAM;MACP,CAAC;AACF,SAAI,IAAI,MAAM,OACZ,aAAY,IAAI;MACd,SAAS,KAAK;MACd,gBAAgB,KAAK;MACrB,cAAc;MACf,CAAC;AAEJ,YAAO;;IAMT,YAAY,OAAO,gBAAyB;AAC1C,YAAO,eAAe,YAAY;;IAMpC,cAAc,OAAO,gBAAwB;KAC3C,MAAM,MAAM,MAAM,OAChB,yBACA;MACE,QAAQ;MACR,MAAM,EAAE,aAAa;MACtB,CACF;AACD,SAAI,IAAI,MAAM,OACZ,OAAM,eAAe,YAAY;AAEnC,YAAO;;IAEV,EACF;;EAGH,cAAc;EACf"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from 'better-auth/client';\nimport { atom } from 'nanostores';\n\n// import { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type { Consent, cookieConsentPlugin } from './index';\n\n/**\n * Client-side consent state, kept in sync with the server.\n */\nexport interface ConsentState<TConsent extends Consent = Consent> {\n consent: TConsent | null;\n consentVersion: string | null;\n versionMatch: boolean;\n}\n\n/**\n * Client plugin for cookie consent management.\n *\n * @typeParam TConsent - The consent shape, typically `z.infer<typeof yourConsentSchema>`.\n *\n * @example\n * ```ts\n * import { defaultConsentSchema } from 'better-auth-cookie-consent';\n * import { cookieConsentClient } from 'better-auth-cookie-consent/client';\n * import type { z } from 'zod';\n *\n * const authClient = createAuthClient({\n * plugins: [cookieConsentClient<z.infer<typeof defaultConsentSchema>>()],\n * });\n * ```\n */\nexport const cookieConsentClient = <TConsent extends Consent = Consent>() => {\n return {\n id: 'cookie-consent',\n $InferServerPlugin: {} as ReturnType<typeof cookieConsentPlugin>,\n\n getAtoms($fetch) {\n const $consent = atom<ConsentState<TConsent>>({\n consent: null,\n consentVersion: null,\n versionMatch: false,\n });\n return { $consent };\n },\n\n getActions($fetch, $store) {\n const consentAtom = $store.atoms.$consent as ReturnType<typeof atom<ConsentState<TConsent>>>;\n\n async function syncFromServer(anonymousId?: string) {\n let path = '/cookie-consent/get';\n if (anonymousId) {\n const params = new URLSearchParams({ anonymousId });\n path = `${path}?${params.toString()}`;\n }\n const res = await $fetch<{\n consent: {\n id: string;\n userId?: string | null;\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n timestamp: string;\n } | null;\n versionMatch: boolean;\n }>(`${path}`, { method: 'GET' });\n if (res.data) {\n consentAtom.set({\n consent: res.data.consent?.consent ?? null,\n consentVersion: res.data.consent?.consentVersion ?? null,\n versionMatch: res.data.versionMatch,\n });\n }\n return res;\n }\n\n return {\n cookieConsent: {\n /**\n * Set consent preferences on the server.\n * Also used for accept-all / reject-all by passing the\n * appropriate consent object (all `true` or all `false`).\n */\n setConsent: async (data: {\n anonymousId: string;\n consent: TConsent;\n consentVersion: string;\n }) => {\n const res = await $fetch<{ status: boolean }>('/cookie-consent/set', {\n method: 'POST',\n body: data,\n });\n if (res.data?.status) {\n consentAtom.set({\n consent: data.consent,\n consentVersion: data.consentVersion,\n versionMatch: true,\n });\n }\n return res;\n },\n\n /**\n * Retrieve consent from the server.\n */\n getConsent: async (anonymousId?: string) => {\n return syncFromServer(anonymousId);\n },\n\n /**\n * Merge anonymous consent into the authenticated user's record.\n */\n mergeConsent: async (anonymousId: string) => {\n const res = await $fetch<{ status: boolean; merged: boolean }>(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: { anonymousId },\n },\n );\n if (res.data?.merged) {\n await syncFromServer(anonymousId);\n }\n return res;\n },\n },\n };\n },\n\n // $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,MAAa,4BAAgE;AAC3E,QAAO;EACL,IAAI;EACJ,oBAAoB,EAAE;EAEtB,SAAS,QAAQ;AAMf,UAAO,EAAE,UALQ,KAA6B;IAC5C,SAAS;IACT,gBAAgB;IAChB,cAAc;IACf,CAAC,EACiB;;EAGrB,WAAW,QAAQ,QAAQ;GACzB,MAAM,cAAc,OAAO,MAAM;GAEjC,eAAe,eAAe,aAAsB;IAClD,IAAI,OAAO;AACX,QAAI,aAAa;KACf,MAAM,SAAS,IAAI,gBAAgB,EAAE,aAAa,CAAC;AACnD,YAAO,GAAG,KAAK,GAAG,OAAO,UAAU;;IAErC,MAAM,MAAM,MAAM,OAUf,GAAG,QAAQ,EAAE,QAAQ,OAAO,CAAC;AAChC,QAAI,IAAI,KACN,aAAY,IAAI;KACd,SAAS,IAAI,KAAK,SAAS,WAAW;KACtC,gBAAgB,IAAI,KAAK,SAAS,kBAAkB;KACpD,cAAc,IAAI,KAAK;KACxB,CAAC;AAEJ,WAAO;;AAGT,UAAO,EACL,eAAe;IAMb,YAAY,OAAO,SAIb;KACJ,MAAM,MAAM,MAAM,OAA4B,uBAAuB;MACnE,QAAQ;MACR,MAAM;MACP,CAAC;AACF,SAAI,IAAI,MAAM,OACZ,aAAY,IAAI;MACd,SAAS,KAAK;MACd,gBAAgB,KAAK;MACrB,cAAc;MACf,CAAC;AAEJ,YAAO;;IAMT,YAAY,OAAO,gBAAyB;AAC1C,YAAO,eAAe,YAAY;;IAMpC,cAAc,OAAO,gBAAwB;KAC3C,MAAM,MAAM,MAAM,OAChB,yBACA;MACE,QAAQ;MACR,MAAM,EAAE,aAAa;MACtB,CACF;AACD,SAAI,IAAI,MAAM,OACZ,OAAM,eAAe,YAAY;AAEnC,YAAO;;IAEV,EACF;;EAIJ"}
@@ -267,4 +267,4 @@ declare function getConsentFromCtx(ctx: {
267
267
  declare function hasConsent(ctx: Parameters<typeof getConsentFromCtx>[0], category: string): Promise<boolean>;
268
268
  //#endregion
269
269
  export { hasConsent as a, CookieConsentOptions as c, getConsentFromCtx as i, CookieConsentPayload as l, cookieConsentPlugin as n, Consent as o, defaultConsentSchema as r, ConsentSchemaModel as s, ANONYMOUS_ID_COOKIE as t, CookieConsentRecord as u };
270
- //# sourceMappingURL=index-SuR6zFkq.d.mts.map
270
+ //# sourceMappingURL=index-B8ovmV25.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as hasConsent, c as CookieConsentOptions, i as getConsentFromCtx, l as CookieConsentPayload, n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema, s as ConsentSchemaModel, t as ANONYMOUS_ID_COOKIE, u as CookieConsentRecord } from "./index-SuR6zFkq.mjs";
1
+ import { a as hasConsent, c as CookieConsentOptions, i as getConsentFromCtx, l as CookieConsentPayload, n as cookieConsentPlugin, o as Consent, r as defaultConsentSchema, s as ConsentSchemaModel, t as ANONYMOUS_ID_COOKIE, u as CookieConsentRecord } from "./index-B8ovmV25.mjs";
2
2
  export { ANONYMOUS_ID_COOKIE, Consent, ConsentSchemaModel, CookieConsentOptions, CookieConsentPayload, CookieConsentRecord, cookieConsentPlugin, defaultConsentSchema, getConsentFromCtx, hasConsent };
package/dist/index.mjs CHANGED
@@ -1,8 +1,16 @@
1
- import { t as COOKIE_CONSENT_ERROR_CODES } from "./error-codes-D3VdAX9J.mjs";
2
1
  import { APIError, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx } from "better-auth/api";
3
2
  import * as z from "zod";
4
- import { BASE_ERROR_CODES } from "better-auth";
3
+ import { BASE_ERROR_CODES, defineErrorCodes } from "better-auth";
5
4
  import { mergeSchema } from "better-auth/db";
5
+ //#region src/error-codes.ts
6
+ const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({
7
+ MISSING_ANONYMOUS_ID: "Anonymous ID is required",
8
+ CONSENT_NOT_FOUND: "Cookie consent record not found",
9
+ INVALID_CONSENT: "Consent must be a non-empty object with boolean values",
10
+ VERSION_MISMATCH: "Consent version is outdated and must be renewed",
11
+ AUTHENTICATION_REQUIRED: "Authentication is required for this operation"
12
+ });
13
+ //#endregion
6
14
  //#region src/routes.ts
7
15
  const setConsentSchema = z.object({
8
16
  anonymousId: z.string().meta({ description: "Anonymous identifier for unauthenticated users" }),
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import {\n BASE_ERROR_CODES,\n type BetterAuthOptions,\n type DBAdapter,\n type InternalLogger,\n} from 'better-auth';\nimport { APIError, createAuthEndpoint, getSessionFromCtx } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type {\n Consent,\n CookieConsentOptions,\n CookieConsentPayload,\n CookieConsentRecord,\n} from './type';\n\nconst setConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier for unauthenticated users',\n }),\n consent: z.record(z.string(), z.boolean()).meta({\n description: 'Consent preferences as category-boolean pairs',\n }),\n consentVersion: z.string().meta({\n description: 'Version of the consent policy',\n }),\n});\n\nconst getConsentSchema = z.object({\n anonymousId: z.string().optional().meta({\n description: 'Anonymous identifier fallback when no session exists',\n }),\n});\n\nconst mergeConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier to merge consent from',\n }),\n});\n\nexport const setConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/set',\n {\n method: 'POST',\n body: setConsentSchema,\n },\n async (ctx) => {\n const { anonymousId, consent, consentVersion } = ctx.body;\n\n if (!anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n if (!consent || Object.keys(consent).length === 0) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n const validatedConsent = validateConsent(options, consent, ctx.context.logger);\n\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n\n // Look for existing record by userId or anonymousId\n const existing = await findConsentRecord(ctx, userId, anonymousId);\n\n const consentJson = JSON.stringify(validatedConsent);\n const now = new Date();\n\n if (existing) {\n await ctx.context.adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: existing.id }],\n update: {\n userId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n } else {\n await ctx.context.adapter.create<CookieConsentPayload, CookieConsentRecord>({\n model: 'cookieConsent',\n data: {\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n }\n\n if (options.onConsentChange) {\n const record =\n (await findConsentRecord(ctx, userId, anonymousId)) ??\n ({\n id: '',\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n } as CookieConsentRecord);\n\n await ctx.context.runInBackgroundOrAwait(\n options.onConsentChange({ consent: record }, ctx.request),\n );\n }\n\n return ctx.json({ status: true });\n },\n );\n\nexport const getConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/get',\n {\n method: 'GET',\n query: getConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (!userId && !anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n const record = await findConsentRecord(ctx, userId, anonymousId);\n\n if (!record) {\n return ctx.json({ consent: null, versionMatch: false });\n }\n\n const currentVersion = options.consentVersion ?? 'v1';\n const versionMatch = record.consentVersion === currentVersion;\n\n return ctx.json({\n consent: {\n id: record.id,\n userId: record.userId,\n anonymousId: record.anonymousId,\n consent: JSON.parse(record.consent) as Consent,\n consentVersion: record.consentVersion,\n timestamp: record.timestamp,\n },\n versionMatch,\n });\n },\n );\n\nexport const mergeConsent = <O extends CookieConsentOptions>(_options: O) =>\n createAuthEndpoint(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: mergeConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id;\n\n if (!userId) {\n throw APIError.from('UNAUTHORIZED', COOKIE_CONSENT_ERROR_CODES.AUTHENTICATION_REQUIRED);\n }\n\n const merged = await mergeAnonymousConsentToUser(\n ctx.context.adapter,\n userId,\n ctx.body.anonymousId,\n );\n return ctx.json({ status: true, merged });\n },\n );\n\n/**\n * Merge anonymous consent into a user's record.\n * Shared between the merge endpoint and the sign-in/sign-up hook.\n *\n * @returns `true` if consent was merged, `false` if no anonymous record was found.\n */\nexport async function mergeAnonymousConsentToUser(\n adapter: DBAdapter<BetterAuthOptions>,\n userId: string,\n anonymousId: string,\n): Promise<boolean> {\n const anonymousRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n\n if (!anonymousRecord || anonymousRecord.userId) return false;\n // ^ Skip if record doesn't exist or is already linked to a user (already merged)\n\n const userRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n\n if (userRecord) {\n // User already has consent; update with anonymous data\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: userRecord.id }],\n update: {\n consent: anonymousRecord.consent,\n consentVersion: anonymousRecord.consentVersion,\n anonymousId,\n timestamp: new Date(),\n },\n });\n await adapter.delete({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n });\n } else {\n // Attach anonymous consent to user\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n update: { userId },\n });\n }\n\n return true;\n}\n\n/**\n * Find a consent record by userId (preferred) or anonymousId fallback.\n */\nasync function findConsentRecord(\n ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n };\n },\n userId: string | null | undefined,\n anonymousId: string | undefined,\n): Promise<CookieConsentRecord | null> {\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Validate the consent object against the configured validation schema.\n * Returns the validated consent or the original consent when no schema is set.\n */\nfunction validateConsent(\n options: CookieConsentOptions,\n consent: Consent,\n logger: InternalLogger,\n): Consent {\n if (!options.consent?.validationSchema) {\n return consent;\n }\n const validationResult = options.consent.validationSchema['~standard'].validate(consent);\n\n if (validationResult instanceof Promise) {\n throw APIError.from('INTERNAL_SERVER_ERROR', BASE_ERROR_CODES.ASYNC_VALIDATION_NOT_SUPPORTED);\n }\n\n if (validationResult.issues) {\n logger.error('Invalid consent', validationResult.issues);\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n return validationResult.value as Consent;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { CookieConsentOptions } from './type';\n\nexport const cookieConsent = {\n cookieConsent: {\n fields: {\n userId: {\n type: 'string',\n required: false,\n references: {\n model: 'user',\n field: 'id',\n },\n },\n anonymousId: {\n type: 'string',\n required: true,\n },\n consent: {\n type: 'string',\n required: true,\n },\n consentVersion: {\n type: 'string',\n required: true,\n },\n timestamp: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends CookieConsentOptions>(options: O) => {\n return mergeSchema(cookieConsent, options.schema);\n};\n","import type { BetterAuthOptions, BetterAuthPlugin, DBAdapter } from 'better-auth';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport { getConsent, mergeAnonymousConsentToUser, mergeConsent, setConsent } from './routes';\nimport { getSchema } from './schema';\nimport type {\n Consent,\n ConsentSchemaModel,\n CookieConsentOptions,\n CookieConsentRecord,\n} from './type';\n\n/**\n * Cookie name used to store the anonymous consent ID.\n * The client sets this cookie so the server-side hook can\n * read it during sign-in to auto-merge anonymous consent.\n */\nexport const ANONYMOUS_ID_COOKIE = 'cookie-consent-anon-id';\n\n/**\n * Parse a single cookie value from a raw `Cookie` header string.\n */\nfunction parseCookieValue(\n cookieHeader: string | null | undefined,\n name: string,\n): string | undefined {\n if (!cookieHeader) return undefined;\n for (const pair of cookieHeader.split('; ')) {\n const [key, ...valueParts] = pair.split('=');\n if (key === name && valueParts.length > 0) {\n const raw = valueParts.join('=');\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Preset validation schema for cookie consent.\n * Validates the standard consent categories: necessary, analytics, marketing, functional.\n * Use this with `consent.validationSchema` in the plugin options.\n *\n * @example\n * ```ts\n * cookieConsentPlugin({\n * consentVersion: 'v1',\n * consent: { validationSchema: defaultConsentSchema },\n * })\n * ```\n */\nexport const defaultConsentSchema = z.object({\n necessary: z.boolean(),\n analytics: z.boolean(),\n marketing: z.boolean(),\n functional: z.boolean(),\n}) satisfies ConsentSchemaModel;\n\nexport const cookieConsentPlugin = <O extends CookieConsentOptions>(options: O = {} as O) => {\n return {\n id: 'cookie-consent',\n schema: getSchema(options),\n endpoints: {\n setConsent: setConsent(options),\n getConsent: getConsent(options),\n mergeConsent: mergeConsent(options),\n },\n hooks: {\n after: [\n {\n // After sign-in or sign-up (any method), merge anonymous consent to user.\n // Uses prefix matching to cover all auth methods: email, social,\n // biometrics, passkey, phone, etc.\n matcher: (context) => {\n return (\n !!context.path &&\n (context.path.startsWith('/sign-in/') || context.path.startsWith('/sign-up/'))\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n const userId = ctx.context.newSession?.user?.id;\n if (!userId) return;\n\n // Read anonymousId from the cookie set by the client\n const anonymousId = parseCookieValue(ctx.headers?.get('cookie'), ANONYMOUS_ID_COOKIE);\n if (!anonymousId) return;\n\n await mergeAnonymousConsentToUser(ctx.context.adapter, userId, anonymousId);\n }),\n },\n ],\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/cookie-consent/set', '/cookie-consent/merge'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 10,\n },\n ],\n $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\n// ─── Helper utilities ──────────────────────────────────────────────────\n\n/**\n * Extract the parsed consent object from the endpoint context.\n * Returns `null` if no consent is available.\n */\nexport async function getConsentFromCtx(ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n session?: { user?: { id?: string } } | null;\n };\n query?: { anonymousId?: string };\n}): Promise<CookieConsentRecord | null> {\n const userId = ctx.context.session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Check if a specific consent category is granted.\n */\nexport async function hasConsent(\n ctx: Parameters<typeof getConsentFromCtx>[0],\n category: string,\n): Promise<boolean> {\n const record = await getConsentFromCtx(ctx);\n if (!record) return false;\n const parsed = JSON.parse(record.consent) as Consent;\n return parsed[category] === true;\n}\n\nexport type * from './type';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/error-codes.ts","../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({\n MISSING_ANONYMOUS_ID: 'Anonymous ID is required',\n CONSENT_NOT_FOUND: 'Cookie consent record not found',\n INVALID_CONSENT: 'Consent must be a non-empty object with boolean values',\n VERSION_MISMATCH: 'Consent version is outdated and must be renewed',\n AUTHENTICATION_REQUIRED: 'Authentication is required for this operation',\n});\n","import {\n BASE_ERROR_CODES,\n type BetterAuthOptions,\n type DBAdapter,\n type InternalLogger,\n} from 'better-auth';\nimport { APIError, createAuthEndpoint, getSessionFromCtx } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport type {\n Consent,\n CookieConsentOptions,\n CookieConsentPayload,\n CookieConsentRecord,\n} from './type';\n\nconst setConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier for unauthenticated users',\n }),\n consent: z.record(z.string(), z.boolean()).meta({\n description: 'Consent preferences as category-boolean pairs',\n }),\n consentVersion: z.string().meta({\n description: 'Version of the consent policy',\n }),\n});\n\nconst getConsentSchema = z.object({\n anonymousId: z.string().optional().meta({\n description: 'Anonymous identifier fallback when no session exists',\n }),\n});\n\nconst mergeConsentSchema = z.object({\n anonymousId: z.string().meta({\n description: 'Anonymous identifier to merge consent from',\n }),\n});\n\nexport const setConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/set',\n {\n method: 'POST',\n body: setConsentSchema,\n },\n async (ctx) => {\n const { anonymousId, consent, consentVersion } = ctx.body;\n\n if (!anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n if (!consent || Object.keys(consent).length === 0) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n const validatedConsent = validateConsent(options, consent, ctx.context.logger);\n\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n\n // Look for existing record by userId or anonymousId\n const existing = await findConsentRecord(ctx, userId, anonymousId);\n\n const consentJson = JSON.stringify(validatedConsent);\n const now = new Date();\n\n if (existing) {\n await ctx.context.adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: existing.id }],\n update: {\n userId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n } else {\n await ctx.context.adapter.create<CookieConsentPayload, CookieConsentRecord>({\n model: 'cookieConsent',\n data: {\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n },\n });\n }\n\n if (options.onConsentChange) {\n const record =\n (await findConsentRecord(ctx, userId, anonymousId)) ??\n ({\n id: '',\n userId,\n anonymousId,\n consent: consentJson,\n consentVersion,\n timestamp: now,\n } as CookieConsentRecord);\n\n await ctx.context.runInBackgroundOrAwait(\n options.onConsentChange({ consent: record }, ctx.request),\n );\n }\n\n return ctx.json({ status: true });\n },\n );\n\nexport const getConsent = <O extends CookieConsentOptions>(options: O) =>\n createAuthEndpoint(\n '/cookie-consent/get',\n {\n method: 'GET',\n query: getConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (!userId && !anonymousId) {\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.MISSING_ANONYMOUS_ID);\n }\n\n const record = await findConsentRecord(ctx, userId, anonymousId);\n\n if (!record) {\n return ctx.json({ consent: null, versionMatch: false });\n }\n\n const currentVersion = options.consentVersion ?? 'v1';\n const versionMatch = record.consentVersion === currentVersion;\n\n return ctx.json({\n consent: {\n id: record.id,\n userId: record.userId,\n anonymousId: record.anonymousId,\n consent: JSON.parse(record.consent) as Consent,\n consentVersion: record.consentVersion,\n timestamp: record.timestamp,\n },\n versionMatch,\n });\n },\n );\n\nexport const mergeConsent = <O extends CookieConsentOptions>(_options: O) =>\n createAuthEndpoint(\n '/cookie-consent/merge',\n {\n method: 'POST',\n body: mergeConsentSchema,\n },\n async (ctx) => {\n const session = await getSessionFromCtx(ctx);\n const userId = session?.user?.id;\n\n if (!userId) {\n throw APIError.from('UNAUTHORIZED', COOKIE_CONSENT_ERROR_CODES.AUTHENTICATION_REQUIRED);\n }\n\n const merged = await mergeAnonymousConsentToUser(\n ctx.context.adapter,\n userId,\n ctx.body.anonymousId,\n );\n return ctx.json({ status: true, merged });\n },\n );\n\n/**\n * Merge anonymous consent into a user's record.\n * Shared between the merge endpoint and the sign-in/sign-up hook.\n *\n * @returns `true` if consent was merged, `false` if no anonymous record was found.\n */\nexport async function mergeAnonymousConsentToUser(\n adapter: DBAdapter<BetterAuthOptions>,\n userId: string,\n anonymousId: string,\n): Promise<boolean> {\n const anonymousRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n\n if (!anonymousRecord || anonymousRecord.userId) return false;\n // ^ Skip if record doesn't exist or is already linked to a user (already merged)\n\n const userRecord = await adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n\n if (userRecord) {\n // User already has consent; update with anonymous data\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: userRecord.id }],\n update: {\n consent: anonymousRecord.consent,\n consentVersion: anonymousRecord.consentVersion,\n anonymousId,\n timestamp: new Date(),\n },\n });\n await adapter.delete({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n });\n } else {\n // Attach anonymous consent to user\n await adapter.update<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'id', value: anonymousRecord.id }],\n update: { userId },\n });\n }\n\n return true;\n}\n\n/**\n * Find a consent record by userId (preferred) or anonymousId fallback.\n */\nasync function findConsentRecord(\n ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n };\n },\n userId: string | null | undefined,\n anonymousId: string | undefined,\n): Promise<CookieConsentRecord | null> {\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Validate the consent object against the configured validation schema.\n * Returns the validated consent or the original consent when no schema is set.\n */\nfunction validateConsent(\n options: CookieConsentOptions,\n consent: Consent,\n logger: InternalLogger,\n): Consent {\n if (!options.consent?.validationSchema) {\n return consent;\n }\n const validationResult = options.consent.validationSchema['~standard'].validate(consent);\n\n if (validationResult instanceof Promise) {\n throw APIError.from('INTERNAL_SERVER_ERROR', BASE_ERROR_CODES.ASYNC_VALIDATION_NOT_SUPPORTED);\n }\n\n if (validationResult.issues) {\n logger.error('Invalid consent', validationResult.issues);\n throw APIError.from('BAD_REQUEST', COOKIE_CONSENT_ERROR_CODES.INVALID_CONSENT);\n }\n\n return validationResult.value as Consent;\n}\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { CookieConsentOptions } from './type';\n\nexport const cookieConsent = {\n cookieConsent: {\n fields: {\n userId: {\n type: 'string',\n required: false,\n references: {\n model: 'user',\n field: 'id',\n },\n },\n anonymousId: {\n type: 'string',\n required: true,\n },\n consent: {\n type: 'string',\n required: true,\n },\n consentVersion: {\n type: 'string',\n required: true,\n },\n timestamp: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends CookieConsentOptions>(options: O) => {\n return mergeSchema(cookieConsent, options.schema);\n};\n","import type { BetterAuthOptions, BetterAuthPlugin, DBAdapter } from 'better-auth';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport * as z from 'zod';\n\nimport { COOKIE_CONSENT_ERROR_CODES } from './error-codes';\nimport { getConsent, mergeAnonymousConsentToUser, mergeConsent, setConsent } from './routes';\nimport { getSchema } from './schema';\nimport type {\n Consent,\n ConsentSchemaModel,\n CookieConsentOptions,\n CookieConsentRecord,\n} from './type';\n\n/**\n * Cookie name used to store the anonymous consent ID.\n * The client sets this cookie so the server-side hook can\n * read it during sign-in to auto-merge anonymous consent.\n */\nexport const ANONYMOUS_ID_COOKIE = 'cookie-consent-anon-id';\n\n/**\n * Parse a single cookie value from a raw `Cookie` header string.\n */\nfunction parseCookieValue(\n cookieHeader: string | null | undefined,\n name: string,\n): string | undefined {\n if (!cookieHeader) return undefined;\n for (const pair of cookieHeader.split('; ')) {\n const [key, ...valueParts] = pair.split('=');\n if (key === name && valueParts.length > 0) {\n const raw = valueParts.join('=');\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Preset validation schema for cookie consent.\n * Validates the standard consent categories: necessary, analytics, marketing, functional.\n * Use this with `consent.validationSchema` in the plugin options.\n *\n * @example\n * ```ts\n * cookieConsentPlugin({\n * consentVersion: 'v1',\n * consent: { validationSchema: defaultConsentSchema },\n * })\n * ```\n */\nexport const defaultConsentSchema = z.object({\n necessary: z.boolean(),\n analytics: z.boolean(),\n marketing: z.boolean(),\n functional: z.boolean(),\n}) satisfies ConsentSchemaModel;\n\nexport const cookieConsentPlugin = <O extends CookieConsentOptions>(options: O = {} as O) => {\n return {\n id: 'cookie-consent',\n schema: getSchema(options),\n endpoints: {\n setConsent: setConsent(options),\n getConsent: getConsent(options),\n mergeConsent: mergeConsent(options),\n },\n hooks: {\n after: [\n {\n // After sign-in or sign-up (any method), merge anonymous consent to user.\n // Uses prefix matching to cover all auth methods: email, social,\n // biometrics, passkey, phone, etc.\n matcher: (context) => {\n return (\n !!context.path &&\n (context.path.startsWith('/sign-in/') || context.path.startsWith('/sign-up/'))\n );\n },\n handler: createAuthMiddleware(async (ctx) => {\n const userId = ctx.context.newSession?.user?.id;\n if (!userId) return;\n\n // Read anonymousId from the cookie set by the client\n const anonymousId = parseCookieValue(ctx.headers?.get('cookie'), ANONYMOUS_ID_COOKIE);\n if (!anonymousId) return;\n\n await mergeAnonymousConsentToUser(ctx.context.adapter, userId, anonymousId);\n }),\n },\n ],\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/cookie-consent/set', '/cookie-consent/merge'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 10,\n },\n ],\n $ERROR_CODES: COOKIE_CONSENT_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\n// ─── Helper utilities ──────────────────────────────────────────────────\n\n/**\n * Extract the parsed consent object from the endpoint context.\n * Returns `null` if no consent is available.\n */\nexport async function getConsentFromCtx(ctx: {\n context: {\n adapter: DBAdapter<BetterAuthOptions>;\n session?: { user?: { id?: string } } | null;\n };\n query?: { anonymousId?: string };\n}): Promise<CookieConsentRecord | null> {\n const userId = ctx.context.session?.user?.id ?? null;\n const anonymousId = ctx.query?.anonymousId;\n\n if (userId) {\n const byUser = await ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'userId', value: userId }],\n });\n if (byUser) return byUser;\n }\n\n if (anonymousId) {\n return ctx.context.adapter.findOne<CookieConsentRecord>({\n model: 'cookieConsent',\n where: [{ field: 'anonymousId', value: anonymousId }],\n });\n }\n\n return null;\n}\n\n/**\n * Check if a specific consent category is granted.\n */\nexport async function hasConsent(\n ctx: Parameters<typeof getConsentFromCtx>[0],\n category: string,\n): Promise<boolean> {\n const record = await getConsentFromCtx(ctx);\n if (!record) return false;\n const parsed = JSON.parse(record.consent) as Consent;\n return parsed[category] === true;\n}\n\nexport type * from './type';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "better-auth-cookie-consent",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Better Auth Cookie Consent plugin for GDPR-compliant consent management",
5
5
  "homepage": "https://github.com/marcjulian/better-auth-plugins#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/marcjulian/better-auth-plugins/issues"
8
8
  },
9
9
  "license": "MIT",
10
- "author": "Gary Großgarten <info@gary.dev>",
10
+ "author": "Marc Stammerjohann <me@marcjulian.de>",
11
11
  "repository": {
12
12
  "type": "git",
13
13
  "url": "git+https://github.com/marcjulian/better-auth-plugins.git"
@@ -1,13 +0,0 @@
1
- import { defineErrorCodes } from "better-auth";
2
- //#region src/error-codes.ts
3
- const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({
4
- MISSING_ANONYMOUS_ID: "Anonymous ID is required",
5
- CONSENT_NOT_FOUND: "Cookie consent record not found",
6
- INVALID_CONSENT: "Consent must be a non-empty object with boolean values",
7
- VERSION_MISMATCH: "Consent version is outdated and must be renewed",
8
- AUTHENTICATION_REQUIRED: "Authentication is required for this operation"
9
- });
10
- //#endregion
11
- export { COOKIE_CONSENT_ERROR_CODES as t };
12
-
13
- //# sourceMappingURL=error-codes-D3VdAX9J.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-codes-D3VdAX9J.mjs","names":[],"sources":["../src/error-codes.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const COOKIE_CONSENT_ERROR_CODES = defineErrorCodes({\n MISSING_ANONYMOUS_ID: 'Anonymous ID is required',\n CONSENT_NOT_FOUND: 'Cookie consent record not found',\n INVALID_CONSENT: 'Consent must be a non-empty object with boolean values',\n VERSION_MISMATCH: 'Consent version is outdated and must be renewed',\n AUTHENTICATION_REQUIRED: 'Authentication is required for this operation',\n});\n"],"mappings":";;AAEA,MAAa,6BAA6B,iBAAiB;CACzD,sBAAsB;CACtB,mBAAmB;CACnB,iBAAiB;CACjB,kBAAkB;CAClB,yBAAyB;CAC1B,CAAC"}