@k3-universe/react-kit 0.0.29 → 0.0.30

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 (152) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1013 -21
  4. package/dist/kit/builder/auth/components/Can.d.ts +13 -0
  5. package/dist/kit/builder/auth/components/Can.d.ts.map +1 -0
  6. package/dist/kit/builder/auth/components/RequireAuth.d.ts +45 -0
  7. package/dist/kit/builder/auth/components/RequireAuth.d.ts.map +1 -0
  8. package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts +8 -0
  9. package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts.map +1 -0
  10. package/dist/kit/builder/auth/components/ShowWhenError.d.ts +8 -0
  11. package/dist/kit/builder/auth/components/ShowWhenError.d.ts.map +1 -0
  12. package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts +8 -0
  13. package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts.map +1 -0
  14. package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts +8 -0
  15. package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts.map +1 -0
  16. package/dist/kit/builder/auth/components/withPermission.d.ts +7 -0
  17. package/dist/kit/builder/auth/components/withPermission.d.ts.map +1 -0
  18. package/dist/kit/builder/auth/hooks/action-hooks.d.ts +18 -0
  19. package/dist/kit/builder/auth/hooks/action-hooks.d.ts.map +1 -0
  20. package/dist/kit/builder/auth/hooks/core-hooks.d.ts +56 -0
  21. package/dist/kit/builder/auth/hooks/core-hooks.d.ts.map +1 -0
  22. package/dist/kit/builder/auth/hooks/index.d.ts +5 -0
  23. package/dist/kit/builder/auth/hooks/index.d.ts.map +1 -0
  24. package/dist/kit/builder/auth/hooks/permission-hooks.d.ts +18 -0
  25. package/dist/kit/builder/auth/hooks/permission-hooks.d.ts.map +1 -0
  26. package/dist/kit/builder/auth/hooks/token-hooks.d.ts +13 -0
  27. package/dist/kit/builder/auth/hooks/token-hooks.d.ts.map +1 -0
  28. package/dist/kit/builder/auth/index.d.ts +14 -8
  29. package/dist/kit/builder/auth/index.d.ts.map +1 -1
  30. package/dist/kit/builder/auth/{AuthProvider.d.ts → providers/AuthProvider.d.ts} +1 -1
  31. package/dist/kit/builder/auth/providers/AuthProvider.d.ts.map +1 -0
  32. package/dist/kit/builder/auth/types/adapter-config.d.ts +31 -0
  33. package/dist/kit/builder/auth/types/adapter-config.d.ts.map +1 -0
  34. package/dist/kit/builder/auth/types/adapter.d.ts +80 -0
  35. package/dist/kit/builder/auth/types/adapter.d.ts.map +1 -0
  36. package/dist/kit/builder/auth/types/core.d.ts +16 -0
  37. package/dist/kit/builder/auth/types/core.d.ts.map +1 -0
  38. package/dist/kit/builder/auth/types/index.d.ts +10 -0
  39. package/dist/kit/builder/auth/types/index.d.ts.map +1 -0
  40. package/dist/kit/builder/auth/types/middleware.d.ts +11 -0
  41. package/dist/kit/builder/auth/types/middleware.d.ts.map +1 -0
  42. package/dist/kit/builder/auth/types/permissions.d.ts +17 -0
  43. package/dist/kit/builder/auth/types/permissions.d.ts.map +1 -0
  44. package/dist/kit/builder/auth/types/state.d.ts +13 -0
  45. package/dist/kit/builder/auth/types/state.d.ts.map +1 -0
  46. package/dist/kit/builder/auth/types/storage.d.ts +20 -0
  47. package/dist/kit/builder/auth/types/storage.d.ts.map +1 -0
  48. package/dist/kit/builder/auth/types/token-manager.d.ts +7 -0
  49. package/dist/kit/builder/auth/types/token-manager.d.ts.map +1 -0
  50. package/dist/kit/builder/auth/types/utils.d.ts +7 -0
  51. package/dist/kit/builder/auth/types/utils.d.ts.map +1 -0
  52. package/dist/kit/builder/auth/{adapter.d.ts → utils/auth-adapter.d.ts} +2 -2
  53. package/dist/kit/builder/auth/utils/auth-adapter.d.ts.map +1 -0
  54. package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts +4 -0
  55. package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts.map +1 -0
  56. package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts +6 -0
  57. package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts.map +1 -0
  58. package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts +6 -0
  59. package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts.map +1 -0
  60. package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts +9 -0
  61. package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts.map +1 -0
  62. package/dist/kit/builder/auth/utils/client-adapters/index.d.ts +7 -0
  63. package/dist/kit/builder/auth/utils/client-adapters/index.d.ts.map +1 -0
  64. package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts +9 -0
  65. package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts.map +1 -0
  66. package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts +14 -0
  67. package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts.map +1 -0
  68. package/dist/kit/builder/auth/{permission-checker.d.ts → utils/permission-checker.d.ts} +1 -1
  69. package/dist/kit/builder/auth/utils/permission-checker.d.ts.map +1 -0
  70. package/dist/kit/builder/auth/utils/storage/browser.d.ts +11 -0
  71. package/dist/kit/builder/auth/utils/storage/browser.d.ts.map +1 -0
  72. package/dist/kit/builder/auth/utils/storage/cookie.d.ts +3 -0
  73. package/dist/kit/builder/auth/utils/storage/cookie.d.ts.map +1 -0
  74. package/dist/kit/builder/auth/utils/storage/encryption.d.ts +7 -0
  75. package/dist/kit/builder/auth/utils/storage/encryption.d.ts.map +1 -0
  76. package/dist/kit/builder/auth/utils/storage/env.d.ts +2 -0
  77. package/dist/kit/builder/auth/utils/storage/env.d.ts.map +1 -0
  78. package/dist/kit/builder/auth/utils/storage/factory.d.ts +6 -0
  79. package/dist/kit/builder/auth/utils/storage/factory.d.ts.map +1 -0
  80. package/dist/kit/builder/auth/utils/storage/index.d.ts +7 -0
  81. package/dist/kit/builder/auth/utils/storage/index.d.ts.map +1 -0
  82. package/dist/kit/builder/auth/utils/storage/memory.d.ts +3 -0
  83. package/dist/kit/builder/auth/utils/storage/memory.d.ts.map +1 -0
  84. package/dist/kit/builder/auth/{token-manager.d.ts → utils/token-manager.d.ts} +1 -1
  85. package/dist/kit/builder/auth/utils/token-manager.d.ts.map +1 -0
  86. package/dist/kit/components/login/Login.d.ts.map +1 -1
  87. package/dist/kit/layouts/admin/components/AdminLayout.d.ts +2 -1
  88. package/dist/kit/layouts/admin/components/AdminLayout.d.ts.map +1 -1
  89. package/package.json +1 -1
  90. package/src/index.ts +1 -0
  91. package/src/kit/builder/auth/components/Can.tsx +27 -0
  92. package/src/kit/builder/auth/components/RequireAuth.tsx +78 -0
  93. package/src/kit/builder/auth/components/ShowWhenAuthenticated.tsx +10 -0
  94. package/src/kit/builder/auth/components/ShowWhenError.tsx +10 -0
  95. package/src/kit/builder/auth/components/ShowWhenLoading.tsx +10 -0
  96. package/src/kit/builder/auth/components/ShowWhenUnauthenticated.tsx +10 -0
  97. package/src/kit/builder/auth/components/withPermission.tsx +23 -0
  98. package/src/kit/builder/auth/hooks/action-hooks.ts +34 -0
  99. package/src/kit/builder/auth/hooks/core-hooks.ts +65 -0
  100. package/src/kit/builder/auth/hooks/index.ts +4 -0
  101. package/src/kit/builder/auth/hooks/permission-hooks.ts +43 -0
  102. package/src/kit/builder/auth/hooks/token-hooks.ts +25 -0
  103. package/src/kit/builder/auth/index.ts +16 -18
  104. package/src/kit/builder/auth/{AuthProvider.tsx → providers/AuthProvider.tsx} +1 -1
  105. package/src/kit/builder/auth/types/adapter-config.ts +44 -0
  106. package/src/kit/builder/auth/types/adapter.ts +132 -0
  107. package/src/kit/builder/auth/types/core.ts +27 -0
  108. package/src/kit/builder/auth/types/index.ts +9 -0
  109. package/src/kit/builder/auth/types/middleware.ts +20 -0
  110. package/src/kit/builder/auth/types/permissions.ts +23 -0
  111. package/src/kit/builder/auth/types/state.ts +16 -0
  112. package/src/kit/builder/auth/types/storage.ts +21 -0
  113. package/src/kit/builder/auth/types/token-manager.ts +9 -0
  114. package/src/kit/builder/auth/types/utils.ts +55 -0
  115. package/src/kit/builder/auth/{adapter.ts → utils/auth-adapter.ts} +3 -2
  116. package/src/kit/builder/auth/utils/client-adapters/apollo-link.ts +30 -0
  117. package/src/kit/builder/auth/utils/client-adapters/axios.ts +61 -0
  118. package/src/kit/builder/auth/utils/client-adapters/fetch.ts +48 -0
  119. package/src/kit/builder/auth/utils/client-adapters/graphql.ts +60 -0
  120. package/src/kit/builder/auth/utils/client-adapters/index.ts +6 -0
  121. package/src/kit/builder/auth/utils/client-adapters/rest.ts +60 -0
  122. package/src/kit/builder/auth/utils/client-adapters/urql-exchange.ts +76 -0
  123. package/src/kit/builder/auth/{permission-checker.ts → utils/permission-checker.ts} +1 -1
  124. package/src/kit/builder/auth/utils/storage/browser.ts +99 -0
  125. package/src/kit/builder/auth/utils/storage/cookie.ts +116 -0
  126. package/src/kit/builder/auth/utils/storage/encryption.ts +80 -0
  127. package/src/kit/builder/auth/utils/storage/env.ts +2 -0
  128. package/src/kit/builder/auth/utils/storage/factory.ts +37 -0
  129. package/src/kit/builder/auth/utils/storage/index.ts +6 -0
  130. package/src/kit/builder/auth/utils/storage/memory.ts +15 -0
  131. package/src/kit/builder/auth/{token-manager.ts → utils/token-manager.ts} +1 -1
  132. package/src/kit/components/login/Login.tsx +9 -7
  133. package/src/kit/layouts/admin/components/AdminLayout.tsx +24 -17
  134. package/dist/kit/builder/auth/AuthProvider.d.ts.map +0 -1
  135. package/dist/kit/builder/auth/adapter.d.ts.map +0 -1
  136. package/dist/kit/builder/auth/client-adapters.d.ts +0 -149
  137. package/dist/kit/builder/auth/client-adapters.d.ts.map +0 -1
  138. package/dist/kit/builder/auth/components.d.ts +0 -119
  139. package/dist/kit/builder/auth/components.d.ts.map +0 -1
  140. package/dist/kit/builder/auth/hooks.d.ts +0 -158
  141. package/dist/kit/builder/auth/hooks.d.ts.map +0 -1
  142. package/dist/kit/builder/auth/permission-checker.d.ts.map +0 -1
  143. package/dist/kit/builder/auth/storage.d.ts +0 -17
  144. package/dist/kit/builder/auth/storage.d.ts.map +0 -1
  145. package/dist/kit/builder/auth/token-manager.d.ts.map +0 -1
  146. package/dist/kit/builder/auth/types.d.ts +0 -183
  147. package/dist/kit/builder/auth/types.d.ts.map +0 -1
  148. package/src/kit/builder/auth/client-adapters.ts +0 -398
  149. package/src/kit/builder/auth/components.tsx +0 -221
  150. package/src/kit/builder/auth/hooks.ts +0 -237
  151. package/src/kit/builder/auth/storage.ts +0 -366
  152. package/src/kit/builder/auth/types.ts +0 -393
