@k3-universe/react-kit 0.0.29 → 0.0.31
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 +1023 -25
- 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 +2 -1
- 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/dist/kit/themes/clean-slate.css +28 -4
- package/dist/kit/themes/default.css +28 -4
- package/dist/kit/themes/minimal-modern.css +28 -4
- package/dist/kit/themes/spotify.css +28 -4
- 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 +36 -21
- 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,16 @@
|
|
|
1
|
+
import type { AuthStatus } from './core';
|
|
2
|
+
|
|
3
|
+
export type AuthAdapterState<TSession, TUser, TRole, TPermission> = {
|
|
4
|
+
status: AuthStatus;
|
|
5
|
+
session: TSession | null;
|
|
6
|
+
user: TUser | null;
|
|
7
|
+
roles: TRole[];
|
|
8
|
+
permissions: TPermission[];
|
|
9
|
+
error?: unknown;
|
|
10
|
+
lastRefresh?: number;
|
|
11
|
+
tokenExpiresIn?: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type AuthAdapterSubscriber<TSession, TUser, TRole, TPermission> = (
|
|
15
|
+
state: AuthAdapterState<TSession, TUser, TRole, TPermission>,
|
|
16
|
+
) => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AuthStorage<TSession> = {
|
|
2
|
+
get: () => Promise<TSession | null> | TSession | null;
|
|
3
|
+
set: (value: TSession | null) => Promise<void> | void;
|
|
4
|
+
clear: () => Promise<void> | void;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type StorageType = 'local' | 'session' | 'cookie' | 'memory';
|
|
8
|
+
|
|
9
|
+
export type StorageOptions = {
|
|
10
|
+
key?: string;
|
|
11
|
+
encrypt?: boolean;
|
|
12
|
+
encryptionKey?: string;
|
|
13
|
+
storage?: StorageType;
|
|
14
|
+
cookieOptions?: {
|
|
15
|
+
domain?: string;
|
|
16
|
+
path?: string;
|
|
17
|
+
secure?: boolean;
|
|
18
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
19
|
+
maxAge?: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type TokenManager = {
|
|
2
|
+
isExpired: (expiresAt?: number | null) => boolean;
|
|
3
|
+
shouldRefresh: (expiresAt?: number | null, threshold?: number) => boolean;
|
|
4
|
+
getTimeUntilExpiry: (expiresAt?: number | null) => number;
|
|
5
|
+
scheduleRefresh?: (
|
|
6
|
+
callback: () => void,
|
|
7
|
+
expiresAt?: number | null,
|
|
8
|
+
) => () => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Utility Types
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import type { AuthAdapter } from "./adapter";
|
|
6
|
+
|
|
7
|
+
export type InferSession<T> = T extends AuthAdapter<
|
|
8
|
+
infer _User,
|
|
9
|
+
infer _Role extends string,
|
|
10
|
+
infer _Permission extends string,
|
|
11
|
+
infer TSession,
|
|
12
|
+
infer _Credentials
|
|
13
|
+
>
|
|
14
|
+
? TSession
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
export type InferUser<T> = T extends AuthAdapter<
|
|
18
|
+
infer TUser,
|
|
19
|
+
infer _Role extends string,
|
|
20
|
+
infer _Permission extends string,
|
|
21
|
+
infer _Session,
|
|
22
|
+
infer _Credentials
|
|
23
|
+
>
|
|
24
|
+
? TUser
|
|
25
|
+
: never;
|
|
26
|
+
|
|
27
|
+
export type InferRole<T> = T extends AuthAdapter<
|
|
28
|
+
infer _User,
|
|
29
|
+
infer TRole extends string,
|
|
30
|
+
infer _Permission extends string,
|
|
31
|
+
infer _Session,
|
|
32
|
+
infer _Credentials
|
|
33
|
+
>
|
|
34
|
+
? TRole
|
|
35
|
+
: never;
|
|
36
|
+
|
|
37
|
+
export type InferPermission<T> = T extends AuthAdapter<
|
|
38
|
+
infer _User,
|
|
39
|
+
infer _Role extends string,
|
|
40
|
+
infer TPermission extends string,
|
|
41
|
+
infer _Session,
|
|
42
|
+
infer _Credentials
|
|
43
|
+
>
|
|
44
|
+
? TPermission
|
|
45
|
+
: never;
|
|
46
|
+
|
|
47
|
+
export type InferCredentials<T> = T extends AuthAdapter<
|
|
48
|
+
infer _User,
|
|
49
|
+
infer _Role extends string,
|
|
50
|
+
infer _Permission extends string,
|
|
51
|
+
infer _Session,
|
|
52
|
+
infer TCredentials
|
|
53
|
+
>
|
|
54
|
+
? TCredentials
|
|
55
|
+
: never;
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
AuthStatus,
|
|
17
17
|
PermissionPolicy,
|
|
18
18
|
PermissionRule,
|
|
19
|
-
} from '
|
|
19
|
+
} from '../types';
|
|
20
20
|
|
|
21
21
|
const unique = <T>(values: T[]): T[] => {
|
|
22
22
|
return Array.from(new Set(values));
|
|
@@ -334,7 +334,8 @@ export function createAuthAdapter<
|
|
|
334
334
|
return nextState;
|
|
335
335
|
} catch (error) {
|
|
336
336
|
await executeMiddleware('onError', error);
|
|
337
|
-
|
|
337
|
+
await setSession(null, 'error', error);
|
|
338
|
+
throw error;
|
|
338
339
|
}
|
|
339
340
|
};
|
|
340
341
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function createApolloAuthLink(
|
|
2
|
+
getToken: () => string | null,
|
|
3
|
+
options?: {
|
|
4
|
+
tokenType?: string
|
|
5
|
+
},
|
|
6
|
+
) {
|
|
7
|
+
const tokenType = options?.tokenType ?? 'Bearer'
|
|
8
|
+
|
|
9
|
+
// This requires @apollo/client to be installed
|
|
10
|
+
// We'll return a function that creates the link to avoid direct dependency
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
return (ApolloLink: any) => {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
return new ApolloLink((operation: any, forward: any) => {
|
|
15
|
+
const token = getToken()
|
|
16
|
+
|
|
17
|
+
if (token) {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
operation.setContext(({ headers = {} }: any) => ({
|
|
20
|
+
headers: {
|
|
21
|
+
...headers,
|
|
22
|
+
authorization: `${tokenType} ${token}`,
|
|
23
|
+
},
|
|
24
|
+
}))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return forward(operation)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function createAxiosAuthInterceptor(
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
+
axiosInstance: any,
|
|
4
|
+
getToken: () => string | null,
|
|
5
|
+
options?: {
|
|
6
|
+
tokenType?: string
|
|
7
|
+
onTokenExpired?: () => void
|
|
8
|
+
refreshToken?: () => Promise<void>
|
|
9
|
+
},
|
|
10
|
+
) {
|
|
11
|
+
const tokenType = options?.tokenType ?? 'Bearer'
|
|
12
|
+
|
|
13
|
+
// Request interceptor
|
|
14
|
+
axiosInstance.interceptors.request.use(
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
async (config: any) => {
|
|
17
|
+
const token = getToken()
|
|
18
|
+
if (token) {
|
|
19
|
+
config.headers.Authorization = `${tokenType} ${token}`
|
|
20
|
+
}
|
|
21
|
+
return config
|
|
22
|
+
},
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
(error: any) => Promise.reject(error),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Response interceptor for token refresh
|
|
28
|
+
if (options?.refreshToken) {
|
|
29
|
+
axiosInstance.interceptors.response.use(
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
(response: any) => response,
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
async (error: any) => {
|
|
34
|
+
const originalRequest = error.config
|
|
35
|
+
|
|
36
|
+
// If token expired and we haven't retried yet
|
|
37
|
+
if (
|
|
38
|
+
error.response?.status === 401 &&
|
|
39
|
+
!originalRequest._retry &&
|
|
40
|
+
options.refreshToken
|
|
41
|
+
) {
|
|
42
|
+
originalRequest._retry = true
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
await options.refreshToken()
|
|
46
|
+
const token = getToken()
|
|
47
|
+
if (token) {
|
|
48
|
+
originalRequest.headers.Authorization = `${tokenType} ${token}`
|
|
49
|
+
}
|
|
50
|
+
return axiosInstance(originalRequest)
|
|
51
|
+
} catch (refreshError) {
|
|
52
|
+
options.onTokenExpired?.()
|
|
53
|
+
return Promise.reject(refreshError)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Promise.reject(error)
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function createAuthFetch(
|
|
2
|
+
getToken: () => string | null,
|
|
3
|
+
options?: {
|
|
4
|
+
tokenType?: string
|
|
5
|
+
onTokenExpired?: () => void
|
|
6
|
+
refreshToken?: () => Promise<void>
|
|
7
|
+
},
|
|
8
|
+
): typeof fetch {
|
|
9
|
+
const tokenType = options?.tokenType ?? 'Bearer'
|
|
10
|
+
|
|
11
|
+
return async (
|
|
12
|
+
input: RequestInfo | URL,
|
|
13
|
+
init?: RequestInit,
|
|
14
|
+
): Promise<Response> => {
|
|
15
|
+
const token = getToken()
|
|
16
|
+
const headers = new Headers(init?.headers)
|
|
17
|
+
|
|
18
|
+
if (token) {
|
|
19
|
+
headers.set('Authorization', `${tokenType} ${token}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const response = await fetch(input, {
|
|
23
|
+
...init,
|
|
24
|
+
headers,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Handle token expiration
|
|
28
|
+
if (response.status === 401 && options?.refreshToken) {
|
|
29
|
+
try {
|
|
30
|
+
await options.refreshToken()
|
|
31
|
+
const newToken = getToken()
|
|
32
|
+
|
|
33
|
+
if (newToken) {
|
|
34
|
+
headers.set('Authorization', `${tokenType} ${newToken}`)
|
|
35
|
+
return await fetch(input, {
|
|
36
|
+
...init,
|
|
37
|
+
headers,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
options.onTokenExpired?.()
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return response
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AuthAdapterConfig, AuthSession, GraphQLAuthClient } from '../../types'
|
|
2
|
+
|
|
3
|
+
export type GraphQLClientAdapterOptions<
|
|
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: GraphQLAuthClient<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 createGraphQLAuthAdapter<
|
|
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: GraphQLClientAdapterOptions<
|
|
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,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
|
+
}
|