@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.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1013 -21
- package/dist/kit/builder/auth/components/Can.d.ts +13 -0
- package/dist/kit/builder/auth/components/Can.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/RequireAuth.d.ts +45 -0
- package/dist/kit/builder/auth/components/RequireAuth.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts +8 -0
- package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/ShowWhenError.d.ts +8 -0
- package/dist/kit/builder/auth/components/ShowWhenError.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts +8 -0
- package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts +8 -0
- package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts.map +1 -0
- package/dist/kit/builder/auth/components/withPermission.d.ts +7 -0
- package/dist/kit/builder/auth/components/withPermission.d.ts.map +1 -0
- package/dist/kit/builder/auth/hooks/action-hooks.d.ts +18 -0
- package/dist/kit/builder/auth/hooks/action-hooks.d.ts.map +1 -0
- package/dist/kit/builder/auth/hooks/core-hooks.d.ts +56 -0
- package/dist/kit/builder/auth/hooks/core-hooks.d.ts.map +1 -0
- package/dist/kit/builder/auth/hooks/index.d.ts +5 -0
- package/dist/kit/builder/auth/hooks/index.d.ts.map +1 -0
- package/dist/kit/builder/auth/hooks/permission-hooks.d.ts +18 -0
- package/dist/kit/builder/auth/hooks/permission-hooks.d.ts.map +1 -0
- package/dist/kit/builder/auth/hooks/token-hooks.d.ts +13 -0
- package/dist/kit/builder/auth/hooks/token-hooks.d.ts.map +1 -0
- package/dist/kit/builder/auth/index.d.ts +14 -8
- package/dist/kit/builder/auth/index.d.ts.map +1 -1
- package/dist/kit/builder/auth/{AuthProvider.d.ts → providers/AuthProvider.d.ts} +1 -1
- package/dist/kit/builder/auth/providers/AuthProvider.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/adapter-config.d.ts +31 -0
- package/dist/kit/builder/auth/types/adapter-config.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/adapter.d.ts +80 -0
- package/dist/kit/builder/auth/types/adapter.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/core.d.ts +16 -0
- package/dist/kit/builder/auth/types/core.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/index.d.ts +10 -0
- package/dist/kit/builder/auth/types/index.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/middleware.d.ts +11 -0
- package/dist/kit/builder/auth/types/middleware.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/permissions.d.ts +17 -0
- package/dist/kit/builder/auth/types/permissions.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/state.d.ts +13 -0
- package/dist/kit/builder/auth/types/state.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/storage.d.ts +20 -0
- package/dist/kit/builder/auth/types/storage.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/token-manager.d.ts +7 -0
- package/dist/kit/builder/auth/types/token-manager.d.ts.map +1 -0
- package/dist/kit/builder/auth/types/utils.d.ts +7 -0
- package/dist/kit/builder/auth/types/utils.d.ts.map +1 -0
- package/dist/kit/builder/auth/{adapter.d.ts → utils/auth-adapter.d.ts} +2 -2
- package/dist/kit/builder/auth/utils/auth-adapter.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts +4 -0
- package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts +6 -0
- package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts +6 -0
- package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts +9 -0
- package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/index.d.ts +7 -0
- package/dist/kit/builder/auth/utils/client-adapters/index.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts +9 -0
- package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts +14 -0
- package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts.map +1 -0
- package/dist/kit/builder/auth/{permission-checker.d.ts → utils/permission-checker.d.ts} +1 -1
- package/dist/kit/builder/auth/utils/permission-checker.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/browser.d.ts +11 -0
- package/dist/kit/builder/auth/utils/storage/browser.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/cookie.d.ts +3 -0
- package/dist/kit/builder/auth/utils/storage/cookie.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/encryption.d.ts +7 -0
- package/dist/kit/builder/auth/utils/storage/encryption.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/env.d.ts +2 -0
- package/dist/kit/builder/auth/utils/storage/env.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/factory.d.ts +6 -0
- package/dist/kit/builder/auth/utils/storage/factory.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/index.d.ts +7 -0
- package/dist/kit/builder/auth/utils/storage/index.d.ts.map +1 -0
- package/dist/kit/builder/auth/utils/storage/memory.d.ts +3 -0
- package/dist/kit/builder/auth/utils/storage/memory.d.ts.map +1 -0
- package/dist/kit/builder/auth/{token-manager.d.ts → utils/token-manager.d.ts} +1 -1
- package/dist/kit/builder/auth/utils/token-manager.d.ts.map +1 -0
- package/dist/kit/components/login/Login.d.ts.map +1 -1
- package/dist/kit/layouts/admin/components/AdminLayout.d.ts +2 -1
- package/dist/kit/layouts/admin/components/AdminLayout.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/kit/builder/auth/components/Can.tsx +27 -0
- package/src/kit/builder/auth/components/RequireAuth.tsx +78 -0
- package/src/kit/builder/auth/components/ShowWhenAuthenticated.tsx +10 -0
- package/src/kit/builder/auth/components/ShowWhenError.tsx +10 -0
- package/src/kit/builder/auth/components/ShowWhenLoading.tsx +10 -0
- package/src/kit/builder/auth/components/ShowWhenUnauthenticated.tsx +10 -0
- package/src/kit/builder/auth/components/withPermission.tsx +23 -0
- package/src/kit/builder/auth/hooks/action-hooks.ts +34 -0
- package/src/kit/builder/auth/hooks/core-hooks.ts +65 -0
- package/src/kit/builder/auth/hooks/index.ts +4 -0
- package/src/kit/builder/auth/hooks/permission-hooks.ts +43 -0
- package/src/kit/builder/auth/hooks/token-hooks.ts +25 -0
- package/src/kit/builder/auth/index.ts +16 -18
- package/src/kit/builder/auth/{AuthProvider.tsx → providers/AuthProvider.tsx} +1 -1
- package/src/kit/builder/auth/types/adapter-config.ts +44 -0
- package/src/kit/builder/auth/types/adapter.ts +132 -0
- package/src/kit/builder/auth/types/core.ts +27 -0
- package/src/kit/builder/auth/types/index.ts +9 -0
- package/src/kit/builder/auth/types/middleware.ts +20 -0
- package/src/kit/builder/auth/types/permissions.ts +23 -0
- package/src/kit/builder/auth/types/state.ts +16 -0
- package/src/kit/builder/auth/types/storage.ts +21 -0
- package/src/kit/builder/auth/types/token-manager.ts +9 -0
- package/src/kit/builder/auth/types/utils.ts +55 -0
- package/src/kit/builder/auth/{adapter.ts → utils/auth-adapter.ts} +3 -2
- package/src/kit/builder/auth/utils/client-adapters/apollo-link.ts +30 -0
- package/src/kit/builder/auth/utils/client-adapters/axios.ts +61 -0
- package/src/kit/builder/auth/utils/client-adapters/fetch.ts +48 -0
- package/src/kit/builder/auth/utils/client-adapters/graphql.ts +60 -0
- package/src/kit/builder/auth/utils/client-adapters/index.ts +6 -0
- package/src/kit/builder/auth/utils/client-adapters/rest.ts +60 -0
- package/src/kit/builder/auth/utils/client-adapters/urql-exchange.ts +76 -0
- package/src/kit/builder/auth/{permission-checker.ts → utils/permission-checker.ts} +1 -1
- package/src/kit/builder/auth/utils/storage/browser.ts +99 -0
- package/src/kit/builder/auth/utils/storage/cookie.ts +116 -0
- package/src/kit/builder/auth/utils/storage/encryption.ts +80 -0
- package/src/kit/builder/auth/utils/storage/env.ts +2 -0
- package/src/kit/builder/auth/utils/storage/factory.ts +37 -0
- package/src/kit/builder/auth/utils/storage/index.ts +6 -0
- package/src/kit/builder/auth/utils/storage/memory.ts +15 -0
- package/src/kit/builder/auth/{token-manager.ts → utils/token-manager.ts} +1 -1
- package/src/kit/components/login/Login.tsx +9 -7
- package/src/kit/layouts/admin/components/AdminLayout.tsx +24 -17
- package/dist/kit/builder/auth/AuthProvider.d.ts.map +0 -1
- package/dist/kit/builder/auth/adapter.d.ts.map +0 -1
- package/dist/kit/builder/auth/client-adapters.d.ts +0 -149
- package/dist/kit/builder/auth/client-adapters.d.ts.map +0 -1
- package/dist/kit/builder/auth/components.d.ts +0 -119
- package/dist/kit/builder/auth/components.d.ts.map +0 -1
- package/dist/kit/builder/auth/hooks.d.ts +0 -158
- package/dist/kit/builder/auth/hooks.d.ts.map +0 -1
- package/dist/kit/builder/auth/permission-checker.d.ts.map +0 -1
- package/dist/kit/builder/auth/storage.d.ts +0 -17
- package/dist/kit/builder/auth/storage.d.ts.map +0 -1
- package/dist/kit/builder/auth/token-manager.d.ts.map +0 -1
- package/dist/kit/builder/auth/types.d.ts +0 -183
- package/dist/kit/builder/auth/types.d.ts.map +0 -1
- package/src/kit/builder/auth/client-adapters.ts +0 -398
- package/src/kit/builder/auth/components.tsx +0 -221
- package/src/kit/builder/auth/hooks.ts +0 -237
- package/src/kit/builder/auth/storage.ts +0 -366
- package/src/kit/builder/auth/types.ts +0 -393
|
@@ -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
|
+
}
|
|
@@ -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,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,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
|
+
}
|
|
@@ -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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
216
|
-
<
|
|
217
|
-
<
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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"}
|