@@ -0,0 +1,6 @@
1
+ export * from './graphql'
2
+ export * from './rest'
3
+ export * from './axios'
4
+ export * from './fetch'
5
+ export * from './apollo-link'
6
+ export * from './urql-exchange'
@@ -0,0 +1,60 @@
1
+ import type { AuthAdapterConfig, AuthSession, RESTAuthClient } from '../../types'
2
+
3
+ export type RESTClientAdapterOptions<
4
+ TUser = unknown,
5
+ TRole extends string = string,
6
+ TPermission extends string = string,
7
+ TSession extends AuthSession<TUser, TRole, TPermission> = AuthSession<
8
+ TUser,
9
+ TRole,
10
+ TPermission
11
+ >,
12
+ TCredentials = unknown,
13
+ > = {
14
+ client: RESTAuthClient<TSession, TCredentials>
15
+ resolveUser?: (session: TSession | null) => TUser | null
16
+ resolveRoles?: (session: TSession | null) => TRole[]
17
+ resolvePermissions?: (session: TSession | null) => TPermission[]
18
+ }
19
+
20
+ export function createRESTAuthAdapter<
21
+ TUser = unknown,
22
+ TRole extends string = string,
23
+ TPermission extends string = string,
24
+ TSession extends AuthSession<TUser, TRole, TPermission> = AuthSession<
25
+ TUser,
26
+ TRole,
27
+ TPermission
28
+ >,
29
+ TCredentials = unknown,
30
+ >(
31
+ options: RESTClientAdapterOptions<
32
+ TUser,
33
+ TRole,
34
+ TPermission,
35
+ TSession,
36
+ TCredentials
37
+ >,
38
+ ): Partial<
39
+ AuthAdapterConfig<TUser, TRole, TPermission, TSession, TCredentials>
40
+ > {
41
+ return {
42
+ login: options.client.login,
43
+ logout: options.client.logout,
44
+ refresh: options.client.refresh
45
+ ? async (session: TSession) => {
46
+ if (!session.refreshToken) {
47
+ throw new Error('No refresh token available')
48
+ }
49
+ if (!options.client.refresh) {
50
+ throw new Error('Refresh function not provided')
51
+ }
52
+ return await options.client.refresh(session.refreshToken)
53
+ }
54
+ : undefined,
55
+ loadSession: options.client.getCurrentUser,
56
+ resolveUser: options.resolveUser,
57
+ resolveRoles: options.resolveRoles,
58
+ resolvePermissions: options.resolvePermissions,
59
+ }
60
+ }
@@ -0,0 +1,76 @@
1
+ export function createUrqlAuthExchange(
2
+ getToken: () => string | null,
3
+ options?: {
4
+ tokenType?: string
5
+ refreshToken?: () => Promise<void>
6
+ onTokenExpired?: () => void
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ willAuthError?: (params: { authState: any; operation: any }) => boolean
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ didAuthError?: (params: { error: any; operation: any }) => boolean
11
+ },
12
+ ) {
13
+ const tokenType = options?.tokenType ?? 'Bearer'
14
+
15
+ // Avoid direct dependency on @urql/exchange-auth. Return a factory that accepts it.
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ return (authExchange: any) =>
18
+ authExchange({
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ addAuthToOperation: ({ authState, operation }: any) => {
21
+ const token = authState?.token ?? getToken()
22
+ if (!token) return operation
23
+
24
+ const fetchOptions =
25
+ typeof operation.context.fetchOptions === 'function'
26
+ ? operation.context.fetchOptions()
27
+ : operation.context.fetchOptions || {}
28
+
29
+ return {
30
+ ...operation,
31
+ context: {
32
+ ...operation.context,
33
+ fetchOptions: {
34
+ ...fetchOptions,
35
+ headers: {
36
+ ...fetchOptions.headers,
37
+ Authorization: `${tokenType} ${token}`,
38
+ },
39
+ },
40
+ },
41
+ }
42
+ },
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ getAuth: async ({ authState }: any) => {
45
+ if (!authState) {
46
+ const token = getToken()
47
+ if (token) return { token }
48
+ return null
49
+ }
50
+
51
+ if (options?.refreshToken) {
52
+ try {
53
+ await options.refreshToken()
54
+ const token = getToken()
55
+ if (token) return { token }
56
+ } catch (_e) {
57
+ options.onTokenExpired?.()
58
+ return null
59
+ }
60
+ }
61
+ return null
62
+ },
63
+ willAuthError: options?.willAuthError,
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ didAuthError: (params: { error: any; operation: any }) => {
66
+ if (options?.didAuthError) return options.didAuthError(params)
67
+ const { error } = params
68
+ return (
69
+ error?.graphQLErrors?.some(
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ (e: any) => e.extensions?.code === 'UNAUTHENTICATED',
72
+ ) || error?.response?.status === 401
73
+ )
74
+ },
75
+ })
76
+ }
@@ -3,7 +3,7 @@ import type {
3
3
  PermissionPolicy,
4
4
  PermissionRule,
5
5
  RoleHierarchy,
6
- } from './types';
6
+ } from '../types';
7
7
 
