create-nuxt-base 1.1.2 → 2.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 (39) hide show
  1. package/.github/workflows/publish.yml +1 -1
  2. package/AUTH.md +16 -14
  3. package/CHANGELOG.md +46 -11
  4. package/README.md +5 -5
  5. package/nuxt-base-template/README.md +11 -11
  6. package/nuxt-base-template/app/components/Modal/ModalBackupCodes.vue +2 -1
  7. package/nuxt-base-template/app/components/Upload/TusFileUpload.vue +7 -7
  8. package/nuxt-base-template/app/interfaces/user.interface.ts +5 -12
  9. package/nuxt-base-template/app/layouts/default.vue +1 -1
  10. package/nuxt-base-template/app/middleware/admin.global.ts +27 -7
  11. package/nuxt-base-template/app/middleware/auth.global.ts +23 -6
  12. package/nuxt-base-template/app/middleware/guest.global.ts +22 -7
  13. package/nuxt-base-template/app/pages/app/index.vue +4 -18
  14. package/nuxt-base-template/app/pages/app/settings/security.vue +2 -2
  15. package/nuxt-base-template/app/pages/auth/2fa.vue +39 -8
  16. package/nuxt-base-template/app/pages/auth/forgot-password.vue +2 -1
  17. package/nuxt-base-template/app/pages/auth/login.vue +14 -21
  18. package/nuxt-base-template/app/pages/auth/register.vue +5 -8
  19. package/nuxt-base-template/app/pages/auth/reset-password.vue +2 -1
  20. package/nuxt-base-template/docs/pages/docs.vue +1 -1
  21. package/nuxt-base-template/nuxt.config.ts +38 -1
  22. package/nuxt-base-template/package-lock.json +136 -2905
  23. package/nuxt-base-template/package.json +1 -0
  24. package/nuxt-base-template/server/api/iam/[...path].ts +12 -4
  25. package/package.json +2 -2
  26. package/nuxt-base-template/app/components/Transition/TransitionFade.vue +0 -27
  27. package/nuxt-base-template/app/components/Transition/TransitionFadeScale.vue +0 -27
  28. package/nuxt-base-template/app/components/Transition/TransitionSlide.vue +0 -12
  29. package/nuxt-base-template/app/components/Transition/TransitionSlideBottom.vue +0 -12
  30. package/nuxt-base-template/app/components/Transition/TransitionSlideRevert.vue +0 -12
  31. package/nuxt-base-template/app/composables/use-better-auth.ts +0 -415
  32. package/nuxt-base-template/app/composables/use-file.ts +0 -71
  33. package/nuxt-base-template/app/composables/use-share.ts +0 -38
  34. package/nuxt-base-template/app/composables/use-tus-upload.ts +0 -278
  35. package/nuxt-base-template/app/composables/use-tw.ts +0 -1
  36. package/nuxt-base-template/app/interfaces/upload.interface.ts +0 -58
  37. package/nuxt-base-template/app/lib/auth-client.ts +0 -232
  38. package/nuxt-base-template/app/plugins/auth-interceptor.client.ts +0 -143
  39. package/nuxt-base-template/app/utils/crypto.ts +0 -44
