nuxt-feathers-zod 6.4.73 → 6.4.98
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 +32 -0
- package/README.md +49 -4
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -1
- package/dist/runtime/client.d.ts +9 -0
- package/dist/runtime/client.js +3 -0
- package/dist/runtime/composables/useAuth.d.ts +7 -6
- package/dist/runtime/composables/useAuth.js +27 -135
- package/dist/runtime/composables/useAuthBoundFetch.d.ts +6 -0
- package/dist/runtime/composables/useAuthBoundFetch.js +86 -0
- package/dist/runtime/composables/useAuthDiagnostics.d.ts +24 -0
- package/dist/runtime/composables/useAuthDiagnostics.js +25 -0
- package/dist/runtime/composables/useAuthRuntime.d.ts +77 -0
- package/dist/runtime/composables/useAuthRuntime.js +371 -0
- package/dist/runtime/composables/useAuthenticatedRequest.d.ts +7 -0
- package/dist/runtime/composables/useAuthenticatedRequest.js +23 -0
- package/dist/runtime/composables/useKeycloakBridge.d.ts +9 -0
- package/dist/runtime/composables/useKeycloakBridge.js +44 -0
- package/dist/runtime/composables/useMongoManagementClient.d.ts +15 -0
- package/dist/runtime/composables/useMongoManagementClient.js +27 -0
- package/dist/runtime/composables/useProtectedService.d.ts +2 -0
- package/dist/runtime/composables/useProtectedService.js +49 -0
- package/dist/runtime/composables/useProtectedTool.d.ts +19 -0
- package/dist/runtime/composables/useProtectedTool.js +60 -0
- package/dist/runtime/options/index.d.ts +15 -0
- package/dist/runtime/options/index.js +17 -0
- package/dist/runtime/plugins/feathers-auth.d.ts +0 -12
- package/dist/runtime/plugins/feathers-auth.js +5 -15
- package/dist/runtime/plugins/keycloak-sso.js +35 -155
- package/dist/runtime/server.d.ts +25 -0
- package/dist/runtime/server.js +6 -0
- package/dist/runtime/stores/auth.d.ts +34 -16
- package/dist/runtime/stores/auth.js +17 -50
- package/dist/runtime/templates/client/connection.js +2 -5
- package/dist/runtime/templates/client/plugin.js +16 -27
- package/dist/runtime/templates/server/app.js +8 -2
- package/dist/runtime/templates/server/authentication.js +10 -0
- package/dist/runtime/templates/server/keycloak.js +28 -8
- package/dist/runtime/utils/config.d.ts +20 -0
- package/dist/runtime/utils/config.js +13 -0
- package/package.json +46 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## 6.4.98
|
|
2
|
+
|
|
3
|
+
- fix(dev/playground): create a self-link `playground/node_modules/nuxt-feathers-zod` so local `nuxi dev playground` resolves `nuxt-feathers-zod/*` bare imports correctly under the nested playground package scope.
|
|
4
|
+
- chore(scripts): run `playground:ensure-link` during `prepare`, `playground:dev`, and `playground:build`.
|
|
5
|
+
|
|
6
|
+
## 6.4.95
|
|
7
|
+
|
|
8
|
+
- add phase 4 auth runtime refactor helpers: `useProtectedTool()` and `useMongoManagementClient()`
|
|
9
|
+
- expose safe public runtime metadata for Mongo management basePath and routes
|
|
10
|
+
- align docs FR/EN for protected runtime tools and embedded Mongo management
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## 6.4.93
|
|
14
|
+
|
|
15
|
+
- auth runtime phase 2: add `useAuthenticatedRequest()` and `useProtectedService()`
|
|
16
|
+
- auth diagnostics: add `useAuthDiagnostics()` and `getStateSnapshot()`
|
|
17
|
+
- generated client plugin now synchronizes remote auth results with the unified auth runtime
|
|
18
|
+
- documentation FR/EN aligned with unified auth runtime and Keycloak bridge behavior
|
|
19
|
+
|
|
1
20
|
## v6.4.57
|
|
2
21
|
|
|
3
22
|
- positionnement produit clarifié face à `@nuxtjs/supabase` (README + docs)
|
|
@@ -246,3 +265,16 @@
|
|
|
246
265
|
- devtools plume icon/theme-parent behavior preserved
|
|
247
266
|
|
|
248
267
|
- 6.4.45: remote Keycloak Option B contract (`strategy: 'sso'`, `user: loginuser`, `authenticated: true`) is now first-class in the runtime and docs; generated route middleware no longer re-runs `auth.init()` or reuses callback hashes in redirect URIs.
|
|
268
|
+
|
|
269
|
+
## 6.4.94
|
|
270
|
+
|
|
271
|
+
- auth runtime refactor phase 3: add `useAuthBoundFetch()` and auth-aware REST fetch implementation
|
|
272
|
+
- generated REST clients now use the auth-bound fetch implementation by default
|
|
273
|
+
- `useProtectedService()` retries once after `reAuthenticate()` on 401
|
|
274
|
+
- docs FR/EN updated for phase 3 runtime auth usage
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
## 6.4.96
|
|
278
|
+
- auth runtime phase 5: official Keycloak bridge helper and richer diagnostics
|
|
279
|
+
- protected helpers now validate Keycloak bearer before protected calls
|
|
280
|
+
- docs FR/EN aligned on auth runtime phases 3/4/5
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# nuxt-feathers-zod
|
|
2
2
|
|
|
3
|
-
> OSS reference snapshot: **v6.4.
|
|
3
|
+
> OSS reference snapshot: **v6.4.98** — optional Mongo management options aligned and release metadata synchronized.
|
|
4
4
|
|
|
5
5
|
[Documentation](https://vevedh.github.io/nuxt-feathers-zod/)
|
|
6
6
|
|
|
7
7
|
`nuxt-feathers-zod` is the official **Nuxt 4** module that embeds or connects to **FeathersJS v5 (Dove)** with a **CLI-first** workflow and optional **Zod-first** service generation.
|
|
8
8
|
|
|
9
|
-
Current OSS release target: **6.4.
|
|
9
|
+
Current OSS release target: **6.4.98**.
|
|
10
10
|
|
|
11
11
|
It supports two main usage patterns:
|
|
12
12
|
|
|
@@ -157,7 +157,7 @@ bunx nuxt-feathers-zod middlewares list --target nitro
|
|
|
157
157
|
bunx nuxt-feathers-zod middlewares add request-id --target nitro
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
## CLI command surface in 6.4.
|
|
160
|
+
## CLI command surface in 6.4.98
|
|
161
161
|
|
|
162
162
|
| Area | Commands |
|
|
163
163
|
|---|---|
|
|
@@ -498,7 +498,52 @@ Le dashboard de démonstration associé au module peut désormais être packagé
|
|
|
498
498
|
The Docker Edition can expose a reusable `License Center` page and feature-gating components to manage future licensed options for the `nuxt-feathers-zod` product surface.
|
|
499
499
|
|
|
500
500
|
|
|
501
|
-
## 6.4.
|
|
501
|
+
## 6.4.87 — License Center layout clarity
|
|
502
502
|
- La page /license-center côté dashboard de démonstration a été refondue pour une lecture plus claire et sans écrasement responsive.
|
|
503
503
|
- Nouveau découpage : status + quick actions + runtime summary en haut, puis onglets Overview / Features / Plans.
|
|
504
504
|
- Le breakpoint de colonnes latérales a été repoussé à 2xl pour éviter les défauts de mise en page sur desktop intermédiaire.
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
## PATCH 6.4.93 auth runtime refactor (phase 2)
|
|
508
|
+
|
|
509
|
+
- Added official protected runtime helpers: `useAuthenticatedRequest()` and `useProtectedService()`.
|
|
510
|
+
- Added `useAuthDiagnostics()` and `getStateSnapshot()` for easier auth diagnostics.
|
|
511
|
+
- Generated remote client plugin now synchronizes remote authenticate / reAuthenticate results with the unified auth runtime instead of only syncing a Pinia store.
|
|
512
|
+
- Documentation FR/EN updated around the unified auth runtime, protected requests, protected services and Keycloak bridge behavior.
|
|
513
|
+
- Recommended rule: protected NFZ tools must stop rebuilding Bearer headers manually and should consume the runtime helpers.
|
|
514
|
+
|
|
515
|
+
## PATCH 6.4.92 auth runtime refactor (phase 1)
|
|
516
|
+
|
|
517
|
+
- Unified client auth runtime added via `useAuthRuntime()`.
|
|
518
|
+
- Single source of truth for token/user/authenticated/ready/status.
|
|
519
|
+
- Keycloak SSO now syncs token and Keycloak user through a dedicated bridge flow before protected Feathers calls.
|
|
520
|
+
- Generated Keycloak bridge service now accepts token aliases and keycloak user hints, and returns `accessToken`/`authentication` in its result.
|
|
521
|
+
- Goal: eliminate auth drift between `$api`, raw Feathers client, Pinia auth store, storage, and runtime tools.
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
## Unified auth runtime helpers
|
|
526
|
+
|
|
527
|
+
`6.4.94` adds a stronger phase-3 runtime auth layer for protected REST/service flows:
|
|
528
|
+
|
|
529
|
+
- `useAuthRuntime()` remains the single auth source of truth
|
|
530
|
+
- `useAuthBoundFetch()` provides an auth-aware fetch helper with automatic bearer injection and one-shot `reAuthenticate()` retry on 401
|
|
531
|
+
- `useAuthenticatedRequest()` now delegates to this helper
|
|
532
|
+
- `useProtectedService()` retries once after `reAuthenticate()` on 401
|
|
533
|
+
- generated REST Feathers clients now use the auth-bound fetch implementation by default
|
|
534
|
+
|
|
535
|
+
This is especially useful for embedded REST mode, remote JWT mode, and Keycloak SSO bridging where a single runtime token must be reused consistently across Feathers services and protected HTTP routes.
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
## 6.4.95
|
|
539
|
+
|
|
540
|
+
- auth runtime refactor phase 4
|
|
541
|
+
- official protected tool helpers: `useProtectedTool()` and `useMongoManagementClient()`
|
|
542
|
+
- public runtime metadata for embedded Mongo management routes
|
|
543
|
+
- better alignment between protected Feathers services and protected runtime tool HTTP routes
|
|
544
|
+
|
|
545
|
+
## 6.4.96
|
|
546
|
+
|
|
547
|
+
- auth runtime refactor phase 5: official Keycloak bridge helper `useKeycloakBridge()`
|
|
548
|
+
- richer runtime diagnostics (`lastBridgeAt`, `lastEnsureReason`, `bridgePath`, `clientSync`)
|
|
549
|
+
- `useAuthBoundFetch()` and `useProtectedService()` now explicitly validate the Keycloak bearer before protected calls
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -491,7 +491,11 @@ function setAliases(options, nuxt) {
|
|
|
491
491
|
"nuxt-feathers-zod/zod$": resolver.resolve("runtime/zod/index"),
|
|
492
492
|
"nuxt-feathers-zod/options$": resolver.resolve("runtime/options"),
|
|
493
493
|
"nuxt-feathers-zod/auth-utils$": resolver.resolve("runtime/utils/auth"),
|
|
494
|
-
"nuxt-feathers-zod/config-utils$": resolver.resolve("runtime/utils/config")
|
|
494
|
+
"nuxt-feathers-zod/config-utils$": resolver.resolve("runtime/utils/config"),
|
|
495
|
+
"nuxt-feathers-zod/auth-runtime$": resolver.resolve("runtime/composables/useAuthRuntime"),
|
|
496
|
+
"nuxt-feathers-zod/auth-bound-fetch$": resolver.resolve("runtime/composables/useAuthBoundFetch"),
|
|
497
|
+
"nuxt-feathers-zod/protected-tool$": resolver.resolve("runtime/composables/useProtectedTool"),
|
|
498
|
+
"nuxt-feathers-zod/mongo-management-client$": resolver.resolve("runtime/composables/useMongoManagementClient")
|
|
495
499
|
};
|
|
496
500
|
nuxt.options.alias = defu(nuxt.options.alias, aliases);
|
|
497
501
|
if (options.client)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Application as FeathersApplication, TransportConnection } from '@feathersjs/feathers';
|
|
2
|
+
export interface ServiceTypes {
|
|
3
|
+
}
|
|
4
|
+
export interface Configuration {
|
|
5
|
+
connection: TransportConnection<ServiceTypes>;
|
|
6
|
+
}
|
|
7
|
+
export type ClientApplication = FeathersApplication<ServiceTypes, Configuration>;
|
|
8
|
+
export type FeathersClientPlugin = Parameters<ClientApplication['configure']>['0'];
|
|
9
|
+
export declare function defineFeathersClientPlugin(def: FeathersClientPlugin): FeathersClientPlugin;
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
type AuthProvider = 'keycloak' | 'local' | 'remote' | 'none';
|
|
2
1
|
export declare function useAuth(): {
|
|
3
|
-
provider: import("vue").ComputedRef<
|
|
2
|
+
provider: import("vue").ComputedRef<"none" | "local" | "remote" | "keycloak">;
|
|
4
3
|
ready: import("vue").Ref<boolean, boolean>;
|
|
4
|
+
status: import("vue").Ref<"error" | "idle" | "bootstrapping" | "authenticated" | "anonymous", "error" | "idle" | "bootstrapping" | "authenticated" | "anonymous">;
|
|
5
5
|
isAuthenticated: import("vue").ComputedRef<boolean>;
|
|
6
6
|
isSsoAuthenticated: import("vue").ComputedRef<boolean>;
|
|
7
7
|
isFeathersAuthenticated: import("vue").ComputedRef<boolean>;
|
|
8
|
-
user: import("vue").
|
|
8
|
+
user: import("vue").Ref<any, any>;
|
|
9
9
|
ssoUser: import("vue").ComputedRef<any>;
|
|
10
10
|
feathersUser: import("vue").ComputedRef<any>;
|
|
11
|
-
permissions: import("vue").
|
|
12
|
-
token: import("vue").
|
|
11
|
+
permissions: import("vue").Ref<any[], any[]>;
|
|
12
|
+
token: import("vue").Ref<string | null, string | null>;
|
|
13
13
|
ssoToken: import("vue").ComputedRef<string | null>;
|
|
14
14
|
feathersToken: import("vue").ComputedRef<string | null>;
|
|
15
15
|
init: () => Promise<void>;
|
|
16
16
|
login: (options?: any) => Promise<any>;
|
|
17
17
|
logout: (options?: any) => Promise<any>;
|
|
18
|
+
ensureReady: (reason?: string) => Promise<void>;
|
|
19
|
+
getAuthorizationHeader: () => Promise<string | null>;
|
|
18
20
|
};
|
|
19
|
-
export {};
|
|
@@ -1,155 +1,47 @@
|
|
|
1
|
-
import { useNuxtApp
|
|
2
|
-
import { computed
|
|
3
|
-
import {
|
|
4
|
-
import { buildRemoteAuthPayload } from "../utils/auth.js";
|
|
5
|
-
import { getForcedAuthProvider, getPublicClientMode, getPublicRemoteAuthConfig, hasPublicKeycloakConfig, isPublicRemoteAuthEnabled } from "../utils/config.js";
|
|
1
|
+
import { useNuxtApp } from "#imports";
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useAuthRuntime } from "./useAuthRuntime.js";
|
|
6
4
|
export function useAuth() {
|
|
5
|
+
const runtime = useAuthRuntime();
|
|
7
6
|
const nuxtApp = useNuxtApp();
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return "remote";
|
|
16
|
-
if (forced === "local")
|
|
17
|
-
return "local";
|
|
18
|
-
const cm = getPublicClientMode(pub);
|
|
19
|
-
const remoteAuth = getPublicRemoteAuthConfig(pub);
|
|
20
|
-
if (hasPublicKeycloakConfig(pub) && cm === "remote" && remoteAuth?.enabled && remoteAuth?.payloadMode === "keycloak")
|
|
21
|
-
return "keycloak";
|
|
22
|
-
if (isPublicRemoteAuthEnabled(pub))
|
|
23
|
-
return "remote";
|
|
24
|
-
if (hasPublicKeycloakConfig(pub))
|
|
25
|
-
return "keycloak";
|
|
26
|
-
if (pub?._feathers?.auth)
|
|
27
|
-
return "local";
|
|
28
|
-
return "none";
|
|
29
|
-
});
|
|
30
|
-
const ready = ref(false);
|
|
31
|
-
const keycloak = computed(() => nuxtApp.$keycloak);
|
|
32
|
-
const authStore = computed(() => {
|
|
33
|
-
try {
|
|
34
|
-
return useAuthStore();
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
const ssoUser = computed(() => {
|
|
40
|
-
const kc = keycloak.value;
|
|
41
|
-
return kc?.user ?? (kc?.tokenParsed ? { ...kc.tokenParsed, userid: kc.userid ?? kc.tokenParsed?.preferred_username } : null);
|
|
42
|
-
});
|
|
43
|
-
const feathersUser = computed(() => authStore.value?.user ?? null);
|
|
44
|
-
const ssoToken = computed(() => keycloak.value?.token?.() ?? null);
|
|
45
|
-
const feathersToken = computed(() => authStore.value?.accessToken ?? null);
|
|
46
|
-
const isSsoAuthenticated = computed(() => {
|
|
47
|
-
if (provider.value !== "keycloak")
|
|
48
|
-
return false;
|
|
49
|
-
return Boolean(keycloak.value?.authenticated);
|
|
50
|
-
});
|
|
51
|
-
const isFeathersAuthenticated = computed(() => {
|
|
52
|
-
if (provider.value === "keycloak")
|
|
53
|
-
return Boolean(authStore.value?.authenticated || keycloak.value?.authenticated);
|
|
54
|
-
if (provider.value === "local" || provider.value === "remote")
|
|
55
|
-
return Boolean(authStore.value?.authenticated);
|
|
56
|
-
return false;
|
|
57
|
-
});
|
|
58
|
-
const isAuthenticated = computed(() => {
|
|
59
|
-
if (provider.value === "keycloak")
|
|
60
|
-
return Boolean(isSsoAuthenticated.value || isFeathersAuthenticated.value);
|
|
61
|
-
if (provider.value === "local" || provider.value === "remote")
|
|
62
|
-
return Boolean(isFeathersAuthenticated.value);
|
|
63
|
-
return false;
|
|
64
|
-
});
|
|
65
|
-
const user = computed(() => {
|
|
66
|
-
if (provider.value === "keycloak")
|
|
67
|
-
return feathersUser.value ?? ssoUser.value ?? null;
|
|
68
|
-
if (provider.value === "local" || provider.value === "remote")
|
|
69
|
-
return feathersUser.value ?? null;
|
|
70
|
-
return null;
|
|
71
|
-
});
|
|
72
|
-
const permissions = computed(() => {
|
|
73
|
-
if (provider.value === "keycloak")
|
|
74
|
-
return keycloak.value?.permissions ?? [];
|
|
75
|
-
return [];
|
|
76
|
-
});
|
|
77
|
-
const token = computed(() => {
|
|
78
|
-
if (provider.value === "keycloak")
|
|
79
|
-
return feathersToken.value ?? ssoToken.value ?? null;
|
|
80
|
-
if (provider.value === "local" || provider.value === "remote")
|
|
81
|
-
return feathersToken.value ?? null;
|
|
82
|
-
return null;
|
|
83
|
-
});
|
|
7
|
+
const isSsoAuthenticated = computed(() => runtime.provider.value === "keycloak" && runtime.authenticated.value);
|
|
8
|
+
const isFeathersAuthenticated = computed(() => runtime.authenticated.value);
|
|
9
|
+
const isAuthenticated = computed(() => runtime.authenticated.value);
|
|
10
|
+
const ssoUser = computed(() => runtime.provider.value === "keycloak" ? runtime.user.value : null);
|
|
11
|
+
const feathersUser = computed(() => runtime.user.value);
|
|
12
|
+
const ssoToken = computed(() => runtime.provider.value === "keycloak" ? runtime.accessToken.value : null);
|
|
13
|
+
const feathersToken = computed(() => runtime.accessToken.value);
|
|
84
14
|
async function init() {
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
if (ready.value) {
|
|
88
|
-
if (provider.value !== "keycloak")
|
|
89
|
-
return;
|
|
90
|
-
if (!isAuthenticated.value)
|
|
91
|
-
return;
|
|
92
|
-
if (user.value)
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (provider.value === "keycloak") {
|
|
96
|
-
const kc = keycloak.value;
|
|
97
|
-
if (kc?.authenticated && typeof kc.ensureFeathersAuth === "function")
|
|
98
|
-
await kc.ensureFeathersAuth("useAuth.init").catch(() => false);
|
|
99
|
-
if (kc?.authenticated && typeof kc.whoami === "function")
|
|
100
|
-
await kc.whoami().catch(() => null);
|
|
101
|
-
}
|
|
102
|
-
if (provider.value === "local" || provider.value === "remote") {
|
|
103
|
-
const s = authStore.value;
|
|
104
|
-
if (s?.restore) {
|
|
105
|
-
await s.restore().catch(() => {
|
|
106
|
-
});
|
|
107
|
-
} else {
|
|
108
|
-
const api = nuxtApp.$api;
|
|
109
|
-
await api?.reAuthenticate?.().catch(() => {
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
ready.value = true;
|
|
15
|
+
await runtime.ensureReady();
|
|
114
16
|
}
|
|
115
17
|
async function login(options) {
|
|
116
|
-
if (provider.value === "keycloak")
|
|
117
|
-
return keycloak
|
|
118
|
-
|
|
119
|
-
return authStore.value?.authenticate?.(options);
|
|
120
|
-
if (provider.value === "remote") {
|
|
121
|
-
const pub2 = useRuntimeConfig().public;
|
|
122
|
-
const ra = getPublicRemoteAuthConfig(pub2);
|
|
123
|
-
const token2 = options?.token || options?.accessToken || options?.access_token;
|
|
124
|
-
if (!token2)
|
|
125
|
-
throw new Error("Remote login requires a token (options.token)");
|
|
126
|
-
const payload = buildRemoteAuthPayload(token2, ra);
|
|
127
|
-
return authStore.value?.authenticate?.(payload);
|
|
128
|
-
}
|
|
18
|
+
if (runtime.provider.value === "keycloak")
|
|
19
|
+
return nuxtApp.$keycloak?.login?.(options);
|
|
20
|
+
return runtime.authenticate(options);
|
|
129
21
|
}
|
|
130
22
|
async function logout(options) {
|
|
131
|
-
if (provider.value === "keycloak")
|
|
132
|
-
return keycloak
|
|
133
|
-
|
|
134
|
-
return authStore.value?.logout?.();
|
|
135
|
-
if (provider.value === "remote")
|
|
136
|
-
return authStore.value?.logout?.();
|
|
23
|
+
if (runtime.provider.value === "keycloak")
|
|
24
|
+
return nuxtApp.$keycloak?.logout?.(options);
|
|
25
|
+
return runtime.logout();
|
|
137
26
|
}
|
|
138
27
|
return {
|
|
139
|
-
provider,
|
|
140
|
-
ready,
|
|
28
|
+
provider: runtime.provider,
|
|
29
|
+
ready: runtime.ready,
|
|
30
|
+
status: runtime.status,
|
|
141
31
|
isAuthenticated,
|
|
142
32
|
isSsoAuthenticated,
|
|
143
33
|
isFeathersAuthenticated,
|
|
144
|
-
user,
|
|
34
|
+
user: runtime.user,
|
|
145
35
|
ssoUser,
|
|
146
36
|
feathersUser,
|
|
147
|
-
permissions,
|
|
148
|
-
token,
|
|
37
|
+
permissions: runtime.permissions,
|
|
38
|
+
token: runtime.accessToken,
|
|
149
39
|
ssoToken,
|
|
150
40
|
feathersToken,
|
|
151
41
|
init,
|
|
152
42
|
login,
|
|
153
|
-
logout
|
|
43
|
+
logout,
|
|
44
|
+
ensureReady: runtime.ensureReady,
|
|
45
|
+
getAuthorizationHeader: runtime.getAuthorizationHeader
|
|
154
46
|
};
|
|
155
47
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface AuthBoundOptions extends RequestInit {
|
|
2
|
+
auth?: 'required' | 'optional' | 'none';
|
|
3
|
+
retryOn401?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function useAuthBoundFetchImplementation(defaults?: Partial<AuthBoundOptions>): (input: RequestInfo | URL, init?: AuthBoundOptions) => Promise<any>;
|
|
6
|
+
export declare function useAuthBoundFetch(defaults?: Partial<AuthBoundOptions>): <T = any>(input: RequestInfo | URL, init?: AuthBoundOptions) => Promise<T>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useRequestFetch } from "#imports";
|
|
2
|
+
import { useAuthRuntime } from "./useAuthRuntime.js";
|
|
3
|
+
function mergeHeaders(input, authorization) {
|
|
4
|
+
const headers = new Headers(input || {});
|
|
5
|
+
if (authorization && !headers.has("authorization") && !headers.has("Authorization")) {
|
|
6
|
+
headers.set("Authorization", authorization);
|
|
7
|
+
}
|
|
8
|
+
return headers;
|
|
9
|
+
}
|
|
10
|
+
function toRequestInit(input, init, headers) {
|
|
11
|
+
const request = input instanceof Request ? input : null;
|
|
12
|
+
const base = request ? {
|
|
13
|
+
method: request.method,
|
|
14
|
+
body: request.body,
|
|
15
|
+
credentials: request.credentials,
|
|
16
|
+
cache: request.cache,
|
|
17
|
+
integrity: request.integrity,
|
|
18
|
+
keepalive: request.keepalive,
|
|
19
|
+
mode: request.mode,
|
|
20
|
+
redirect: request.redirect,
|
|
21
|
+
referrer: request.referrer,
|
|
22
|
+
referrerPolicy: request.referrerPolicy,
|
|
23
|
+
signal: request.signal
|
|
24
|
+
} : {};
|
|
25
|
+
const result = {
|
|
26
|
+
...base,
|
|
27
|
+
...init,
|
|
28
|
+
headers
|
|
29
|
+
};
|
|
30
|
+
delete result.auth;
|
|
31
|
+
delete result.retryOn401;
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function resolveNativeFetch() {
|
|
35
|
+
if (import.meta.server)
|
|
36
|
+
return useRequestFetch();
|
|
37
|
+
if (typeof window !== "undefined" && typeof window.fetch === "function")
|
|
38
|
+
return window.fetch.bind(window);
|
|
39
|
+
if (typeof globalThis.fetch === "function")
|
|
40
|
+
return globalThis.fetch.bind(globalThis);
|
|
41
|
+
throw new Error("[nuxt-feathers-zod] No native fetch implementation available");
|
|
42
|
+
}
|
|
43
|
+
export function useAuthBoundFetchImplementation(defaults = {}) {
|
|
44
|
+
const auth = useAuthRuntime();
|
|
45
|
+
const nativeFetch = resolveNativeFetch();
|
|
46
|
+
return async function authBoundFetch(input, init = {}) {
|
|
47
|
+
const authMode = init.auth ?? defaults.auth ?? "required";
|
|
48
|
+
const retryOn401 = init.retryOn401 ?? defaults.retryOn401 ?? true;
|
|
49
|
+
if (authMode !== "none") {
|
|
50
|
+
await auth.ensureReady("auth-bound-fetch");
|
|
51
|
+
if (auth.provider.value === "keycloak")
|
|
52
|
+
await auth.ensureValidatedBearer("auth-bound-fetch");
|
|
53
|
+
}
|
|
54
|
+
async function execute() {
|
|
55
|
+
const authorization = authMode === "none" ? null : await auth.getAuthorizationHeader();
|
|
56
|
+
const headers = mergeHeaders(init.headers, authorization);
|
|
57
|
+
return await nativeFetch(input, toRequestInit(input, init, headers));
|
|
58
|
+
}
|
|
59
|
+
let response = await execute();
|
|
60
|
+
if (authMode !== "none" && retryOn401 && response.status === 401) {
|
|
61
|
+
await auth.reAuthenticate().catch(() => null);
|
|
62
|
+
response = await execute();
|
|
63
|
+
}
|
|
64
|
+
return response;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function useAuthBoundFetch(defaults = {}) {
|
|
68
|
+
const request = useAuthBoundFetchImplementation(defaults);
|
|
69
|
+
return async function authBoundRequest(input, init = {}) {
|
|
70
|
+
const response = await request(input, init);
|
|
71
|
+
if (response.status === 204)
|
|
72
|
+
return null;
|
|
73
|
+
const contentType = response.headers.get("content-type") || "";
|
|
74
|
+
const payload = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw {
|
|
77
|
+
name: "AuthBoundFetchError",
|
|
78
|
+
message: payload?.message || response.statusText || "Request failed",
|
|
79
|
+
status: response.status,
|
|
80
|
+
statusCode: response.status,
|
|
81
|
+
data: payload
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return payload;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function useAuthDiagnostics(): import("vue").ComputedRef<{
|
|
2
|
+
provider: "none" | "local" | "remote" | "keycloak";
|
|
3
|
+
status: "error" | "idle" | "bootstrapping" | "authenticated" | "anonymous";
|
|
4
|
+
ready: boolean;
|
|
5
|
+
bootstrapping: boolean;
|
|
6
|
+
authenticated: boolean;
|
|
7
|
+
hasAccessToken: boolean;
|
|
8
|
+
tokenSource: "none" | "storage" | "authenticate" | "reauth" | "keycloak-bridge" | "keycloak-fallback" | "runtime";
|
|
9
|
+
user: any;
|
|
10
|
+
permissions: any[];
|
|
11
|
+
error: any;
|
|
12
|
+
lastSyncAt: number;
|
|
13
|
+
lastReadyAt: number;
|
|
14
|
+
lastAuthenticateAt: number;
|
|
15
|
+
lastReAuthenticateAt: number;
|
|
16
|
+
lastBridgeAt: number;
|
|
17
|
+
lastEnsureReason: string;
|
|
18
|
+
bridgePath: string;
|
|
19
|
+
clientSync: {
|
|
20
|
+
api: "error" | "idle" | "ok" | "missing";
|
|
21
|
+
client: "error" | "idle" | "ok" | "missing";
|
|
22
|
+
feathersClient: "error" | "idle" | "ok" | "missing";
|
|
23
|
+
};
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useAuthRuntime } from "./useAuthRuntime.js";
|
|
3
|
+
export function useAuthDiagnostics() {
|
|
4
|
+
const auth = useAuthRuntime();
|
|
5
|
+
return computed(() => ({
|
|
6
|
+
provider: auth.provider.value,
|
|
7
|
+
status: auth.status.value,
|
|
8
|
+
ready: auth.ready.value,
|
|
9
|
+
bootstrapping: auth.bootstrapping.value,
|
|
10
|
+
authenticated: auth.authenticated.value,
|
|
11
|
+
hasAccessToken: Boolean(auth.accessToken.value),
|
|
12
|
+
tokenSource: auth.tokenSource.value,
|
|
13
|
+
user: auth.user.value,
|
|
14
|
+
permissions: auth.permissions.value,
|
|
15
|
+
error: auth.error.value,
|
|
16
|
+
lastSyncAt: auth.lastSyncAt.value,
|
|
17
|
+
lastReadyAt: auth.lastReadyAt.value,
|
|
18
|
+
lastAuthenticateAt: auth.lastAuthenticateAt.value,
|
|
19
|
+
lastReAuthenticateAt: auth.lastReAuthenticateAt.value,
|
|
20
|
+
lastBridgeAt: auth.lastBridgeAt.value,
|
|
21
|
+
lastEnsureReason: auth.lastEnsureReason.value,
|
|
22
|
+
bridgePath: auth.bridgePath.value,
|
|
23
|
+
clientSync: auth.clientSync.value
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
type AuthProvider = 'keycloak' | 'local' | 'remote' | 'none';
|
|
2
|
+
type AuthStatus = 'idle' | 'bootstrapping' | 'authenticated' | 'anonymous' | 'error';
|
|
3
|
+
type TokenSource = 'none' | 'storage' | 'authenticate' | 'reauth' | 'keycloak-bridge' | 'keycloak-fallback' | 'runtime';
|
|
4
|
+
type AuthSyncState = 'idle' | 'ok' | 'missing' | 'error';
|
|
5
|
+
export declare function useAuthRuntime(): {
|
|
6
|
+
provider: import("vue").ComputedRef<AuthProvider>;
|
|
7
|
+
ensureReady: (reason?: string) => Promise<void>;
|
|
8
|
+
authenticate: (payload: any) => Promise<any>;
|
|
9
|
+
reAuthenticate: () => Promise<any>;
|
|
10
|
+
logout: () => Promise<void>;
|
|
11
|
+
setSession: (payload?: {
|
|
12
|
+
user?: any;
|
|
13
|
+
accessToken?: string | null;
|
|
14
|
+
authenticated?: boolean;
|
|
15
|
+
permissions?: any[];
|
|
16
|
+
error?: any;
|
|
17
|
+
}, source?: TokenSource) => Promise<void>;
|
|
18
|
+
synchronizeKeycloakSession: (reason?: string, hint?: {
|
|
19
|
+
token?: string | null;
|
|
20
|
+
user?: any;
|
|
21
|
+
tokenParsed?: any;
|
|
22
|
+
permissions?: any[];
|
|
23
|
+
loginuser?: string | null;
|
|
24
|
+
}) => Promise<boolean>;
|
|
25
|
+
ensureValidatedBearer: (reason?: string) => Promise<boolean>;
|
|
26
|
+
getAuthorizationHeader: () => Promise<string | null>;
|
|
27
|
+
getStateSnapshot: () => {
|
|
28
|
+
provider: AuthProvider;
|
|
29
|
+
status: AuthStatus;
|
|
30
|
+
ready: boolean;
|
|
31
|
+
bootstrapping: boolean;
|
|
32
|
+
authenticated: boolean;
|
|
33
|
+
accessToken: string | null;
|
|
34
|
+
user: any;
|
|
35
|
+
permissions: any[];
|
|
36
|
+
tokenSource: TokenSource;
|
|
37
|
+
error: any;
|
|
38
|
+
lastSyncAt: number;
|
|
39
|
+
lastReadyAt: number;
|
|
40
|
+
lastAuthenticateAt: number;
|
|
41
|
+
lastReAuthenticateAt: number;
|
|
42
|
+
lastBridgeAt: number;
|
|
43
|
+
lastEnsureReason: string;
|
|
44
|
+
bridgePath: string;
|
|
45
|
+
clientSync: {
|
|
46
|
+
api: AuthSyncState;
|
|
47
|
+
client: AuthSyncState;
|
|
48
|
+
feathersClient: AuthSyncState;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
status: import("vue").Ref<AuthStatus, AuthStatus>;
|
|
52
|
+
ready: import("vue").Ref<boolean, boolean>;
|
|
53
|
+
bootstrapping: import("vue").Ref<boolean, boolean>;
|
|
54
|
+
authenticated: import("vue").Ref<boolean, boolean>;
|
|
55
|
+
accessToken: import("vue").Ref<string | null, string | null>;
|
|
56
|
+
user: import("vue").Ref<any, any>;
|
|
57
|
+
permissions: import("vue").Ref<any[], any[]>;
|
|
58
|
+
tokenSource: import("vue").Ref<TokenSource, TokenSource>;
|
|
59
|
+
error: import("vue").Ref<any, any>;
|
|
60
|
+
lastSyncAt: import("vue").Ref<number, number>;
|
|
61
|
+
lastReadyAt: import("vue").Ref<number, number>;
|
|
62
|
+
lastAuthenticateAt: import("vue").Ref<number, number>;
|
|
63
|
+
lastReAuthenticateAt: import("vue").Ref<number, number>;
|
|
64
|
+
lastBridgeAt: import("vue").Ref<number, number>;
|
|
65
|
+
lastEnsureReason: import("vue").Ref<string, string>;
|
|
66
|
+
bridgePath: import("vue").Ref<string, string>;
|
|
67
|
+
clientSync: import("vue").Ref<{
|
|
68
|
+
api: AuthSyncState;
|
|
69
|
+
client: AuthSyncState;
|
|
70
|
+
feathersClient: AuthSyncState;
|
|
71
|
+
}, {
|
|
72
|
+
api: AuthSyncState;
|
|
73
|
+
client: AuthSyncState;
|
|
74
|
+
feathersClient: AuthSyncState;
|
|
75
|
+
}>;
|
|
76
|
+
};
|
|
77
|
+
export {};
|