8
8
  /**
9
9
  * Expands permissions based on hierarchy
@@ -0,0 +1,99 @@
1
+ import type { AuthStorage } from '../../types'
2
+ import { isBrowser } from './env'
3
+ import { createMemoryStorage } from './memory'
4
+ import { SimpleEncryption } from './encryption'
5
+
6
+ export type BrowserStorageOptions<T> = {
7
+ key: string
8
+ serialize?: (value: T | null) => Promise<string | null> | string | null
9
+ deserialize?: (value: string | null) => Promise<T | null> | T | null
10
+ storage?: Storage
11
+ encrypt?: boolean
12
+ encryptionKey?: string
13
+ }
14
+
15
+ export const createBrowserStorage = <T>(
16
+ options: BrowserStorageOptions<T>,
17
+ ): AuthStorage<T> => {
18
+ const fallback = createMemoryStorage<T>()
19
+ const targetStorage =
20
+ options.storage ?? (isBrowser ? window.localStorage : undefined)
21
+
22
+ const encryption =
23
+ options.encrypt && options.encryptionKey
24
+ ? new SimpleEncryption(options.encryptionKey)
25
+ : null
26
+
27
+ const defaultSerialize = async (value: T | null): Promise<string | null> => {
28
+ if (value === null) return null
29
+ const json = JSON.stringify(value)
30
+ return encryption ? await encryption.encrypt(json) : json
31
+ }
32
+
33
+ const defaultDeserialize = async (
34
+ value: string | null,
35
+ ): Promise<T | null> => {
36
+ if (!value) return null
37
+ try {
38
+ const decrypted = encryption ? await encryption.decrypt(value) : value
39
+ return JSON.parse(decrypted) as T
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ const serialize = options.serialize
46
+ ? async (value: T | null) => {
47
+ const result = await options.serialize?.(value)
48
+ return result ?? null
49
+ }
50
+ : defaultSerialize
51
+
52
+ const deserialize = options.deserialize
53
+ ? async (value: string | null) => {
54
+ const result = await options.deserialize?.(value)
55
+ return result ?? null
56
+ }
57
+ : defaultDeserialize
58
+
59
+ if (!targetStorage) {
60
+ return fallback
61
+ }
62
+
63
+ return {
64
+ get: async () => {
65
+ try {
66
+ const raw = targetStorage.getItem(options.key)
67
+ const value = await deserialize(raw)
68
+ fallback.set(value)
69
+ return value
70
+ } catch (error) {
71
+ console.warn('[auth2][storage] Failed to read from storage', error)
72
+ return fallback.get()
73
+ }
74
+ },
75
+ set: async (value) => {
76
+ try {
77
+ const serialized = await serialize(value)
78
+ if (serialized === null) {
79
+ targetStorage.removeItem(options.key)
80
+ } else {
81
+ targetStorage.setItem(options.key, serialized)
82
+ }
83
+ fallback.set(value)
84
+ } catch (error) {
85
+ console.warn('[auth2][storage] Failed to write to storage', error)
86
+ fallback.set(value)
87
+ }
88
+ },
89
+ clear: () => {
90
+ try {
91
+ targetStorage.removeItem(options.key)
92
+ } catch (error) {
93
+ console.warn('[auth2][storage] Failed to clear storage', error)
94
+ } finally {
95
+ fallback.clear()
96
+ }
97
+ },
98
+ }
99
+ }
@@ -0,0 +1,116 @@
1
+ import type { AuthStorage, StorageOptions } from '../../types'
2
+ import { isBrowser } from './env'
3
+ import { createMemoryStorage } from './memory'
4
+ import { SimpleEncryption } from './encryption'
5
+
6
+ export const createCookieStorage = <T>(
7
+ options: StorageOptions,
8
+ ): AuthStorage<T> => {
9
+ const key = options.key ?? 'auth2_session'
10
+ const cookieOpts = options.cookieOptions ?? {}
11
+ const fallback = createMemoryStorage<T>()
12
+ const encryption =
13
+ options.encrypt && options.encryptionKey
14
+ ? new SimpleEncryption(options.encryptionKey)
15
+ : null
16
+ const cookieSetter =
17
+ typeof Document !== 'undefined'
18
+ ? Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')?.set
19
+ : undefined
20
+
21
+ const assignCookie = (value: string) => {
22
+ if (!isBrowser || typeof document === 'undefined') return
23
+ if (cookieSetter) {
24
+ cookieSetter.call(document, value)
25
+ return
26
+ }
27
+ Reflect.set(document, 'cookie', value)
28
+ }
29
+
30
+ const serialize = async (value: T | null): Promise<string | null> => {
31
+ if (value === null) return null
32
+ const json = JSON.stringify(value)
33
+ return encryption ? await encryption.encrypt(json) : json
34
+ }
35
+
36
+ const deserialize = async (value: string | null): Promise<T | null> => {
37
+ if (!value) return null
38
+ try {
39
+ const decrypted = encryption ? await encryption.decrypt(value) : value
40
+ return JSON.parse(decrypted) as T
41
+ } catch {
42
+ return null
43
+ }
44
+ }
45
+
46
+ const getCookie = (name: string): string | null => {
47
+ if (!isBrowser) return null
48
+ const matches = document.cookie.match(
49
+ new RegExp(
50
+ `(?:^|; )${name.replace(/([.$?*|{}()\[\]\\/+^])/g, '\\$1')}=([^;]*)`,
51
+ ),
52
+ )
53
+ return matches ? decodeURIComponent(matches[1]) : null
54
+ }
55
+
56
+ const setCookie = (
57
+ name: string,
58
+ value: string,
59
+ opts: typeof cookieOpts = {},
60
+ ) => {
61
+ if (!isBrowser) return
62
+
63
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
64
+
65
+ if (opts.maxAge) cookie += `; max-age=${opts.maxAge}`
66
+ if (opts.domain) cookie += `; domain=${opts.domain}`
67
+ if (opts.path !== undefined) cookie += `; path=${opts.path}`
68
+ else cookie += '; path=/'
69
+ if (opts.secure) cookie += '; secure'
70
+ if (opts.sameSite) cookie += `; samesite=${opts.sameSite}`
71
+
72
+ assignCookie(cookie)
73
+ }
74
+
75
+ const deleteCookie = (name: string) => {
76
+ if (!isBrowser) return
77
+ assignCookie(`${name}=; max-age=0; path=/`)
78
+ }
79
+
80
+ return {
81
+ get: async () => {
82
+ try {
83
+ const raw = getCookie(key)
84
+ const value = await deserialize(raw)
85
+ fallback.set(value)
86
+ return value
87
+ } catch (error) {
88
+ console.warn('[auth2][storage] Failed to read from cookie', error)
89
+ return fallback.get()
90
+ }
91
+ },
92
+ set: async (value) => {
93
+ try {
94
+ const serialized = await serialize(value)
95
+ if (serialized === null) {
96
+ deleteCookie(key)
97
+ } else {
98
+ setCookie(key, serialized, cookieOpts)
99
+ }
100
+ fallback.set(value)
101
+ } catch (error) {
102
+ console.warn('[auth2][storage] Failed to write to cookie', error)
103
+ fallback.set(value)
104
+ }
105
+ },
106
+ clear: () => {
107
+ try {
108
+ deleteCookie(key)
109
+ } catch (error) {
110
+ console.warn('[auth2][storage] Failed to clear cookie', error)
111
+ } finally {
112
+ fallback.clear()
113
+ }
114
+ },
115
+ }
116
+ }
@@ -0,0 +1,80 @@
1
+ export class SimpleEncryption {
2
+ private key: string
3
+
4
+ constructor(key: string) {
5
+ this.key = key
6
+ }
7
+
8
+ async encrypt(text: string): Promise<string> {
9
+ if (!crypto?.subtle) {
10
+ console.warn('[auth2][storage] Crypto API not available, storing unencrypted')
11
+ return text
12
+ }
13
+
14
+ try {
15
+ const encoder = new TextEncoder()
16
+ const data = encoder.encode(text)
17
+ const keyData = encoder.encode(this.key.padEnd(32, '0').slice(0, 32))
18
+
19
+ const cryptoKey = await crypto.subtle.importKey(
20
+ 'raw',
21
+ keyData,
22
+ { name: 'AES-GCM', length: 256 },
23
+ false,
24
+ ['encrypt'],
25
+ )
26
+
27
+ const iv = crypto.getRandomValues(new Uint8Array(12))
28
+ const encrypted = await crypto.subtle.encrypt(
29
+ { name: 'AES-GCM', iv },
30
+ cryptoKey,
31
+ data,
32
+ )
33
+
34
+ const result = new Uint8Array(iv.length + encrypted.byteLength)
35
+ result.set(iv, 0)
36
+ result.set(new Uint8Array(encrypted), iv.length)
37
+
38
+ return btoa(String.fromCharCode(...result))
39
+ } catch (error) {
40
+ console.error('[auth2][storage] Encryption failed:', error)
41
+ return text
42
+ }
43
+ }
44
+
45
+ async decrypt(encrypted: string): Promise<string> {
46
+ if (!crypto?.subtle) {
47
+ console.warn('[auth2][storage] Crypto API not available, reading unencrypted')
48
+ return encrypted
49
+ }
50
+
51
+ try {
52
+ const encoder = new TextEncoder()
53
+ const decoder = new TextDecoder()
54
+ const keyData = encoder.encode(this.key.padEnd(32, '0').slice(0, 32))
55
+
56
+ const cryptoKey = await crypto.subtle.importKey(
57
+ 'raw',
58
+ keyData,
59
+ { name: 'AES-GCM', length: 256 },
60
+ false,
61
+ ['decrypt'],
62
+ )
63
+
64
+ const data = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0))
65
+ const iv = data.slice(0, 12)
66
+ const encryptedData = data.slice(12)
67
+
68
+ const decrypted = await crypto.subtle.decrypt(
69
+ { name: 'AES-GCM', iv },
70
+ cryptoKey,
71
+ encryptedData,
72
+ )
73
+
74
+ return decoder.decode(decrypted)
75
+ } catch (error) {
76
+ console.error('[auth2][storage] Decryption failed:', error)
77
+ return encrypted
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,2 @@
1
+ export const isBrowser =
2
+ typeof window !== "undefined" && typeof window.localStorage !== "undefined";
@@ -0,0 +1,37 @@
1
+ import type { AuthStorage, StorageOptions } from '../../types'
2
+ import { isBrowser } from './env'
3
+ import { createMemoryStorage } from './memory'
4
+ import { createCookieStorage } from './cookie'
5
+ import { createBrowserStorage } from './browser'
6
+
7
+ /**
8
+ * Creates appropriate storage based on options
9
+ */
10
+ export function createStorage<T>(options: StorageOptions = {}): AuthStorage<T> {
11
+ const storageType = options.storage ?? 'local'
12
+ const key = options.key ?? 'auth2_session'
13
+
14
+ switch (storageType) {
15
+ case 'cookie':
16
+ return createCookieStorage<T>(options)
17
+
18
+ case 'session':
19
+ return createBrowserStorage<T>({
20
+ key,
21
+ storage: isBrowser ? window.sessionStorage : undefined,
22
+ encrypt: options.encrypt,
23
+ encryptionKey: options.encryptionKey,
24
+ })
25
+
26
+ case 'local':
27
+ return createBrowserStorage<T>({
28
+ key,
29
+ storage: isBrowser ? window.localStorage : undefined,
30
+ encrypt: options.encrypt,
31
+ encryptionKey: options.encryptionKey,
32
+ })
33
+
34
+ default:
35
+ return createMemoryStorage<T>()
36
+ }
37
+ }
@@ -0,0 +1,6 @@
1
+ export * from './env'
2
+ export * from './encryption'
3
+ export * from './memory'
4
+ export * from './cookie'
5
+ export * from './browser'
6
+ export * from './factory'
@@ -0,0 +1,15 @@
1
+ import type { AuthStorage } from '../../types'
2
+
3
+ export const createMemoryStorage = <T>(): AuthStorage<T> => {
4
+ let value: T | null = null
5
+
6
+ return {
7
+ get: () => value,
8
+ set: (next) => {
9
+ value = next
10
+ },
11
+ clear: () => {
12
+ value = null
13
+ },
14
+ }
15
+ }
@@ -1,4 +1,4 @@
1
- import type { TokenManager } from './types';
1
+ import type { TokenManager } from '../types';
2
2
 
