@lenne.tech/nuxt-extensions 1.0.0

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/dist/module.d.mts +10 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +126 -0
  6. package/dist/runtime/components/transition/LtTransitionFade.d.vue.ts +39 -0
  7. package/dist/runtime/components/transition/LtTransitionFade.vue +21 -0
  8. package/dist/runtime/components/transition/LtTransitionFade.vue.d.ts +39 -0
  9. package/dist/runtime/components/transition/LtTransitionFadeScale.d.vue.ts +42 -0
  10. package/dist/runtime/components/transition/LtTransitionFadeScale.vue +21 -0
  11. package/dist/runtime/components/transition/LtTransitionFadeScale.vue.d.ts +42 -0
  12. package/dist/runtime/components/transition/LtTransitionSlide.d.vue.ts +13 -0
  13. package/dist/runtime/components/transition/LtTransitionSlide.vue +16 -0
  14. package/dist/runtime/components/transition/LtTransitionSlide.vue.d.ts +13 -0
  15. package/dist/runtime/components/transition/LtTransitionSlideBottom.d.vue.ts +13 -0
  16. package/dist/runtime/components/transition/LtTransitionSlideBottom.vue +16 -0
  17. package/dist/runtime/components/transition/LtTransitionSlideBottom.vue.d.ts +13 -0
  18. package/dist/runtime/components/transition/LtTransitionSlideRevert.d.vue.ts +13 -0
  19. package/dist/runtime/components/transition/LtTransitionSlideRevert.vue +16 -0
  20. package/dist/runtime/components/transition/LtTransitionSlideRevert.vue.d.ts +13 -0
  21. package/dist/runtime/composables/auth/use-lt-auth.d.ts +32 -0
  22. package/dist/runtime/composables/auth/use-lt-auth.js +411 -0
  23. package/dist/runtime/composables/index.d.ts +5 -0
  24. package/dist/runtime/composables/index.js +5 -0
  25. package/dist/runtime/composables/use-lt-auth-client.d.ts +29 -0
  26. package/dist/runtime/composables/use-lt-auth-client.js +27 -0
  27. package/dist/runtime/composables/use-lt-file.d.ts +27 -0
  28. package/dist/runtime/composables/use-lt-file.js +60 -0
  29. package/dist/runtime/composables/use-lt-share.d.ts +32 -0
  30. package/dist/runtime/composables/use-lt-share.js +52 -0
  31. package/dist/runtime/composables/use-lt-tus-upload.d.ts +32 -0
  32. package/dist/runtime/composables/use-lt-tus-upload.js +236 -0
  33. package/dist/runtime/lib/auth-client.d.ts +831 -0
  34. package/dist/runtime/lib/auth-client.js +144 -0
  35. package/dist/runtime/lib/auth-state.d.ts +58 -0
  36. package/dist/runtime/lib/auth-state.js +131 -0
  37. package/dist/runtime/lib/index.d.ts +2 -0
  38. package/dist/runtime/lib/index.js +12 -0
  39. package/dist/runtime/locales/de.json +19 -0
  40. package/dist/runtime/locales/en.json +19 -0
  41. package/dist/runtime/plugins/auth-interceptor.client.d.ts +14 -0
  42. package/dist/runtime/plugins/auth-interceptor.client.js +88 -0
  43. package/dist/runtime/server/tsconfig.json +3 -0
  44. package/dist/runtime/types/auth.d.ts +127 -0
  45. package/dist/runtime/types/auth.js +0 -0
  46. package/dist/runtime/types/index.d.ts +3 -0
  47. package/dist/runtime/types/index.js +0 -0
  48. package/dist/runtime/types/module.d.ts +88 -0
  49. package/dist/runtime/types/module.js +0 -0
  50. package/dist/runtime/types/upload.d.ts +133 -0
  51. package/dist/runtime/types/upload.js +0 -0
  52. package/dist/runtime/utils/crypto.d.ts +40 -0
  53. package/dist/runtime/utils/crypto.js +17 -0
  54. package/dist/runtime/utils/index.d.ts +2 -0
  55. package/dist/runtime/utils/index.js +2 -0
  56. package/dist/runtime/utils/tw.d.ts +20 -0
  57. package/dist/runtime/utils/tw.js +1 -0
  58. package/dist/types.d.mts +9 -0
  59. package/package.json +99 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Fade with scale transition component
