@ram_28/kf-ai-sdk 1.0.1 → 1.0.3
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/README.md +155 -1
- package/dist/auth/AuthProvider.d.ts +5 -0
- package/dist/auth/AuthProvider.d.ts.map +1 -0
- package/dist/auth/authClient.d.ts +42 -0
- package/dist/auth/authClient.d.ts.map +1 -0
- package/dist/auth/authConfig.d.ts +38 -0
- package/dist/auth/authConfig.d.ts.map +1 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/types.d.ts +156 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/useAuth.d.ts +27 -0
- package/dist/auth/useAuth.d.ts.map +1 -0
- package/dist/index.cjs +12 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2005 -1731
- package/package.json +1 -1
- package/sdk/auth/AuthProvider.tsx +277 -0
- package/sdk/auth/authClient.ts +175 -0
- package/sdk/auth/authConfig.ts +105 -0
- package/sdk/auth/index.ts +40 -0
- package/sdk/auth/types.ts +210 -0
- package/sdk/auth/useAuth.ts +69 -0
- package/sdk/index.ts +4 -1
package/package.json
CHANGED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AUTH PROVIDER COMPONENT
|
|
3
|
+
// ============================================================
|
|
4
|
+
// React Context Provider for authentication state
|
|
5
|
+
|
|
6
|
+
import React, {
|
|
7
|
+
createContext,
|
|
8
|
+
useContext,
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useMemo,
|
|
12
|
+
useState,
|
|
13
|
+
useRef,
|
|
14
|
+
} from "react";
|
|
15
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
AuthContextValue,
|
|
19
|
+
AuthProviderProps,
|
|
20
|
+
AuthStatus,
|
|
21
|
+
UserDetails,
|
|
22
|
+
SessionResponse,
|
|
23
|
+
AuthProviderName,
|
|
24
|
+
LoginOptions,
|
|
25
|
+
LogoutOptions,
|
|
26
|
+
} from "./types";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
fetchSession,
|
|
30
|
+
initiateLogin,
|
|
31
|
+
performLogout,
|
|
32
|
+
AuthenticationError,
|
|
33
|
+
} from "./authClient";
|
|
34
|
+
import { getAuthConfig, configureAuth } from "./authConfig";
|
|
35
|
+
|
|
36
|
+
// ============================================================
|
|
37
|
+
// CONTEXT
|
|
38
|
+
// ============================================================
|
|
39
|
+
|
|
40
|
+
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
41
|
+
|
|
42
|
+
const SESSION_QUERY_KEY = ["auth", "session"] as const;
|
|
43
|
+
|
|
44
|
+
// ============================================================
|
|
45
|
+
// PROVIDER COMPONENT
|
|
46
|
+
// ============================================================
|
|
47
|
+
|
|
48
|
+
export function AuthProvider({
|
|
49
|
+
children,
|
|
50
|
+
config: configOverride,
|
|
51
|
+
onAuthChange,
|
|
52
|
+
onError,
|
|
53
|
+
loadingComponent,
|
|
54
|
+
unauthenticatedComponent,
|
|
55
|
+
skipInitialCheck = false,
|
|
56
|
+
}: AuthProviderProps): React.ReactElement {
|
|
57
|
+
const configApplied = useRef(false);
|
|
58
|
+
|
|
59
|
+
if (configOverride && !configApplied.current) {
|
|
60
|
+
configureAuth(configOverride);
|
|
61
|
+
configApplied.current = true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const queryClient = useQueryClient();
|
|
65
|
+
const authConfig = getAuthConfig();
|
|
66
|
+
|
|
67
|
+
// ============================================================
|
|
68
|
+
// SESSION QUERY
|
|
69
|
+
// ============================================================
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
data: sessionData,
|
|
73
|
+
isLoading,
|
|
74
|
+
error: queryError,
|
|
75
|
+
refetch,
|
|
76
|
+
isFetching,
|
|
77
|
+
} = useQuery<SessionResponse, Error>({
|
|
78
|
+
queryKey: SESSION_QUERY_KEY,
|
|
79
|
+
queryFn: fetchSession,
|
|
80
|
+
enabled: !skipInitialCheck,
|
|
81
|
+
retry: (failureCount, error) => {
|
|
82
|
+
if (error instanceof AuthenticationError) {
|
|
83
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return failureCount < authConfig.retry.count;
|
|
88
|
+
},
|
|
89
|
+
retryDelay: authConfig.retry.delay,
|
|
90
|
+
staleTime: authConfig.staleTime,
|
|
91
|
+
gcTime: authConfig.staleTime * 2,
|
|
92
|
+
refetchOnWindowFocus: authConfig.refetchOnWindowFocus ?? true,
|
|
93
|
+
refetchOnReconnect: authConfig.refetchOnReconnect ?? true,
|
|
94
|
+
refetchInterval: authConfig.sessionCheckInterval || false,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ============================================================
|
|
98
|
+
// DERIVED STATE
|
|
99
|
+
// ============================================================
|
|
100
|
+
|
|
101
|
+
const [error, setError] = useState<Error | null>(null);
|
|
102
|
+
|
|
103
|
+
const status: AuthStatus = useMemo(() => {
|
|
104
|
+
if (isLoading || isFetching) return "loading";
|
|
105
|
+
if (sessionData?.userDetails) return "authenticated";
|
|
106
|
+
return "unauthenticated";
|
|
107
|
+
}, [isLoading, isFetching, sessionData]);
|
|
108
|
+
|
|
109
|
+
const user: UserDetails | null = sessionData?.userDetails || null;
|
|
110
|
+
const staticBaseUrl: string | null = sessionData?.staticBaseUrl || null;
|
|
111
|
+
const buildId: string | null = sessionData?.buildId || null;
|
|
112
|
+
const isAuthenticated = status === "authenticated";
|
|
113
|
+
|
|
114
|
+
// ============================================================
|
|
115
|
+
// REFS FOR CALLBACKS
|
|
116
|
+
// ============================================================
|
|
117
|
+
|
|
118
|
+
const onAuthChangeRef = useRef(onAuthChange);
|
|
119
|
+
onAuthChangeRef.current = onAuthChange;
|
|
120
|
+
|
|
121
|
+
const onErrorRef = useRef(onError);
|
|
122
|
+
onErrorRef.current = onError;
|
|
123
|
+
|
|
124
|
+
// ============================================================
|
|
125
|
+
// EFFECTS
|
|
126
|
+
// ============================================================
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!isLoading) {
|
|
130
|
+
onAuthChangeRef.current?.(status, user);
|
|
131
|
+
}
|
|
132
|
+
}, [status, user, isLoading]);
|
|
133
|
+
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (queryError) {
|
|
136
|
+
setError(queryError);
|
|
137
|
+
onErrorRef.current?.(queryError);
|
|
138
|
+
}
|
|
139
|
+
}, [queryError]);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (
|
|
143
|
+
status === "unauthenticated" &&
|
|
144
|
+
authConfig.autoRedirect &&
|
|
145
|
+
!isLoading
|
|
146
|
+
) {
|
|
147
|
+
initiateLogin();
|
|
148
|
+
}
|
|
149
|
+
}, [status, isLoading, authConfig.autoRedirect]);
|
|
150
|
+
|
|
151
|
+
// ============================================================
|
|
152
|
+
// AUTH OPERATIONS
|
|
153
|
+
// ============================================================
|
|
154
|
+
|
|
155
|
+
const login = useCallback(
|
|
156
|
+
(provider?: AuthProviderName, options?: LoginOptions) => {
|
|
157
|
+
initiateLogin(provider, options);
|
|
158
|
+
},
|
|
159
|
+
[]
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const logout = useCallback(
|
|
163
|
+
async (options?: LogoutOptions) => {
|
|
164
|
+
queryClient.removeQueries({ queryKey: SESSION_QUERY_KEY });
|
|
165
|
+
await performLogout(options);
|
|
166
|
+
},
|
|
167
|
+
[queryClient]
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const refreshSession = useCallback(async (): Promise<SessionResponse | null> => {
|
|
171
|
+
// Prevent concurrent refreshes - return existing data if already fetching
|
|
172
|
+
if (isFetching) {
|
|
173
|
+
return sessionData || null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const result = await refetch();
|
|
178
|
+
return result.data || null;
|
|
179
|
+
} catch (err) {
|
|
180
|
+
setError(err as Error);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}, [refetch, isFetching, sessionData]);
|
|
184
|
+
|
|
185
|
+
const hasRole = useCallback(
|
|
186
|
+
(role: string): boolean => {
|
|
187
|
+
return user?.Role === role;
|
|
188
|
+
},
|
|
189
|
+
[user]
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const hasAnyRole = useCallback(
|
|
193
|
+
(roles: string[]): boolean => {
|
|
194
|
+
return roles.includes(user?.Role || "");
|
|
195
|
+
},
|
|
196
|
+
[user]
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const clearError = useCallback(() => {
|
|
200
|
+
setError(null);
|
|
201
|
+
}, []);
|
|
202
|
+
|
|
203
|
+
const _forceCheck = useCallback(() => {
|
|
204
|
+
refetch();
|
|
205
|
+
}, [refetch]);
|
|
206
|
+
|
|
207
|
+
// ============================================================
|
|
208
|
+
// CONTEXT VALUE
|
|
209
|
+
// ============================================================
|
|
210
|
+
|
|
211
|
+
const contextValue: AuthContextValue = useMemo(
|
|
212
|
+
() => ({
|
|
213
|
+
user,
|
|
214
|
+
staticBaseUrl,
|
|
215
|
+
buildId,
|
|
216
|
+
status,
|
|
217
|
+
isAuthenticated,
|
|
218
|
+
isLoading,
|
|
219
|
+
login,
|
|
220
|
+
logout,
|
|
221
|
+
refreshSession,
|
|
222
|
+
hasRole,
|
|
223
|
+
hasAnyRole,
|
|
224
|
+
error,
|
|
225
|
+
clearError,
|
|
226
|
+
_forceCheck,
|
|
227
|
+
}),
|
|
228
|
+
[
|
|
229
|
+
user,
|
|
230
|
+
staticBaseUrl,
|
|
231
|
+
buildId,
|
|
232
|
+
status,
|
|
233
|
+
isAuthenticated,
|
|
234
|
+
isLoading,
|
|
235
|
+
login,
|
|
236
|
+
logout,
|
|
237
|
+
refreshSession,
|
|
238
|
+
hasRole,
|
|
239
|
+
hasAnyRole,
|
|
240
|
+
error,
|
|
241
|
+
clearError,
|
|
242
|
+
_forceCheck,
|
|
243
|
+
]
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// ============================================================
|
|
247
|
+
// RENDER
|
|
248
|
+
// ============================================================
|
|
249
|
+
|
|
250
|
+
if (status === "loading" && loadingComponent) {
|
|
251
|
+
return <>{loadingComponent}</>;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
status === "unauthenticated" &&
|
|
256
|
+
!authConfig.autoRedirect &&
|
|
257
|
+
unauthenticatedComponent
|
|
258
|
+
) {
|
|
259
|
+
return <>{unauthenticatedComponent}</>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ============================================================
|
|
268
|
+
// CONTEXT HOOK (internal)
|
|
269
|
+
// ============================================================
|
|
270
|
+
|
|
271
|
+
export function useAuthContext(): AuthContextValue {
|
|
272
|
+
const context = useContext(AuthContext);
|
|
273
|
+
if (!context) {
|
|
274
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
275
|
+
}
|
|
276
|
+
return context;
|
|
277
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AUTH API CLIENT
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Low-level functions for authentication API calls
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
SessionResponse,
|
|
8
|
+
AuthProviderName,
|
|
9
|
+
LoginOptions,
|
|
10
|
+
LogoutOptions,
|
|
11
|
+
} from "./types";
|
|
12
|
+
import {
|
|
13
|
+
getAuthBaseUrl,
|
|
14
|
+
getAuthConfig,
|
|
15
|
+
getProviderConfig,
|
|
16
|
+
} from "./authConfig";
|
|
17
|
+
import { getDefaultHeaders } from "../api/client";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom error class for authentication errors
|
|
21
|
+
*/
|
|
22
|
+
export class AuthenticationError extends Error {
|
|
23
|
+
public readonly statusCode: number;
|
|
24
|
+
|
|
25
|
+
constructor(message: string, statusCode: number) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "AuthenticationError";
|
|
28
|
+
this.statusCode = statusCode;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Fetch current session from the server
|
|
34
|
+
* Calls the session endpoint (default: /api/id)
|
|
35
|
+
*
|
|
36
|
+
* @throws AuthenticationError if session check fails or user is not authenticated
|
|
37
|
+
*/
|
|
38
|
+
export async function fetchSession(): Promise<SessionResponse> {
|
|
39
|
+
const config = getAuthConfig();
|
|
40
|
+
const baseUrl = getAuthBaseUrl();
|
|
41
|
+
const headers = getDefaultHeaders();
|
|
42
|
+
|
|
43
|
+
const response = await fetch(`${baseUrl}${config.sessionEndpoint}`, {
|
|
44
|
+
method: "GET",
|
|
45
|
+
headers,
|
|
46
|
+
credentials: "include",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
if (response.status === 401 || response.status === 403) {
|
|
51
|
+
throw new AuthenticationError("Not authenticated", response.status);
|
|
52
|
+
}
|
|
53
|
+
throw new AuthenticationError(
|
|
54
|
+
`Session check failed: ${response.statusText}`,
|
|
55
|
+
response.status
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const data: SessionResponse = await response.json();
|
|
60
|
+
return data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Initiates OAuth login flow by redirecting to the auth provider.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* This function redirects the browser and never resolves.
|
|
68
|
+
* Any code after calling this function will not execute.
|
|
69
|
+
*
|
|
70
|
+
* @param provider - OAuth provider to use (defaults to config.defaultProvider)
|
|
71
|
+
* @param options - Login options including callback URL and custom params
|
|
72
|
+
* @returns Promise that never resolves (browser redirects away)
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // Correct usage - no code after login()
|
|
77
|
+
* function handleLoginClick() {
|
|
78
|
+
* login('google');
|
|
79
|
+
* // Don't put code here - it won't run
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function initiateLogin(
|
|
84
|
+
provider?: AuthProviderName,
|
|
85
|
+
options?: LoginOptions
|
|
86
|
+
): Promise<never> {
|
|
87
|
+
return new Promise(() => {
|
|
88
|
+
const config = getAuthConfig();
|
|
89
|
+
const baseUrl = getAuthBaseUrl();
|
|
90
|
+
|
|
91
|
+
// Validate base URL
|
|
92
|
+
if (!baseUrl) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
'Auth base URL is not configured. Call setApiBaseUrl("https://...") or configureAuth({ baseUrl: "https://..." }) first.'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const selectedProvider = provider || config.defaultProvider;
|
|
99
|
+
const providerConfig = getProviderConfig(selectedProvider);
|
|
100
|
+
|
|
101
|
+
// Validate provider config
|
|
102
|
+
if (!providerConfig) {
|
|
103
|
+
const availableProviders = Object.keys(config.providers || {}).join(", ") || "none";
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Auth provider "${selectedProvider}" is not configured. Available providers: ${availableProviders}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Validate login path
|
|
110
|
+
if (!providerConfig.loginPath) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Login path not configured for provider "${selectedProvider}". ` +
|
|
113
|
+
`Configure it with: configureAuth({ providers: { ${selectedProvider}: { loginPath: '/api/auth/...' } } })`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Validate URL construction
|
|
118
|
+
let loginUrl: URL;
|
|
119
|
+
try {
|
|
120
|
+
loginUrl = new URL(`${baseUrl}${providerConfig.loginPath}`);
|
|
121
|
+
} catch {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Failed to construct login URL. Base URL: "${baseUrl}", Login path: "${providerConfig.loginPath}". ` +
|
|
124
|
+
`Ensure baseUrl is a valid URL (e.g., "https://example.com").`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (options?.callbackUrl || config.callbackUrl) {
|
|
129
|
+
loginUrl.searchParams.set(
|
|
130
|
+
"callbackUrl",
|
|
131
|
+
options?.callbackUrl || config.callbackUrl || window.location.href
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (options?.params) {
|
|
136
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
137
|
+
loginUrl.searchParams.set(key, value);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
window.location.href = loginUrl.toString();
|
|
142
|
+
// Promise never resolves - browser navigates away
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Logout the current user
|
|
148
|
+
* Optionally calls the logout endpoint before clearing client state
|
|
149
|
+
*/
|
|
150
|
+
export async function performLogout(options?: LogoutOptions): Promise<void> {
|
|
151
|
+
const config = getAuthConfig();
|
|
152
|
+
const baseUrl = getAuthBaseUrl();
|
|
153
|
+
const headers = getDefaultHeaders();
|
|
154
|
+
|
|
155
|
+
const providerConfig = getProviderConfig(config.defaultProvider);
|
|
156
|
+
const logoutPath = providerConfig?.logoutPath;
|
|
157
|
+
|
|
158
|
+
if (logoutPath && options?.callLogoutEndpoint !== false) {
|
|
159
|
+
try {
|
|
160
|
+
await fetch(`${baseUrl}${logoutPath}`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers,
|
|
163
|
+
credentials: "include",
|
|
164
|
+
});
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.warn("Logout endpoint call failed:", error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (options?.redirectUrl) {
|
|
171
|
+
window.location.href = options.redirectUrl;
|
|
172
|
+
} else if (config.loginRedirectUrl) {
|
|
173
|
+
window.location.href = config.loginRedirectUrl;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AUTH CONFIGURATION
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Global auth configuration following the setApiBaseUrl pattern
|
|
5
|
+
|
|
6
|
+
import type { AuthConfig, AuthProviderName, AuthEndpointConfig } from "./types";
|
|
7
|
+
import { getApiBaseUrl } from "../api/client";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default auth configuration
|
|
11
|
+
*/
|
|
12
|
+
const defaultAuthConfig: AuthConfig = {
|
|
13
|
+
sessionEndpoint: "/api/id",
|
|
14
|
+
providers: {
|
|
15
|
+
google: {
|
|
16
|
+
loginPath: "/api/auth/google/login",
|
|
17
|
+
logoutPath: "/api/auth/logout",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultProvider: "google",
|
|
21
|
+
autoRedirect: true,
|
|
22
|
+
sessionCheckInterval: 0,
|
|
23
|
+
retry: {
|
|
24
|
+
count: 3,
|
|
25
|
+
delay: 1000,
|
|
26
|
+
},
|
|
27
|
+
staleTime: 5 * 60 * 1000,
|
|
28
|
+
refetchOnWindowFocus: true,
|
|
29
|
+
refetchOnReconnect: true,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Current auth configuration (mutable)
|
|
34
|
+
*/
|
|
35
|
+
let authConfig: AuthConfig = { ...defaultAuthConfig };
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Configure authentication settings globally
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* configureAuth({
|
|
42
|
+
* defaultProvider: "google",
|
|
43
|
+
* autoRedirect: true,
|
|
44
|
+
* providers: {
|
|
45
|
+
* google: { loginPath: "/api/auth/google/login" },
|
|
46
|
+
* microsoft: { loginPath: "/api/auth/microsoft/login" },
|
|
47
|
+
* },
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function configureAuth(config: Partial<AuthConfig>): void {
|
|
52
|
+
authConfig = {
|
|
53
|
+
...authConfig,
|
|
54
|
+
...config,
|
|
55
|
+
providers: {
|
|
56
|
+
...authConfig.providers,
|
|
57
|
+
...config.providers,
|
|
58
|
+
},
|
|
59
|
+
retry: {
|
|
60
|
+
...authConfig.retry,
|
|
61
|
+
...config.retry,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Add or update an auth provider configuration
|
|
68
|
+
*/
|
|
69
|
+
export function setAuthProvider(
|
|
70
|
+
provider: AuthProviderName,
|
|
71
|
+
config: AuthEndpointConfig
|
|
72
|
+
): void {
|
|
73
|
+
authConfig.providers[provider] = config;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get current auth configuration
|
|
78
|
+
*/
|
|
79
|
+
export function getAuthConfig(): Readonly<AuthConfig> {
|
|
80
|
+
return { ...authConfig };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get the base URL for auth endpoints
|
|
85
|
+
* Falls back to API base URL if not explicitly set
|
|
86
|
+
*/
|
|
87
|
+
export function getAuthBaseUrl(): string {
|
|
88
|
+
return authConfig.baseUrl || getApiBaseUrl();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get endpoint configuration for a specific provider
|
|
93
|
+
*/
|
|
94
|
+
export function getProviderConfig(
|
|
95
|
+
provider: AuthProviderName
|
|
96
|
+
): AuthEndpointConfig | undefined {
|
|
97
|
+
return authConfig.providers[provider];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Reset auth configuration to defaults
|
|
102
|
+
*/
|
|
103
|
+
export function resetAuthConfig(): void {
|
|
104
|
+
authConfig = { ...defaultAuthConfig };
|
|
105
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// AUTH MODULE EXPORTS
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
// Provider component
|
|
6
|
+
export { AuthProvider } from "./AuthProvider";
|
|
7
|
+
|
|
8
|
+
// Main hook
|
|
9
|
+
export { useAuth } from "./useAuth";
|
|
10
|
+
|
|
11
|
+
// Configuration functions
|
|
12
|
+
export {
|
|
13
|
+
configureAuth,
|
|
14
|
+
setAuthProvider,
|
|
15
|
+
getAuthConfig,
|
|
16
|
+
getAuthBaseUrl,
|
|
17
|
+
resetAuthConfig,
|
|
18
|
+
} from "./authConfig";
|
|
19
|
+
|
|
20
|
+
// API client functions (for advanced use cases)
|
|
21
|
+
export {
|
|
22
|
+
fetchSession,
|
|
23
|
+
initiateLogin,
|
|
24
|
+
performLogout,
|
|
25
|
+
AuthenticationError,
|
|
26
|
+
} from "./authClient";
|
|
27
|
+
|
|
28
|
+
// Type exports
|
|
29
|
+
export type {
|
|
30
|
+
UserDetails,
|
|
31
|
+
SessionResponse,
|
|
32
|
+
AuthStatus,
|
|
33
|
+
AuthConfig,
|
|
34
|
+
AuthProviderName,
|
|
35
|
+
AuthEndpointConfig,
|
|
36
|
+
AuthProviderProps,
|
|
37
|
+
UseAuthReturn,
|
|
38
|
+
LoginOptions,
|
|
39
|
+
LogoutOptions,
|
|
40
|
+
} from "./types";
|