3
3
  /**
4
4
  * Default token manager implementation
@@ -39,7 +39,7 @@ export function Login({
39
39
  subtitle,
40
40
  signupLabel = "Don't have an account?",
41
41
  signupLinkLabel = 'Sign up',
42
- signupHref = '#',
42
+ signupHref,
43
43
  forgotPasswordLabel = 'Forgot your password?',
44
44
  forgotPasswordLinkLabel = 'Reset Here',
45
45
  forgotPasswordHref,
@@ -99,12 +99,14 @@ export function Login({
99
99
  </div>
100
100
 
101
101
  {/* Signup footer */}
102
- <div className="mt-6 text-center text-sm">
103
- {signupLabel}{' '}
104
- <a href={signupHref} className="underline underline-offset-4">
105
- {signupLinkLabel}
106
- </a>
107
- </div>
102
+ {signupHref ? (
103
+ <div className="mt-6 text-center text-sm">
104
+ {signupLabel}{' '}
105
+ <a href={signupHref} className="underline underline-offset-4">
106
+ {signupLinkLabel}
107
+ </a>
108
+ </div>
109
+ ) : null}
108
110
  </div>
109
111
  </div>
110
112
 
@@ -41,6 +41,7 @@ type AdminLayoutProps = {
41
41
  // Header bar customization
42
42
  headerAfterTrigger?: React.ReactNode; // renders right after the collapsible icon trigger
43
43
  headerAfterTheme?: React.ReactNode; // renders right after the ThemeToggle on the right
44
+ onLogout?: () => void | Promise<void>;
44
45
  };