3
+ *
4
+ * Provides a combined fade and scale animation.
5
+ * Elements fade in while scaling up from 95% to 100%,
6
+ * and fade out while scaling down to 95%.
7
+ *
8
+ * Great for modal dialogs, dropdown menus, and pop-ups.
9
+ *
10
+ * @example
11
+ * ```vue
12
+ * <LtTransitionFadeScale>
13
+ * <div v-if="show">Content fades and scales</div>
14
+ * </LtTransitionFadeScale>
15
+ *
16
+ * <LtTransitionFadeScale :start-duration="200" :leave-duration="150">
17
+ * <div v-if="show">Custom timing</div>
18
+ * </LtTransitionFadeScale>
19
+ * ```
20
+ */
21
+ type __VLS_Props = {
22
+ /** Duration in ms for the leave animation (default: 100) */
23
+ leaveDuration?: `${number}` | number;
24
+ /** Duration in ms for the enter animation (default: 100) */
25
+ startDuration?: `${number}` | number;
26
+ };
27
+ declare var __VLS_7: {};
28
+ type __VLS_Slots = {} & {
29
+ default?: (props: typeof __VLS_7) => any;
30
+ };
31
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
32
+ leaveDuration: `${number}` | number;
33
+ startDuration: `${number}` | number;
34
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
36
+ declare const _default: typeof __VLS_export;
37
+ export default _default;
38
+ type __VLS_WithSlots<T, S> = T & {
39
+ new (): {
40
+ $slots: S;
41
+ };
42
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,16 @@
1
+ <script setup>
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <Transition
7
+ enter-active-class="transition ease-out duration-500"
8
+ enter-from-class="transform translate-x-full"
9
+ enter-to-class="transform translate-x-0"
10
+ leave-active-class="transition ease-in duration-500"
11
+ leave-from-class="transform translate-x-0"
12
+ leave-to-class="transform translate-x-full"
13
+ >
14
+ <slot></slot>
15
+ </Transition>
16
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,16 @@
1
+ <script setup>
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <Transition
7
+ enter-active-class="transition ease-out duration-500"
8
+ enter-from-class="transform translate-y-full"
9
+ enter-to-class="transform translate-y-0"
10
+ leave-active-class="transition ease-in duration-500"
11
+ leave-from-class="transform translate-y-0"
12
+ leave-to-class="transform translate-y-full"
13
+ >
14
+ <slot></slot>
15
+ </Transition>
16
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,16 @@
1
+ <script setup>
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <Transition
7
+ enter-active-class="transition ease-out duration-500"
8
+ enter-from-class="transform translate-x-[-100%]"
9
+ enter-to-class="transform translate-x-0"
10
+ leave-active-class="transition ease-in duration-500"
11
+ leave-from-class="transform translate-x-0"
12
+ leave-to-class="transform translate-x-[-100%]"
13
+ >
14
+ <slot></slot>
15
+ </Transition>
16
+ </template>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_7: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_7) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Better Auth composable with client-side state management
3
+ *
4
+ * This composable manages auth state using:
5
+ * 1. Primary: Session cookies (more secure, HttpOnly)
6
+ * 2. Fallback: JWT tokens (when cookies are not available/working)
7
+ *
8
+ * The auth mode is automatically detected:
9
+ * - If session cookie works -> use cookies
10
+ * - If cookies fail (401) -> switch to JWT mode
11
+ */
12
+ import type { UseLtAuthReturn } from '../../types/index.js';
13
+ /**
14
+ * Better Auth composable with Cookie/JWT dual-mode authentication
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const { user, isAuthenticated, signIn, signOut } = useLtAuth();
19
+ *
20
+ * // Login
21
+ * await signIn.email({ email, password });
22
+ *
23
+ * // Check auth status
24
+ * if (isAuthenticated.value) {
25
+ * console.log('Logged in as:', user.value?.name);
26
+ * }
27
+ *
28
+ * // Logout
29
+ * await signOut();
30
+ * ```
31
+ */
32
+ export declare function useLtAuth(): UseLtAuthReturn;
@@ -0,0 +1,411 @@
1
+ import { useNuxtApp, useRuntimeConfig, useCookie, ref, computed, watch } from "#imports";
2
+ import { ltArrayBufferToBase64Url, ltBase64UrlToUint8Array } from "../../utils/crypto.js";
3
+ import { createLtAuthClient } from "../../lib/auth-client.js";
4
+ let _authClient = null;
5
+ function getAuthClient() {
6
+ if (!_authClient) {
7
+ _authClient = createLtAuthClient();
8
+ }
9
+ return _authClient;
10
+ }
11
+ function useTranslation() {
12
+ const nuxtApp = useNuxtApp();
13
+ const i18n = nuxtApp.$i18n;
14
+ return (key, germanFallback) => {
15
+ if (!i18n?.t) {
16
+ return germanFallback;
17
+ }
18
+ return i18n.t(key);
19
+ };
20
+ }
21
+ export function useLtAuth() {
22
+ const authClient = getAuthClient();
23
+ const t = useTranslation();
24
+ const authState = useCookie("lt-auth-state", {
25
+ maxAge: 60 * 60 * 24 * 7,
26
+ // 7 days
27
+ sameSite: "lax"
28
+ });
29
+ if (import.meta.client) {
30
+ try {
31
+ const cookieStr = document.cookie.split("; ").find((row) => row.startsWith("lt-auth-state="));
32
+ if (cookieStr) {
33
+ const parts = cookieStr.split("=");
34
+ const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join("=")) : "";
35
+ if (value) {
36
+ const parsed = JSON.parse(value);
37
+ if (parsed?.user && !authState.value?.user) {
38
+ authState.value = parsed;
39
+ }
40
+ }
41
+ }
42
+ } catch {
43
+ }
44
+ }
45
+ if (import.meta.server && (authState.value === null || authState.value === void 0)) {
46
+ authState.value = { user: null, authMode: "cookie" };
47
+ }
48
+ const jwtToken = useCookie("lt-jwt-token", {
49
+ maxAge: 60 * 60 * 24 * 7,
50
+ // 7 days
51
+ sameSite: "lax"
52
+ });
53
+ const isLoading = ref(false);
54
+ const authMode = computed(() => authState.value?.authMode || "cookie");
55
+ const isJwtMode = computed(() => authMode.value === "jwt");
56
+ const user = computed(() => authState.value?.user ?? null);
57
+ const isAuthenticated = computed(() => !!user.value);
58
+ const isAdmin = computed(() => user.value?.role === "admin");
59
+ const is2FAEnabled = computed(() => user.value?.twoFactorEnabled ?? false);
60
+ function getApiBase() {
61
+ const isDev = import.meta.dev;
62
+ const runtimeConfig = useRuntimeConfig();
63
+ const config = runtimeConfig.public.ltExtensions?.auth;
64
+ const baseURL = config?.baseURL || "http://localhost:3000";
65
+ const basePath = config?.basePath || "/iam";
66
+ return isDev ? `/api${basePath}` : `${baseURL}${basePath}`;
67
+ }
68
+ function setUser(userData, mode = "cookie") {
69
+ const newState = { user: userData, authMode: mode };
70
+ authState.value = newState;
71
+ if (import.meta.client) {
72
+ const maxAge = 60 * 60 * 24 * 7;
73
+ document.cookie = `lt-auth-state=${encodeURIComponent(JSON.stringify(newState))}; path=/; max-age=${maxAge}; samesite=lax`;
74
+ }
75
+ }
76
+ function clearUser() {
77
+ const clearedState = { user: null, authMode: "cookie" };
78
+ authState.value = clearedState;
79
+ jwtToken.value = null;
80
+ if (import.meta.client) {
81
+ const maxAge = 60 * 60 * 24 * 7;
82
+ document.cookie = `lt-auth-state=${encodeURIComponent(JSON.stringify(clearedState))}; path=/; max-age=${maxAge}; samesite=lax`;
83
+ document.cookie = `lt-jwt-token=; path=/; max-age=0`;
84
+ const sessionCookieNames = ["better-auth.session_token", "better-auth.session", "__Secure-better-auth.session_token", "session_token", "session"];
85
+ for (const name of sessionCookieNames) {
86
+ document.cookie = `${name}=; path=/; max-age=0`;
87
+ document.cookie = `${name}=; path=/api; max-age=0`;
88
+ document.cookie = `${name}=; path=/api/iam; max-age=0`;
89
+ document.cookie = `${name}=; path=/iam; max-age=0`;
90
+ }
91
+ }
92
+ }
93
+ async function switchToJwtMode() {
94
+ try {
95
+ const apiBase = getApiBase();
96
+ const response = await fetch(`${apiBase}/token`, {
97
+ method: "GET",
98
+ credentials: "include"
99
+ });
100
+ if (response.ok) {
101
+ const data = await response.json();
102
+ if (data.token) {
103
+ jwtToken.value = data.token;
104
+ if (authState.value) {
105
+ authState.value = { ...authState.value, authMode: "jwt" };
106
+ }
107
+ console.debug("[LtAuth] Switched to JWT mode");
108
+ return true;
109
+ }
110
+ }
111
+ return false;
112
+ } catch {
113
+ return false;
114
+ }
115
+ }
116
+ async function refreshJwtToken() {
117
+ if (!isJwtMode.value || !jwtToken.value) return false;
118
+ return switchToJwtMode();
119
+ }
120
+ async function fetchWithAuth(url, options = {}) {
121
+ const headers = new Headers(options.headers);
122
+ if (isJwtMode.value && jwtToken.value) {
123
+ headers.set("Authorization", `Bearer ${jwtToken.value}`);
124
+ }
125
+ const response = await fetch(url, {
126
+ ...options,
127
+ headers,
128
+ // Only include credentials in cookie mode
129
+ credentials: isJwtMode.value ? "omit" : "include"
130
+ });
131
+ if (response.status === 401 && !isJwtMode.value && isAuthenticated.value) {
132
+ console.debug("[LtAuth] Cookie auth failed, attempting JWT fallback...");
133
+ const switched = await switchToJwtMode();
134
+ if (switched) {
135
+ headers.set("Authorization", `Bearer ${jwtToken.value}`);
136
+ return fetch(url, {
137
+ ...options,
138
+ headers,
139
+ credentials: "omit"
140
+ });
141
+ }
142
+ }
143
+ return response;
144
+ }
145
+ async function validateSession() {
146
+ try {
147
+ const session = authClient.useSession();
148
+ if (session.value.isPending) {
149
+ await new Promise((resolve) => {
150
+ const unwatch = watch(
151
+ () => session.value.isPending,
152
+ (isPending) => {
153
+ if (!isPending) {
154
+ unwatch();
155
+ resolve(true);
156
+ }
157
+ },
158
+ { immediate: true }
159
+ );
160
+ });
161
+ }
162
+ if (session.value.data?.user) {
163
+ setUser(session.value.data.user, "cookie");
164
+ switchToJwtMode().catch(() => {
165
+ });
166
+ return true;
167
+ }
168
+ if (authState.value?.user) {
169
+ switchToJwtMode().catch(() => {
170
+ });
171
+ return true;
172
+ }
173
+ return false;
174
+ } catch (error) {
175
+ console.debug("Session validation failed:", error);
176
+ return !!authState.value?.user;
177
+ }
178
+ }
179
+ const signIn = {
180
+ ...authClient.signIn,
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ email: async (params, options) => {
183
+ isLoading.value = true;
184
+ try {
185
+ const result = await authClient.signIn.email(params, options);
186
+ const resultAny = result;
187
+ const token = resultAny?.token || resultAny?.data?.token;
188
+ const userData = resultAny?.user || resultAny?.data?.user;
189
+ if (token) {
190
+ jwtToken.value = token;
191
+ if (userData) {
192
+ setUser(userData, "jwt");
193
+ }
194
+ console.debug("[LtAuth] JWT token received from login response");
195
+ } else if (userData) {
196
+ setUser(userData, "cookie");
197
+ switchToJwtMode().catch(() => {
198
+ });
199
+ }
200
+ return result;
201
+ } finally {
202
+ isLoading.value = false;
203
+ }
204
+ }
205
+ };
206
+ const signUp = {
207
+ ...authClient.signUp,
208
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
+ email: async (params, options) => {
210
+ isLoading.value = true;
211
+ try {
212
+ const result = await authClient.signUp.email(params, options);
213
+ const resultAny = result;
214
+ const token = resultAny?.token || resultAny?.data?.token;
215
+ const userData = resultAny?.user || resultAny?.data?.user;
216
+ if (token) {
217
+ jwtToken.value = token;
218
+ if (userData) {
219
+ setUser(userData, "jwt");
220
+ }
221
+ console.debug("[LtAuth] JWT token received from signup response");
222
+ } else if (userData) {
223
+ setUser(userData, "cookie");
224
+ switchToJwtMode().catch(() => {
225
+ });
226
+ }
227
+ return result;
228
+ } finally {
229
+ isLoading.value = false;
230
+ }
231
+ }
232
+ };
233
+ const signOut = async (options) => {
234
+ isLoading.value = true;
235
+ try {
236
+ const result = await authClient.signOut(options);
237
+ clearUser();
238
+ return result;
239
+ } finally {
240
+ isLoading.value = false;
241
+ }
242
+ };
243
+ async function authenticateWithPasskey() {
244
+ isLoading.value = true;
245
+ try {
246
+ const apiBase = getApiBase();
247
+ const optionsResponse = await fetchWithAuth(`${apiBase}/passkey/generate-authenticate-options`, {
248
+ method: "GET"
249
+ });
250
+ if (!optionsResponse.ok) {
251
+ return { success: false, error: t("lt.auth.passkeyError", "Konnte Passkey-Optionen nicht laden") };
252
+ }
253
+ const options = await optionsResponse.json();
254
+ const challengeBuffer = ltBase64UrlToUint8Array(options.challenge).buffer;
255
+ const credential = await navigator.credentials.get({
256
+ publicKey: {
257
+ challenge: challengeBuffer,
258
+ rpId: options.rpId,
259
+ allowCredentials: options.allowCredentials || [],
260
+ userVerification: options.userVerification,
261
+ timeout: options.timeout
262
+ }
263
+ });
264
+ if (!credential) {
265
+ return { success: false, error: t("lt.auth.noPasskeySelected", "Kein Passkey ausgew\xE4hlt") };
266
+ }
267
+ const response = credential.response;
268
+ const credentialBody = {
269
+ id: credential.id,
270
+ rawId: ltArrayBufferToBase64Url(credential.rawId),
271
+ type: credential.type,
272
+ response: {
273
+ authenticatorData: ltArrayBufferToBase64Url(response.authenticatorData),
274
+ clientDataJSON: ltArrayBufferToBase64Url(response.clientDataJSON),
275
+ signature: ltArrayBufferToBase64Url(response.signature),
276
+ userHandle: response.userHandle ? ltArrayBufferToBase64Url(response.userHandle) : null
277
+ },
278
+ clientExtensionResults: credential.getClientExtensionResults?.() || {}
279
+ };
280
+ const authResponse = await fetchWithAuth(`${apiBase}/passkey/verify-authentication`, {
281
+ method: "POST",
282
+ headers: { "Content-Type": "application/json" },
283
+ body: JSON.stringify({ challengeId: options.challengeId, response: credentialBody })
284
+ });
285
+ const result = await authResponse.json();
286
+ if (!authResponse.ok) {
287
+ return { success: false, error: result.message || t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen") };
288
+ }
289
+ if (result.user) {
290
+ setUser(result.user, "cookie");
291
+ switchToJwtMode().catch(() => {
292
+ });
293
+ } else if (result.session?.token) {
294
+ jwtToken.value = result.session.token;
295
+ if (authState.value) {
296
+ authState.value = { ...authState.value, authMode: "jwt" };
297
+ }
298
+ console.debug("[LtAuth] Passkey: Stored session token as JWT");
299
+ }
300
+ return { success: true, user: result.user, session: result.session };
301
+ } catch (err) {
302
+ if (err instanceof Error && err.name === "NotAllowedError") {
303
+ return { success: false, error: t("lt.auth.passkeyAborted", "Passkey-Authentifizierung wurde abgebrochen") };
304
+ }
305
+ return { success: false, error: err instanceof Error ? err.message : t("lt.auth.passkeyFailed", "Passkey-Anmeldung fehlgeschlagen") };
306
+ } finally {
307
+ isLoading.value = false;
308
+ }
309
+ }
310
+ async function registerPasskey(name) {
311
+ isLoading.value = true;
312
+ try {
313
+ const apiBase = getApiBase();
314
+ const optionsResponse = await fetchWithAuth(`${apiBase}/passkey/generate-register-options`, {
315
+ method: "GET"
316
+ });
317
+ if (!optionsResponse.ok) {
318
+ const error = await optionsResponse.json().catch(() => ({}));
319
+ return { success: false, error: error.message || t("lt.auth.registerOptionsError", "Konnte Registrierungsoptionen nicht laden") };
320
+ }
321
+ const options = await optionsResponse.json();
322
+ const challengeBuffer = ltBase64UrlToUint8Array(options.challenge).buffer;
323
+ const userIdBuffer = ltBase64UrlToUint8Array(options.user.id).buffer;
324
+ const credential = await navigator.credentials.create({
325
+ publicKey: {
326
+ challenge: challengeBuffer,
327
+ rp: options.rp,
328
+ user: {
329
+ ...options.user,
330
+ id: userIdBuffer
331
+ },
332
+ pubKeyCredParams: options.pubKeyCredParams,
333
+ timeout: options.timeout,
334
+ attestation: options.attestation,
335
+ authenticatorSelection: options.authenticatorSelection,
336
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
337
+ excludeCredentials: (options.excludeCredentials || []).map((cred) => ({
338
+ ...cred,
339
+ id: ltBase64UrlToUint8Array(cred.id).buffer
340
+ }))
341
+ }
342
+ });
343
+ if (!credential) {
344
+ return { success: false, error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung abgebrochen") };
345
+ }
346
+ const attestationResponse = credential.response;
347
+ const credentialBody = {
348
+ name,
349
+ // Include challengeId for JWT mode (database challenge storage)
350
+ challengeId: options.challengeId,
351
+ response: {
352
+ id: credential.id,
353
+ rawId: ltArrayBufferToBase64Url(credential.rawId),
354
+ type: credential.type,
355
+ response: {
356
+ attestationObject: ltArrayBufferToBase64Url(attestationResponse.attestationObject),
357
+ clientDataJSON: ltArrayBufferToBase64Url(attestationResponse.clientDataJSON),
358
+ transports: attestationResponse.getTransports?.() || []
359
+ },
360
+ clientExtensionResults: credential.getClientExtensionResults?.() || {}
361
+ }
362
+ };
363
+ const registerResponse = await fetchWithAuth(`${apiBase}/passkey/verify-registration`, {
364
+ method: "POST",
365
+ headers: { "Content-Type": "application/json" },
366
+ body: JSON.stringify(credentialBody)
367
+ });
368
+ const result = await registerResponse.json();
369
+ if (!registerResponse.ok) {
370
+ return { success: false, error: result.message || t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen") };
371
+ }
372
+ return { success: true, passkey: result };
373
+ } catch (err) {
374
+ if (err instanceof Error && err.name === "NotAllowedError") {
375
+ return { success: false, error: t("lt.auth.passkeyCreationAborted", "Passkey-Erstellung wurde abgebrochen") };
376
+ }
377
+ return { success: false, error: err instanceof Error ? err.message : t("lt.auth.passkeyRegisterFailed", "Passkey-Registrierung fehlgeschlagen") };
378
+ } finally {
379
+ isLoading.value = false;
380
+ }
381
+ }
382
+ return {
383
+ // Auth state
384
+ authMode,
385
+ isAuthenticated,
386
+ isJwtMode,
387
+ isLoading: computed(() => isLoading.value),
388
+ user,
389
+ // User properties
390
+ is2FAEnabled,
391
+ isAdmin,
392
+ // Auth actions
393
+ authenticateWithPasskey,
394
+ changePassword: authClient.changePassword,
395
+ clearUser,
396
+ registerPasskey,
397
+ setUser,
398
+ signIn,
399
+ signOut,
400
+ signUp,
401
+ validateSession,
402
+ // JWT management
403
+ fetchWithAuth,
404
+ jwtToken,
405
+ refreshJwtToken,
406
+ switchToJwtMode,
407
+ // Better Auth client passthrough
408
+ passkey: authClient.passkey,
409
+ twoFactor: authClient.twoFactor
410
+ };
411
+ }
@@ -0,0 +1,5 @@
1
+ export { useLtAuth } from './auth/use-lt-auth.js';
2
+ export { useLtAuthClient, ltAuthClient } from './use-lt-auth-client.js';
3
+ export { useLtTusUpload } from './use-lt-tus-upload.js';
4
+ export { useLtFile } from './use-lt-file.js';
5
+ export { useLtShare, type UseLtShareReturn } from './use-lt-share.js';
@@ -0,0 +1,5 @@
1
+ export { useLtAuth } from "./auth/use-lt-auth.js";
2
+ export { useLtAuthClient, ltAuthClient } from "./use-lt-auth-client.js";
3
+ export { useLtTusUpload } from "./use-lt-tus-upload.js";
4
+ export { useLtFile } from "./use-lt-file.js";
5
+ export { useLtShare } from "./use-lt-share.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Auth Client Composable
3
+ *
4
+ * Provides access to the Better-Auth client singleton.
5
+ * Use this when you need direct access to the auth client API
6
+ * (e.g., for 2FA management, passkey operations).
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const authClient = useLtAuthClient();
11
+ *
12
+ * // Access 2FA methods
13
+ * await authClient.twoFactor.enable({ password: 'test' });
14
+ *
15
+ * // Access passkey methods
16
+ * await authClient.passkey.listUserPasskeys();
17
+ * ```
18
+ */
19
+ import { type LtAuthClient } from '../lib/auth-client.js';
20
+ /**
21
+ * Returns the Better-Auth client singleton
22
+ *
23
+ * The client is created once and reused across all calls.
24
+ * Configuration is read from RuntimeConfig on first call.
25
+ */
26
+ export declare function useLtAuthClient(): LtAuthClient;
27
+ export declare const ltAuthClient: {
28
+ readonly instance: LtAuthClient;
29
+ };