@@ -1,143 +0,0 @@
1
- /**
2
- * Auth Interceptor Plugin
3
- *
4
- * This plugin intercepts all API responses and handles session expiration.
5
- * When a 401 (Unauthorized) response is received, it automatically:
6
- * 1. Clears the user session state
7
- * 2. Redirects to the login page
8
- *
9
- * Note: This is a client-only plugin (.client.ts) since auth state
10
- * management only makes sense in the browser context.
11
- */
12
- export default defineNuxtPlugin(() => {
13
- const { clearUser, isAuthenticated } = useBetterAuth();
14
- const route = useRoute();
15
-
16
- // Track if we're already handling a 401 to prevent multiple redirects
17
- let isHandling401 = false;
18
-
19
- // Paths that should not trigger auto-logout on 401
20
- // (public auth endpoints where 401 is expected)
21
- const publicAuthPaths = [
22
- '/auth/login',
23
- '/auth/register',
24
- '/auth/forgot-password',
25
- '/auth/reset-password',
26
- '/auth/2fa',
27
- ];
28
-
29
- /**
30
- * Check if current route is a public auth route
31
- */
32
- function isPublicAuthRoute(): boolean {
33
- return publicAuthPaths.some((path) => route.path.startsWith(path));
34
- }
35
-
36
- /**
37
- * Check if URL is an auth-related endpoint that shouldn't trigger logout
38
- * (e.g., login, register, password reset endpoints)
39
- */
40
- function isAuthEndpoint(url: string): boolean {
41
- const authEndpoints = [
42
- '/sign-in',
43
- '/sign-up',
44
- '/sign-out',
45
- '/forgot-password',
46
- '/reset-password',
47
- '/verify-email',
48
- '/session',
49
- ];
50
- return authEndpoints.some((endpoint) => url.includes(endpoint));
51
- }
52
-
53
- /**
54
- * Handle 401 Unauthorized responses
55
- * Clears user state and redirects to login page
56
- */
57
- async function handleUnauthorized(requestUrl?: string): Promise<void> {
58
- // Prevent multiple simultaneous 401 handling
59
- if (isHandling401) {
60
- return;
61
- }
62
-
63
- // Don't handle 401 for auth endpoints (expected behavior)
64
- if (requestUrl && isAuthEndpoint(requestUrl)) {
65
- return;
66
- }
67
-
68
- // Don't handle 401 on public auth pages
69
- if (isPublicAuthRoute()) {
70
- return;
71
- }
72
-
73
- isHandling401 = true;
74
-
75
- try {
76
- // Only handle if user was authenticated (prevents redirect loops)
77
- if (isAuthenticated.value) {
78
- console.debug('[Auth Interceptor] Session expired, logging out...');
79
-
80
- // Clear user state
81
- clearUser();
82
-
83
- // Redirect to login page with return URL
84
- await navigateTo({
85
- path: '/auth/login',
86
- query: {
87
- redirect: route.fullPath !== '/auth/login' ? route.fullPath : undefined,
88
- },
89
- }, {
90
- replace: true,
91
- });
92
- }
93
- } finally {
94
- // Reset flag after a short delay to allow navigation to complete
95
- setTimeout(() => {
96
- isHandling401 = false;
97
- }, 1000);
98
- }
99
- }
100
-
101
- // Override the default $fetch to add response error handling
102
- const originalFetch = globalThis.$fetch;
103
-
104
- // Use a wrapper to intercept responses
105
- globalThis.$fetch = ((url: string, options?: any) => {
106
- return originalFetch(url, {
107
- ...options,
108
- onResponseError: (context: any) => {
109
- // Call original onResponseError if provided
110
- if (options?.onResponseError) {
111
- options.onResponseError(context);
112
- }
113
-
114
- // Handle 401 errors
115
- if (context.response?.status === 401) {
116
- handleUnauthorized(url);
117
- }
118
- },
119
- });
120
- }) as typeof globalThis.$fetch;
121
-
122
- // Also intercept native fetch for manual API calls
123
- const originalNativeFetch = globalThis.fetch;
124
-
125
- globalThis.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
126
- const response = await originalNativeFetch(input, init);
127
-
128
- // Handle 401 errors from native fetch
129
- if (response.status === 401) {
130
- const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
131
- handleUnauthorized(url);
132
- }
133
-
134
- return response;
135
- };
136
-
137
- // Provide a manual method to trigger logout on 401
138
- return {
139
- provide: {
140
- handleUnauthorized,
141
- },
142
- };
143
- });
@@ -1,44 +0,0 @@
1
- /**
2
- * Hashes a string using SHA256
3
- * Uses the Web Crypto API which is available in all modern browsers
4
- *
5
- * @param message - The string to hash
6
- * @returns The SHA256 hash as a lowercase hex string (64 characters)
7
- */
8
- export async function sha256(message: string): Promise<string> {
9
- const msgBuffer = new TextEncoder().encode(message);
10
- const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
11
- const hashArray = Array.from(new Uint8Array(hashBuffer));
12
- return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
13
- }
14
-
15
- // ============================================================================
16
- // WebAuthn/Passkey Utilities
17
- // ============================================================================
18
-
19
- /**
20
- * Converts an ArrayBuffer to a base64url-encoded string
21
- * Used for WebAuthn credential responses
22
- *
23
- * @param buffer - The ArrayBuffer to convert
24
- * @returns The base64url-encoded string (no padding)
25
- */
26
- export function arrayBufferToBase64Url(buffer: ArrayBuffer): string {
27
- const bytes = new Uint8Array(buffer);
28
- let binary = '';
29
- bytes.forEach((b) => (binary += String.fromCharCode(b)));
30
- return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
31
- }
32
-
33
- /**
34
- * Converts a base64url-encoded string to a Uint8Array
35
- * Used for WebAuthn challenge decoding
36
- *
37
- * @param base64url - The base64url-encoded string
38
- * @returns The decoded Uint8Array
39
- */
40
- export function base64UrlToUint8Array(base64url: string): Uint8Array {
41
- const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
42
- const paddedBase64 = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
43
- return Uint8Array.from(atob(paddedBase64), (c) => c.charCodeAt(0));
44
- }