@habityzer/nuxt-symfony-kinde-layer 2.2.0 → 2.2.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [2.2.2](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.2.1...v2.2.2) (2026-02-18)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Enhance Kinde authentication configuration and type safety ([ee59fc4](https://github.com/Habityzer/nuxt-symfony-kinde-layer/commit/ee59fc4d97832f9effcedb613c9c57773cee3d80))
7
+ * Update pnpm lockfile to use published @habityzer/nuxt-kinde-auth package ([39e9f2c](https://github.com/Habityzer/nuxt-symfony-kinde-layer/commit/39e9f2c19ffa5f731cef610bd219dc3f4ba90bf5))
8
+
9
+ ## [2.2.1](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.2.0...v2.2.1) (2026-02-13)
10
+
1
11
  # [2.2.0](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.1.4...v2.2.0) (2026-02-13)
2
12
 
3
13
 
package/README.md CHANGED
@@ -47,13 +47,13 @@ export default defineNuxtConfig({
47
47
  public: {
48
48
  apiBaseUrl: process.env.API_BASE_URL,
49
49
 
50
- // IMPORTANT: Expose auth config for middleware (must match kindeAuth below)
50
+ // Optional: Override layer defaults for auth config
51
51
  kindeAuth: {
52
52
  cookie: {
53
- prefix: 'myapp_' // Must match prefix in kindeAuth
53
+ prefix: 'myapp_' // Override default cookie prefix
54
54
  },
55
55
  middleware: {
56
- publicRoutes: ['/', '/blog', '/help']
56
+ publicRoutes: ['/', '/blog', '/help'] // Override default public routes
57
57
  }
58
58
  }
59
59
  }
@@ -61,17 +61,17 @@ export default defineNuxtConfig({
61
61
 
62
62
  // Configure Kinde authentication module
63
63
  kindeAuth: {
64
- authDomain: process.env.NUXT_KINDE_AUTH_DOMAIN,
65
- clientId: process.env.NUXT_KINDE_CLIENT_ID,
66
- clientSecret: process.env.NUXT_KINDE_CLIENT_SECRET,
67
- redirectURL: process.env.NUXT_KINDE_REDIRECT_URL,
68
- logoutRedirectURL: process.env.NUXT_KINDE_LOGOUT_REDIRECT_URL,
64
+ authDomain: process.env.KINDE_AUTH_DOMAIN,
65
+ clientId: process.env.KINDE_CLIENT_ID,
66
+ clientSecret: process.env.KINDE_CLIENT_SECRET,
67
+ redirectURL: process.env.KINDE_REDIRECT_URL,
68
+ logoutRedirectURL: process.env.KINDE_LOGOUT_REDIRECT_URL,
69
69
  postLoginRedirectURL: '/dashboard',
70
70
  cookie: {
71
- prefix: 'myapp_' // IMPORTANT: Must be unique per project to avoid cookie conflicts
71
+ prefix: 'myapp_' // IMPORTANT: Must match runtimeConfig.public.kindeAuth.cookie.prefix
72
72
  },
73
73
  middleware: {
74
- publicRoutes: ['/', '/blog', '/help'] // Must match publicRoutes in runtimeConfig.public
74
+ publicRoutes: ['/', '/blog', '/help'] // Must match runtimeConfig.public
75
75
  }
76
76
  }
77
77
  })
@@ -79,21 +79,32 @@ export default defineNuxtConfig({
79
79
 
80
80
  ### 2. Environment Variables
81
81
 
82
- Create a `.env` file:
82
+ Create a `.env` file in your project:
83
83
 
84
84
  ```bash
85
85
  # Symfony Backend
86
86
  API_BASE_URL=http://localhost:8000
87
87
 
88
- # Kinde Authentication
89
- NUXT_KINDE_AUTH_DOMAIN=https://your-domain.kinde.com
90
- NUXT_KINDE_CLIENT_ID=your-client-id
91
- NUXT_KINDE_CLIENT_SECRET=your-client-secret
92
- NUXT_KINDE_REDIRECT_URL=http://localhost:3000/api/kinde/callback
93
- NUXT_KINDE_LOGOUT_REDIRECT_URL=http://localhost:3000
94
- NUXT_KINDE_POST_LOGIN_REDIRECT_URL=/dashboard
88
+ # Kinde Authentication (required by @habityzer/nuxt-kinde-auth module)
89
+ KINDE_AUTH_DOMAIN=https://your-domain.kinde.com
90
+ KINDE_CLIENT_ID=your-client-id
91
+ KINDE_CLIENT_SECRET=your-client-secret
92
+ KINDE_REDIRECT_URL=http://localhost:3000/api/kinde/callback
93
+ KINDE_LOGOUT_REDIRECT_URL=http://localhost:3000
94
+
95
+ # Layer Configuration (optional - layer provides defaults)
96
+ NUXT_PUBLIC_AUTH_COOKIE_PREFIX=myapp_
97
+ NUXT_PUBLIC_AUTH_LOGIN_PATH=/api/kinde/login
98
+ NUXT_PUBLIC_AUTH_CLOCK_SKEW_SECONDS=300
99
+ NUXT_PUBLIC_AUTH_APP_TOKEN_PREFIX=Bearer
100
+ NUXT_PUBLIC_AUTH_E2E_TOKEN_COOKIE_NAME=kinde_token
101
+ NUXT_PUBLIC_AUTH_ID_TOKEN_NAME=id_token
102
+ NUXT_PUBLIC_AUTH_ACCESS_TOKEN_NAME=access_token
103
+ NUXT_PUBLIC_AUTH_REFRESH_TOKEN_NAME=refresh_token
95
104
  ```
96
105
 
106
+ **Note:** The layer provides sensible defaults for all `NUXT_PUBLIC_AUTH_*` variables. You only need to override them if you want different values. The `KINDE_*` variables are required.
107
+
97
108
  ### 3. Use the Auth Composable
98
109
 
99
110
  ```vue
@@ -158,9 +169,12 @@ const response = await getUsersApi()
158
169
  ### Files
159
170
 
160
171
  - `server/api/symfony/[...].ts` - Symfony API proxy with auth
172
+ - `server/middleware/auth-guard.ts` - Server-side authentication middleware
173
+ - `server/utils/auth-constants.ts` - Re-exports shared auth constants for server
161
174
  - `app/composables/useAuth.ts` - Authentication composable
162
- - `app/constants/auth.ts` - Auth constants
163
- - `app/middleware/auth.global.ts` - Global route protection
175
+ - `app/plugins/auth-guard.client.ts` - Client-side authentication guard
176
+ - `app/constants/auth.ts` - Re-exports shared auth constants for app
177
+ - `shared/auth-constants.ts` - Core authentication constants (source of truth)
164
178
 
165
179
  ## Configuration Options
166
180
 
@@ -266,16 +280,19 @@ Add these scripts to your project's `package.json`:
266
280
  pnpm install
267
281
  ```
268
282
 
269
- 2. **The project uses Husky for git hooks:**
270
- - Pre-commit: Automatically runs `pnpm lint` before each commit
271
- - Commit-msg: Validates commit message format (conventional commits)
272
-
273
- 3. **Run linter manually:**
283
+ 2. **Available scripts:**
274
284
  ```bash
275
- pnpm lint # Check for issues
276
- pnpm lint:fix # Auto-fix issues
285
+ pnpm dev # Run dev server with example env
286
+ pnpm build # Build the layer
287
+ pnpm lint # Check for linting issues
288
+ pnpm lint:fix # Auto-fix linting issues
289
+ pnpm release # Create semantic release (CI only)
277
290
  ```
278
291
 
292
+ 3. **Git hooks (via Husky):**
293
+ - Pre-commit: Automatically runs `pnpm lint` before each commit
294
+ - Commit-msg: Validates commit message format (conventional commits)
295
+
279
296
  4. **First time setup:**
280
297
  The pre-commit hook will automatically run `nuxt prepare` if needed (with placeholder environment variables).
281
298
 
@@ -354,16 +371,34 @@ If you get type mismatches between expected Hydra collections and plain arrays,
354
371
 
355
372
  ## Architecture & Design Decisions
356
373
 
357
- ### Why Constants Are Defined Inline in Server Code
374
+ ### Shared Constants Architecture
375
+
376
+ The layer uses a centralized constants file at `shared/auth-constants.ts` as the single source of truth for authentication configuration values (cookie names, token prefixes, etc.).
377
+
378
+ **Structure:**
379
+ - `shared/auth-constants.ts` - Core constants definitions
380
+ - `app/constants/auth.ts` - Re-exports for client-side code (supports `~/constants/auth` imports)
381
+ - `server/utils/auth-constants.ts` - Re-exports for server-side code (supports `#imports` and relative imports)
382
+
383
+ **Why this structure:**
384
+ - Single source of truth prevents drift between client and server values
385
+ - Re-export pattern works around Nuxt/Nitro bundling constraints
386
+ - Maintains clean import paths for consuming projects
387
+
388
+ ### Runtime Configuration
358
389
 
359
- You'll notice that auth constants (`E2E_TOKEN_COOKIE_NAME`, `APP_TOKEN_PREFIX`, `KINDE_ID_TOKEN_COOKIE_NAME`) are defined directly in the server files (`server/api/symfony/[...].ts`) rather than imported from a shared constants file.
390
+ The layer uses Nuxt's `runtimeConfig` to make authentication settings available to both server middleware and client code:
360
391
 
361
- **Reason**: Nitro's bundling process for server-side code doesn't support:
362
- - App aliases like `~` or `@` (these resolve to the consuming project's app directory, not the layer's)
363
- - Relative imports from external layers during the rollup bundling phase
364
- - The `#build` alias for accessing layer exports
392
+ **Implementation:**
393
+ 1. Constants are imported in `nuxt.config.ts` from `shared/auth-constants.ts`
394
+ 2. Environment variables override defaults (e.g., `NUXT_PUBLIC_AUTH_COOKIE_PREFIX`)
395
+ 3. Values are merged into `runtimeConfig.public.kindeAuth` for runtime access
396
+ 4. Both server middleware and client plugins read from runtime config
365
397
 
366
- **Solution**: We define these constants inline in server files while maintaining the shared `app/constants/auth.ts` for client-side code. This is a deliberate architectural choice to ensure reliable builds across all consuming projects.
398
+ This approach allows:
399
+ - Projects to override defaults via environment variables
400
+ - Type-safe access to configuration throughout the app
401
+ - Consistent behavior between development and production
367
402
 
368
403
  ### Cookie Prefix Configuration
369
404
 
@@ -1,4 +1,5 @@
1
1
  import { computed, ref, readonly } from 'vue'
2
+ import type { KindeAuthRuntimeConfig } from '../../types/kinde-auth'
2
3
  import { E2E_TOKEN_COOKIE_NAME } from '../constants/auth'
3
4
 
4
5
  const LEGACY_E2E_STORAGE_KEY = 'e2e_app_token'
@@ -110,7 +111,7 @@ export const useAuth = () => {
110
111
  // Clear auth cookies first so route middleware blocks protected pages immediately.
111
112
  if (import.meta.client) {
112
113
  const config = useRuntimeConfig()
113
- const kindeConfig = config.public.kindeAuth || {}
114
+ const kindeConfig = (config.public.kindeAuth || {}) as KindeAuthRuntimeConfig
114
115
  const cookieConfig = kindeConfig.cookie || {}
115
116
  const middlewareConfig = kindeConfig.middleware || {}
116
117
  const cookiePrefix = requireString(cookieConfig.prefix, 'kindeAuth.cookie.prefix')
@@ -1,10 +1,11 @@
1
+ import type { KindeAuthRuntimeConfig } from '../../types/kinde-auth'
2
+
1
3
  export default defineNuxtPlugin(() => {
2
4
  const router = useRouter()
3
5
  const config = useRuntimeConfig()
4
- const kindeConfig = config.public.kindeAuth || {}
6
+ const kindeConfig = (config.public.kindeAuth || {}) as KindeAuthRuntimeConfig
5
7
  const middlewareConfig = kindeConfig.middleware || {}
6
8
  const cookieConfig = kindeConfig.cookie || {}
7
- // @ts-expect-error - cookie property exists in runtime config but not in module types
8
9
  const cookiePrefix = requireString(cookieConfig.prefix, 'kindeAuth.cookie.prefix')
9
10
  const idTokenBaseName = requireString(cookieConfig.idTokenName, 'kindeAuth.cookie.idTokenName')
10
11
  const accessTokenBaseName = requireString(cookieConfig.accessTokenName, 'kindeAuth.cookie.accessTokenName')
package/nuxt.config.ts CHANGED
@@ -25,7 +25,6 @@ export default defineNuxtConfig({
25
25
  modules: [
26
26
  '@nuxt/eslint',
27
27
  '@nuxt/ui',
28
- '@nuxt/image',
29
28
  '@habityzer/nuxt-kinde-auth',
30
29
  '@pinia/nuxt'
31
30
  ],
@@ -136,15 +135,31 @@ export default defineNuxtConfig({
136
135
  pinia: {}
137
136
  })
138
137
 
138
+ // Mapping of runtime config keys to their environment variable names
139
+ const CONFIG_TO_ENV_MAP: Record<string, string> = {
140
+ 'runtimeConfig.public.kindeAuth.cookie.prefix': 'NUXT_PUBLIC_AUTH_COOKIE_PREFIX',
141
+ 'runtimeConfig.public.kindeAuth.middleware.clockSkewSeconds': 'NUXT_PUBLIC_AUTH_CLOCK_SKEW_SECONDS',
142
+ 'kindeAuth.cookie.prefix': 'NUXT_PUBLIC_AUTH_COOKIE_PREFIX',
143
+ 'kindeAuth.authDomain': 'KINDE_AUTH_DOMAIN',
144
+ 'kindeAuth.clientId': 'KINDE_CLIENT_ID',
145
+ 'kindeAuth.clientSecret': 'KINDE_CLIENT_SECRET',
146
+ 'kindeAuth.redirectURL': 'KINDE_REDIRECT_URL',
147
+ 'kindeAuth.logoutRedirectURL': 'KINDE_LOGOUT_REDIRECT_URL'
148
+ }
149
+
139
150
  function assertRequiredString(value: unknown, key: string) {
140
151
  if (typeof value !== 'string' || value.trim().length === 0) {
141
- throw new Error(`[nuxt-symfony-kinde-layer] Missing required config: ${key}`)
152
+ const envVar = CONFIG_TO_ENV_MAP[key]
153
+ const envVarHint = envVar ? `\n\n \x1b[1m\x1b[33m→ Set the environment variable:\x1b[0m \x1b[1m\x1b[36m${envVar}\x1b[0m\n \x1b[90mAdd it to your .env file (see .env.example for reference)\x1b[0m` : ''
154
+ throw new Error(`[nuxt-symfony-kinde-layer] \x1b[1m\x1b[31mMissing required config:\x1b[0m ${key}${envVarHint}`)
142
155
  }
143
156
  }
144
157
 
145
158
  function assertRequiredNumber(value: unknown, key: string) {
146
159
  if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
147
- throw new Error(`[nuxt-symfony-kinde-layer] Missing or invalid required numeric config: ${key}`)
160
+ const envVar = CONFIG_TO_ENV_MAP[key]
161
+ const envVarHint = envVar ? `\n\n \x1b[1m\x1b[33m→ Set the environment variable:\x1b[0m \x1b[1m\x1b[36m${envVar}\x1b[0m\n \x1b[90mAdd it to your .env file (see .env.example for reference)\x1b[0m` : ''
162
+ throw new Error(`[nuxt-symfony-kinde-layer] \x1b[1m\x1b[31mMissing or invalid required numeric config:\x1b[0m ${key}${envVarHint}`)
148
163
  }
149
164
  }
150
165
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@habityzer/nuxt-symfony-kinde-layer",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Shared Nuxt layer for Symfony + Kinde authentication integration",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -23,11 +23,11 @@
23
23
  "author": "Habityzer",
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
- "@habityzer/nuxt-kinde-auth": "^1.2.0",
27
- "@pinia/nuxt": "^0.11.2",
28
- "@nuxt/ui": "^4.0.1",
29
- "@nuxt/image": "^1.11.0",
26
+ "@habityzer/nuxt-kinde-auth": "^1.2.2",
30
27
  "@nuxt/eslint": "^1.9.0",
28
+ "@nuxt/image": "^1.11.0",
29
+ "@nuxt/ui": "^4.0.1",
30
+ "@pinia/nuxt": "^0.11.2",
31
31
  "@vueuse/core": "^13.9.0",
32
32
  "nuxt": "^4.1.3",
33
33
  "vue": "^3.5.22",
@@ -35,20 +35,20 @@
35
35
  },
36
36
  "devDependencies": {
37
37
  "@commitlint/config-conventional": "^19.8.1",
38
- "commitlint": "^19.8.1",
39
- "conventional-changelog-conventionalcommits": "^9.1.0",
38
+ "@habityzer/nuxt-openapi-composables": "^1.1.0",
39
+ "@iconify-json/heroicons": "^1.2.3",
40
40
  "@semantic-release/changelog": "^6.0.3",
41
41
  "@semantic-release/commit-analyzer": "^13.0.1",
42
42
  "@semantic-release/git": "^10.0.1",
43
43
  "@semantic-release/npm": "^12.0.1",
44
44
  "@semantic-release/release-notes-generator": "^14.1.0",
45
- "@habityzer/nuxt-openapi-composables": "^1.1.0",
46
- "@iconify-json/heroicons": "^1.2.3",
47
- "openapi-typescript": "^7.10.0",
48
- "typescript": "^5.9.3",
45
+ "commitlint": "^19.8.1",
46
+ "conventional-changelog-conventionalcommits": "^9.1.0",
49
47
  "eslint": "^9.37.0",
48
+ "husky": "^9.0.0",
49
+ "openapi-typescript": "^7.10.0",
50
50
  "semantic-release": "^24.2.9",
51
- "husky": "^9.0.0"
51
+ "typescript": "^5.9.3"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "nuxt": "^3.0.0 || ^4.0.0"
@@ -12,9 +12,11 @@
12
12
  * @see .cursorrules for proxy best practices
13
13
  */
14
14
 
15
+ import type { KindeAuthRuntimeConfig } from '../../../types/kinde-auth'
16
+
15
17
  export default defineEventHandler(async (event) => {
16
18
  const config = useRuntimeConfig()
17
- const kindeConfig = config.public.kindeAuth || {}
19
+ const kindeConfig = (config.public.kindeAuth || {}) as KindeAuthRuntimeConfig
18
20
  const middlewareConfig = kindeConfig.middleware || {}
19
21
  const cookieConfig = kindeConfig.cookie || {}
20
22
  const appTokenPrefix = requireString(middlewareConfig.appTokenPrefix, 'kindeAuth.middleware.appTokenPrefix')
@@ -32,7 +34,7 @@ export default defineEventHandler(async (event) => {
32
34
 
33
35
  // Check if this is a public API route (no auth required)
34
36
  const publicApiRoutes
35
- = config.public.kindeAuth?.middleware?.publicApiRoutes || []
37
+ = (config.public.kindeAuth as KindeAuthRuntimeConfig | undefined)?.middleware?.publicApiRoutes || []
36
38
  const isPublicRoute = publicApiRoutes.some((route: string) => {
37
39
  if (route.endsWith('/**')) {
38
40
  const prefix = route.slice(0, -3)
@@ -124,13 +126,13 @@ export default defineEventHandler(async (event) => {
124
126
  // Get Content-Type to determine how to handle body
125
127
  const contentType = getHeader(event, 'content-type') || ''
126
128
 
127
- let body: string | undefined
129
+ let body: string | Buffer | undefined
128
130
 
129
131
  // Handle multipart/form-data specially (preserve binary data and MIME types)
130
132
  if (contentType.includes('multipart/form-data')) {
131
133
  // For multipart/form-data, read the raw body without parsing
132
134
  // This preserves the boundary and binary data including MIME types
133
- body = await readRawBody(event, false)
135
+ body = await readRawBody(event, false) as string | Buffer | undefined
134
136
  } else if (method !== 'GET' && method !== 'HEAD') {
135
137
  // For other content types (JSON, etc.), read and parse the body
136
138
  body = await readBody(event)
@@ -6,11 +6,19 @@ declare module '@habityzer/nuxt-kinde-auth' {
6
6
  interface ModuleOptions {
7
7
  cookie?: {
8
8
  prefix?: string
9
+ idTokenName?: string
10
+ accessTokenName?: string
11
+ refreshTokenName?: string
9
12
  }
10
13
  middleware?: {
11
14
  enabled?: boolean
12
15
  global?: boolean
13
16
  publicRoutes?: string[]
17
+ publicApiRoutes?: string[]
18
+ e2eTokenCookieName?: string
19
+ appTokenPrefix?: string
20
+ clockSkewSeconds?: number
21
+ loginPath?: string
14
22
  }
15
23
  debug?: {
16
24
  enabled?: boolean
@@ -18,4 +26,25 @@ declare module '@habityzer/nuxt-kinde-auth' {
18
26
  }
19
27
  }
20
28
 
21
- export {}
29
+ /** Runtime config shape for kindeAuth (used for type-safe access in layer code) */
30
+ export interface KindeAuthRuntimeConfig {
31
+ cookie?: {
32
+ prefix?: string
33
+ idTokenName?: string
34
+ accessTokenName?: string
35
+ refreshTokenName?: string
36
+ }
37
+ middleware?: {
38
+ enabled?: boolean
39
+ global?: boolean
40
+ publicRoutes?: string[]
41
+ publicApiRoutes?: string[]
42
+ e2eTokenCookieName?: string
43
+ appTokenPrefix?: string
44
+ clockSkewSeconds?: number
45
+ loginPath?: string
46
+ }
47
+ debug?: {
48
+ enabled?: boolean
49
+ }
50
+ }