From 5a334cf826519a3ebcf5d62f2221ed0090d43b1d Mon Sep 17 00:00:00 2001 From: harmzeinstra Date: Wed, 31 Dec 2025 09:00:48 +0100 Subject: [PATCH 1/5] * fix(playground): html w3c errors --- playground/pages/login.vue | 9 ++++++--- playground/pages/protected.vue | 9 ++++++--- playground/pages/user.vue | 9 ++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/playground/pages/login.vue b/playground/pages/login.vue index 55ce65e0..7e4535bd 100644 --- a/playground/pages/login.vue +++ b/playground/pages/login.vue @@ -8,10 +8,13 @@ definePageMeta({

Log in

- Only logged out users can see this page

definePageMeta({
-  middleware: ['hanko-logged-out'],
-})
+ Only logged out users can see this page

+

+    definePageMeta({
+      middleware: ['hanko-logged-out'],
+    })
+      

You were redirected here from {{ $route.query.redirect }}, once you login, you'll be sent back automatically!

diff --git a/playground/pages/protected.vue b/playground/pages/protected.vue index 11a81886..37191e42 100644 --- a/playground/pages/protected.vue +++ b/playground/pages/protected.vue @@ -12,10 +12,13 @@ function logout() {

Protected Page

- Only logged in users can see this page

definePageMeta({
-  middleware: ['hanko-logged-in'],
-})
+ Only logged in users can see this page

+

+    definePageMeta({
+      middleware: ['hanko-logged-in'],
+    })
+      
diff --git a/playground/pages/user.vue b/playground/pages/user.vue index d1739f53..33740e91 100644 --- a/playground/pages/user.vue +++ b/playground/pages/user.vue @@ -20,10 +20,13 @@ async function tryAuthenticatedRequest() {

You are logged in!

- Only logged in users can see this page

definePageMeta({
-  middleware: ['hanko-logged-in'],
-})
+ Only logged in users can see this page

+

+    definePageMeta({
+      middleware: ['hanko-logged-in'],
+    })
+      
From c7f0add1ce632b8d8e7f334d85d8e8048f607f92 Mon Sep 17 00:00:00 2001 From: harmzeinstra Date: Wed, 31 Dec 2025 09:02:03 +0100 Subject: [PATCH 2/5] * chore: update @teamhanko/hanko-elements to 2.3.0 --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- src/module.ts | 2 ++ src/runtime/components.d.ts | 4 ++-- src/runtime/middleware/logged-in.ts | 3 +-- src/runtime/middleware/logged-out.ts | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 91d4b588..82b5b997 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "dependencies": { "@nuxt/kit": "^4.0.0", - "@teamhanko/hanko-elements": "1.5.2", + "@teamhanko/hanko-elements": "2.3.0", "defu": "^6.1.4", "jose": "^6.0.0", "ufo": "^1.5.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 221f0e8b..179a3129 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,8 +16,8 @@ importers: specifier: ^4.0.0 version: 4.2.2(magicast@0.5.1) '@teamhanko/hanko-elements': - specifier: 1.5.2 - version: 1.5.2 + specifier: 2.3.0 + version: 2.3.0 defu: specifier: ^6.1.4 version: 6.1.4 @@ -1406,11 +1406,11 @@ packages: peerDependencies: eslint: '>=9.0.0' - '@teamhanko/hanko-elements@1.5.2': - resolution: {integrity: sha512-RrOgSoKwHbysDOOffOHn3p8s/zXxY2qvWwZFQdMeEP8Qp/C/RQBwGOzax0Y2gySWflDHM2uu4+tW8dXB+mZfQg==} + '@teamhanko/hanko-elements@2.3.0': + resolution: {integrity: sha512-hMDecxL4gNNxk8mOwWxqaRvguSNPZ9QPrUVcjFP6kwZN3wHj47iD4A+htWFB+VKLWAqyDiioyx/MAM4HhPMsAQ==} - '@teamhanko/hanko-frontend-sdk@1.5.2': - resolution: {integrity: sha512-sSY2NGsJRndrC4HwxBIdog9gOGUzJ3EGKmEn7p+rzXzJaLik3ywJb25DloegPekMZQ8hwOzliF0uJh7kNxFgTA==} + '@teamhanko/hanko-frontend-sdk@2.3.0': + resolution: {integrity: sha512-rZkyWddmozkgpsC0mN+1FuxG2L7OCYsD3D040Njp1o73VyZFpvZ1rbBQv7eOI3V7qZc4rtgKc+fPizM1Hp2pCQ==} '@teamhanko/preact-custom-element@4.2.2': resolution: {integrity: sha512-3djCQr25IxQIgpidxD8J0fTQ7/hMuoa5Nmv7delQc6sX4kXgv9e/pE01cywpmGZLi69H1KUH4dmZ4cPPJQrsQQ==} @@ -5663,15 +5663,15 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.3 - '@teamhanko/hanko-elements@1.5.2': + '@teamhanko/hanko-elements@2.3.0': dependencies: '@denysvuika/preact-translate': 0.5.1(preact@10.27.2) - '@teamhanko/hanko-frontend-sdk': 1.5.2 + '@teamhanko/hanko-frontend-sdk': 2.3.0 '@teamhanko/preact-custom-element': 4.2.2(preact@10.27.2) classnames: 2.5.1 preact: 10.27.2 - '@teamhanko/hanko-frontend-sdk@1.5.2': + '@teamhanko/hanko-frontend-sdk@2.3.0': dependencies: '@types/js-cookie': 3.0.6 diff --git a/src/module.ts b/src/module.ts index 87823a5d..d9f66bb8 100644 --- a/src/module.ts +++ b/src/module.ts @@ -28,6 +28,8 @@ export interface ModuleOptions { translations?: RegisterOptions['translations'] translationsLocation?: RegisterOptions['translationsLocation'] fallbackLanguage?: RegisterOptions['fallbackLanguage'] + sessionCheckInterval?: RegisterOptions['sessionCheckInterval'] + sessionTokenLocation?: RegisterOptions['sessionTokenLocation'] } } diff --git a/src/runtime/components.d.ts b/src/runtime/components.d.ts index f4ff11c7..69357f37 100644 --- a/src/runtime/components.d.ts +++ b/src/runtime/components.d.ts @@ -7,7 +7,7 @@ declare module 'vue' { * Currently supported values are "en" for English and "de" for German. * If the value is omitted, "en" is used. */ - lang?: 'en' | 'de' | (string & {}) + lang?: 'bn' | 'de' | 'en' | 'fr' | 'it' | 'pt-BR' | 'zh' | (string & {}) /** A space-separated list of experimental features to be enabled. See experimental features. */ experimental?: string }> @@ -16,7 +16,7 @@ declare module 'vue' { * Currently supported values are "en" for English and "de" for German. * If the value is omitted, "en" is used. */ - lang?: 'en' | 'de' | (string & {}) + lang?: 'bn' | 'de' | 'en' | 'fr' | 'it' | 'pt-BR' | 'zh' | (string & {}) }> HankoEvents: DefineComponent> } diff --git a/src/runtime/middleware/logged-in.ts b/src/runtime/middleware/logged-in.ts index bd79eed1..d40e5eb0 100644 --- a/src/runtime/middleware/logged-in.ts +++ b/src/runtime/middleware/logged-in.ts @@ -1,6 +1,5 @@ import { withQuery } from 'ufo' import { defineNuxtRouteMiddleware, navigateTo, useRouter, useAppConfig, useHanko, useRequestEvent } from '#imports' -import type {} from 'nuxt/app' export default defineNuxtRouteMiddleware(async (to) => { const redirects = useAppConfig().hanko.redirects @@ -16,7 +15,7 @@ export default defineNuxtRouteMiddleware(async (to) => { const hanko = useHanko()! - if (!(await hanko.user.getCurrent().catch(() => null)) && to.path !== redirects.login) { + if (!(await hanko.getUser().catch(() => null)) && to.path !== redirects.login) { return navigateTo(withQuery(redirects.login, { redirect: to.path })) } diff --git a/src/runtime/middleware/logged-out.ts b/src/runtime/middleware/logged-out.ts index e569ca6e..fac59c54 100644 --- a/src/runtime/middleware/logged-out.ts +++ b/src/runtime/middleware/logged-out.ts @@ -14,7 +14,7 @@ export default defineNuxtRouteMiddleware(async (to) => { const hanko = useHanko()! - if ((await hanko.user.getCurrent().catch(() => null)) && to.path !== redirects.home) { + if ((await hanko.getUser().catch(() => null)) && to.path !== redirects.home) { return navigateTo(redirects.home) } From 49f83fcb4a18676ce384d2fdcef9f3e985a9b835 Mon Sep 17 00:00:00 2001 From: harmzeinstra Date: Wed, 31 Dec 2025 09:03:17 +0100 Subject: [PATCH 3/5] * feat(composables): useHanko only uses one instance of Hanko to prevent multiple instances that poll /session/validation --- src/runtime/composables/index.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/runtime/composables/index.ts b/src/runtime/composables/index.ts index 5dae66cd..56b9a59e 100644 --- a/src/runtime/composables/index.ts +++ b/src/runtime/composables/index.ts @@ -1,5 +1,5 @@ import { Hanko } from '@teamhanko/hanko-elements' -import { useRuntimeConfig } from '#imports' +import { useRuntimeConfig, useState, toValue } from '#imports' /** * This composable returns a Hanko instance. @@ -10,12 +10,17 @@ export function useHanko() { if (import.meta.server) { return null } - const hankoConfig = useRuntimeConfig().public.hanko - return new Hanko(hankoConfig.apiURL, { - cookieName: hankoConfig.cookieName, - localStorageKey: hankoConfig.storageKey, - cookieSameSite: hankoConfig.cookieSameSite, - cookieDomain: hankoConfig.cookieDomain, - }) + + const hanko = useState( + 'single-hanko-instance', + () => + new Hanko(hankoConfig.apiURL, { + cookieName: hankoConfig.cookieName, + localStorageKey: hankoConfig.storageKey, + cookieSameSite: hankoConfig.cookieSameSite, + cookieDomain: hankoConfig.cookieDomain, + }), + ) + return toValue(hanko) } From e8d584fdfbb66fc2e4204433c1687a39fb58663b Mon Sep 17 00:00:00 2001 From: harmzeinstra Date: Mon, 5 Jan 2026 11:41:07 +0100 Subject: [PATCH 4/5] * use `nuxtApp.provide` instead of `useState` for singleton pattern for Hanko instance --- playground/pages/about.vue | 2 +- playground/pages/index.vue | 2 +- playground/pages/protected.vue | 7 +++++-- playground/pages/user.vue | 4 ++-- src/runtime/composables/index.ts | 17 ++--------------- src/runtime/plugins/components.client.ts | 22 +++++++++++++++++++++- 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/playground/pages/about.vue b/playground/pages/about.vue index c288fdb5..b9984822 100644 --- a/playground/pages/about.vue +++ b/playground/pages/about.vue @@ -2,7 +2,7 @@

About

- This page is accesible to both logged in and logged out users. No middleware is defined for + This page is accessible to both logged in and logged out users. No middleware is defined for this page.

diff --git a/playground/pages/index.vue b/playground/pages/index.vue index eb514a7e..57b5b341 100644 --- a/playground/pages/index.vue +++ b/playground/pages/index.vue @@ -4,7 +4,7 @@

Home

- This page is accesible to both logged in and logged out users. No middleware is defined for + This page is accessible to both logged in and logged out users. No middleware is defined for this page. Use the navigation above to navigate to other pages.

diff --git a/playground/pages/protected.vue b/playground/pages/protected.vue index 37191e42..ec1cf788 100644 --- a/playground/pages/protected.vue +++ b/playground/pages/protected.vue @@ -4,7 +4,7 @@ definePageMeta({ }) const hanko = useHanko() function logout() { - hanko!.user.logout() + hanko!.logout() } @@ -20,7 +20,10 @@ function logout() { }) +
diff --git a/playground/pages/user.vue b/playground/pages/user.vue index 33740e91..e81f05c9 100644 --- a/playground/pages/user.vue +++ b/playground/pages/user.vue @@ -3,9 +3,9 @@ definePageMeta({ middleware: ['hanko-logged-in'], }) -const hanko = useHanko() +const { $hanko } = useNuxtApp() function logout() { - hanko!.user.logout() + $hanko!.logout() } const result = ref() diff --git a/src/runtime/composables/index.ts b/src/runtime/composables/index.ts index 56b9a59e..10d7d041 100644 --- a/src/runtime/composables/index.ts +++ b/src/runtime/composables/index.ts @@ -1,5 +1,4 @@ -import { Hanko } from '@teamhanko/hanko-elements' -import { useRuntimeConfig, useState, toValue } from '#imports' +import { useNuxtApp } from '#app' /** * This composable returns a Hanko instance. @@ -10,17 +9,5 @@ export function useHanko() { if (import.meta.server) { return null } - const hankoConfig = useRuntimeConfig().public.hanko - - const hanko = useState( - 'single-hanko-instance', - () => - new Hanko(hankoConfig.apiURL, { - cookieName: hankoConfig.cookieName, - localStorageKey: hankoConfig.storageKey, - cookieSameSite: hankoConfig.cookieSameSite, - cookieDomain: hankoConfig.cookieDomain, - }), - ) - return toValue(hanko) + return useNuxtApp().$hanko } diff --git a/src/runtime/plugins/components.client.ts b/src/runtime/plugins/components.client.ts index 878abb62..8db1162c 100644 --- a/src/runtime/plugins/components.client.ts +++ b/src/runtime/plugins/components.client.ts @@ -1,6 +1,18 @@ -import { register } from '@teamhanko/hanko-elements' +import { Hanko, register } from '@teamhanko/hanko-elements' import { defineNuxtPlugin, useRuntimeConfig } from '#imports' +declare module '#app' { + interface NuxtApp { + $hanko: InstanceType | null + } +} + +declare module 'vue' { + interface ComponentCustomProperties { + $hanko: InstanceType | null + } +} + export default defineNuxtPlugin((nuxtApp) => { const config = useRuntimeConfig() nuxtApp.hook('app:mounted', async () => { @@ -13,4 +25,12 @@ export default defineNuxtPlugin((nuxtApp) => { ...config.public.hanko.components, }) }) + + const hankoConfig = useRuntimeConfig().public.hanko + nuxtApp.provide('hanko', new Hanko(hankoConfig.apiURL, { + cookieName: hankoConfig.cookieName, + localStorageKey: hankoConfig.storageKey, + cookieSameSite: hankoConfig.cookieSameSite, + cookieDomain: hankoConfig.cookieDomain, + })) }) From df09451b6cb48af7e34fa97913f2b4d2a00b5444 Mon Sep 17 00:00:00 2001 From: harmzeinstra Date: Mon, 5 Jan 2026 11:51:53 +0100 Subject: [PATCH 5/5] docs: update README to clarify `useHanko` and `$hanko` usage and improve wording --- README.md | 4 ++-- src/runtime/middleware/logged-in.ts | 4 ++-- src/runtime/middleware/logged-out.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2afc2dae..762facfc 100644 --- a/README.md +++ b/README.md @@ -83,11 +83,11 @@ You can also create your own middleware for full control. ### Auto-imports -`useHanko` is exposed in the Vue part of your app to allow you direct access to the Hanko API. You can access the current user and much more. **Note**: It will return `null` on the server. +`useHanko` and `$hanko` is exposed in the Vue part of your app to allow you direct access to the Hanko API. You can access the current user and much more. **Note**: It will return `null` on the server. ### Server utilities -By default you can access a verified JWT context on `event.context.hanko`. (It will be undefined if the user is not logged in.) If you want to handle this yourself you can set `augmentContext: false`. +By default, you can access a verified JWT context on `event.context.hanko`. (It will be undefined if the user is not logged in.) If you want to handle this yourself you can set `augmentContext: false`. `verifyHankoEvent` is exposed in the Nitro part of your app to expose the underlying utility used to create `event.context.hanko` if you want to handle things manually. diff --git a/src/runtime/middleware/logged-in.ts b/src/runtime/middleware/logged-in.ts index d40e5eb0..96be4f27 100644 --- a/src/runtime/middleware/logged-in.ts +++ b/src/runtime/middleware/logged-in.ts @@ -1,5 +1,5 @@ import { withQuery } from 'ufo' -import { defineNuxtRouteMiddleware, navigateTo, useRouter, useAppConfig, useHanko, useRequestEvent } from '#imports' +import { defineNuxtRouteMiddleware, navigateTo, useRouter, useAppConfig, useNuxtApp, useRequestEvent } from '#imports' export default defineNuxtRouteMiddleware(async (to) => { const redirects = useAppConfig().hanko.redirects @@ -13,7 +13,7 @@ export default defineNuxtRouteMiddleware(async (to) => { return } - const hanko = useHanko()! + const hanko = useNuxtApp().$hanko! if (!(await hanko.getUser().catch(() => null)) && to.path !== redirects.login) { return navigateTo(withQuery(redirects.login, { redirect: to.path })) diff --git a/src/runtime/middleware/logged-out.ts b/src/runtime/middleware/logged-out.ts index fac59c54..f4947e42 100644 --- a/src/runtime/middleware/logged-out.ts +++ b/src/runtime/middleware/logged-out.ts @@ -1,4 +1,4 @@ -import { defineNuxtRouteMiddleware, navigateTo, useRouter, useAppConfig, useHanko, useRequestEvent } from '#imports' +import { defineNuxtRouteMiddleware, navigateTo, useRouter, useAppConfig, useNuxtApp, useRequestEvent } from '#imports' export default defineNuxtRouteMiddleware(async (to) => { const redirects = useAppConfig().hanko.redirects @@ -12,7 +12,7 @@ export default defineNuxtRouteMiddleware(async (to) => { return } - const hanko = useHanko()! + const hanko = useNuxtApp().$hanko! if ((await hanko.getUser().catch(() => null)) && to.path !== redirects.home) { return navigateTo(redirects.home)