45
46
 
46
47
  function AdminLayoutContent({
@@ -52,6 +53,7 @@ function AdminLayoutContent({
52
53
  sidebarHeaderIcon,
53
54
  headerAfterTrigger,
54
55
  headerAfterTheme,
56
+ onLogout,
55
57
  }: AdminLayoutProps) {
56
58
  const { groups: sidebarGroups } = useAdminSidebarMenu();
57
59
  const location = useLocation();
@@ -212,23 +214,26 @@ function AdminLayoutContent({
212
214
  </Fragment>
213
215
  ))}
214
216
  </SidebarContent>
215
- <SidebarFooter className="bg-sidebar border-t border-sidebar-border px-2 py-3">
216
- <SidebarMenu>
217
- <SidebarMenuItem className="group-data-[collapsible=icon]:flex group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:justify-center">
218
- <SidebarMenuButton
219
- tooltip="Logout"
220
- className="group relative overflow-hidden rounded-lg mx-1 transition-all duration-200 hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:flex group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:!justify-center group-data-[collapsible=icon]:!px-0 group-data-[collapsible=icon]:!gap-0"
221
- >
222
- <div className="flex items-center gap-3 px-3 py-2.5 group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:!justify-center group-data-[collapsible=icon]:!gap-0 group-data-[collapsible=icon]:!px-0">
223
- <LogOut className="h-4 w-4 text-sidebar-foreground group-hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:mx-auto group-data-[collapsible=icon]:mr-0" />
224
- <span className="font-medium text-sidebar-foreground group-hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:hidden">
225
- Logout
226
- </span>
227
- </div>
228
- </SidebarMenuButton>
229
- </SidebarMenuItem>
230
- </SidebarMenu>
231
- </SidebarFooter>
217
+ {onLogout && (
218
+ <SidebarFooter className="bg-sidebar border-t border-sidebar-border px-2 py-3">
219
+ <SidebarMenu>
220
+ <SidebarMenuItem className="group-data-[collapsible=icon]:flex group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:justify-center">
221
+ <SidebarMenuButton
222
+ tooltip="Logout"
223
+ className="cursor-pointer group relative overflow-hidden rounded-lg mx-1 transition-all duration-200 hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:flex group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:!justify-center group-data-[collapsible=icon]:!px-0 group-data-[collapsible=icon]:!gap-0"
224
+ onClick={() => { void onLogout(); }}
225
+ >
226
+ <div className="flex items-center gap-3 px-3 py-2.5 group-data-[collapsible=icon]:w-full group-data-[collapsible=icon]:items-center group-data-[collapsible=icon]:!justify-center group-data-[collapsible=icon]:!gap-0 group-data-[collapsible=icon]:!px-0">
227
+ <LogOut className="h-4 w-4 text-sidebar-foreground group-hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:mx-auto group-data-[collapsible=icon]:mr-0" />
228
+ <span className="font-medium text-sidebar-foreground group-hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:hidden">
229
+ Logout
230
+ </span>
231
+ </div>
232
+ </SidebarMenuButton>
233
+ </SidebarMenuItem>
234
+ </SidebarMenu>
235
+ </SidebarFooter>
236
+ )}
232
237
  </Sidebar>
233
238
  <SidebarInset>
234
239
  <header className="flex h-16 shrink-0 items-center justify-between gap-4 border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 px-6">
@@ -258,6 +263,7 @@ export default function AdminLayout({
258
263
  sidebarHeaderIcon,
259
264
  headerAfterTrigger,
260
265
  headerAfterTheme,
266
+ onLogout,
261
267
  }: AdminLayoutProps) {
262
268
  return (
263
269
  <AdminMenuProvider>
@@ -270,6 +276,7 @@ export default function AdminLayout({
270
276
  sidebarHeaderIcon={sidebarHeaderIcon}
271
277
  headerAfterTrigger={headerAfterTrigger}
272
278
  headerAfterTheme={headerAfterTheme}
279
+ onLogout={onLogout}
273
280
  >
274
281
  {children}
275
282
  </AdminLayoutContent>
@@ -1 +0,0 @@
1
- {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../../../../src/kit/builder/auth/AuthProvider.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1E,KAAK,mBAAmB,GAAG,gBAAgB,CACzC,OAAO,EACP,MAAM,EACN,MAAM,EACN,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EACpC,OAAO,CACR,CAAC;AAIF,KAAK,iBAAiB,CACpB,KAAK,GAAG,OAAO,EACf,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,QAAQ,SAAS,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,WAAW,CACnE,KAAK,EACL,KAAK,EACL,WAAW,CACZ,EACD,YAAY,GAAG,OAAO,IACpB;IACF,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxE,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,GAAG,OAAO,EACf,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,QAAQ,SAAS,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,WAAW,CACnE,KAAK,EACL,KAAK,EACL,WAAW,CACZ,EACD,YAAY,GAAG,OAAO,EACtB,EACA,OAAO,EACP,QAAQ,EACR,QAAe,GAChB,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,2CAkDtE;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,mBAAmB,CAIpD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../src/kit/builder/auth/adapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EAGjB,WAAW,EAIZ,MAAM,SAAS,CAAC;AAMjB;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,GAAG,OAAO,EACf,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,WAAW,SAAS,MAAM,GAAG,MAAM,EACnC,QAAQ,SAAS,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,WAAW,CACnE,KAAK,EACL,KAAK,EACL,WAAW,CACZ,EACD,YAAY,GAAG,OAAO,EAEtB,MAAM,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,GAC3E,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,CAoYhE"}