@aifabrix/miso-client 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/express/client-token-endpoint.d.ts +76 -0
- package/dist/express/client-token-endpoint.d.ts.map +1 -0
- package/dist/express/client-token-endpoint.js +108 -0
- package/dist/express/client-token-endpoint.js.map +1 -0
- package/dist/express/index.d.ts +2 -1
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/index.js +8 -3
- package/dist/express/index.js.map +1 -1
- package/dist/express/response-middleware.d.ts.map +1 -1
- package/dist/express/response-middleware.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/services/auth.service.js +3 -3
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/browser-permission.service.d.ts +60 -0
- package/dist/services/browser-permission.service.d.ts.map +1 -0
- package/dist/services/browser-permission.service.js +159 -0
- package/dist/services/browser-permission.service.js.map +1 -0
- package/dist/services/browser-role.service.d.ts +60 -0
- package/dist/services/browser-role.service.d.ts.map +1 -0
- package/dist/services/browser-role.service.js +159 -0
- package/dist/services/browser-role.service.js.map +1 -0
- package/dist/services/cache.service.d.ts.map +1 -1
- package/dist/services/cache.service.js +4 -0
- package/dist/services/cache.service.js.map +1 -1
- package/dist/services/logger.service.d.ts +4 -0
- package/dist/services/logger.service.d.ts.map +1 -1
- package/dist/services/logger.service.js +21 -0
- package/dist/services/logger.service.js.map +1 -1
- package/dist/utils/audit-log-queue.d.ts +4 -0
- package/dist/utils/audit-log-queue.d.ts.map +1 -1
- package/dist/utils/audit-log-queue.js +22 -2
- package/dist/utils/audit-log-queue.js.map +1 -1
- package/dist/utils/browser-jwt-decoder.d.ts +20 -0
- package/dist/utils/browser-jwt-decoder.d.ts.map +1 -0
- package/dist/utils/browser-jwt-decoder.js +75 -0
- package/dist/utils/browser-jwt-decoder.js.map +1 -0
- package/dist/utils/controller-url-resolver.d.ts +16 -0
- package/dist/utils/controller-url-resolver.d.ts.map +1 -1
- package/dist/utils/controller-url-resolver.js +12 -0
- package/dist/utils/controller-url-resolver.js.map +1 -1
- package/dist/utils/data-client-audit.d.ts.map +1 -1
- package/dist/utils/data-client-audit.js +19 -8
- package/dist/utils/data-client-audit.js.map +1 -1
- package/dist/utils/data-client-auth.d.ts +2 -3
- package/dist/utils/data-client-auth.d.ts.map +1 -1
- package/dist/utils/data-client-auth.js +121 -80
- package/dist/utils/data-client-auth.js.map +1 -1
- package/dist/utils/data-client-auto-init.d.ts +66 -0
- package/dist/utils/data-client-auto-init.d.ts.map +1 -0
- package/dist/utils/data-client-auto-init.js +215 -0
- package/dist/utils/data-client-auto-init.js.map +1 -0
- package/dist/utils/data-client-redirect.d.ts +22 -0
- package/dist/utils/data-client-redirect.d.ts.map +1 -0
- package/dist/utils/data-client-redirect.js +345 -0
- package/dist/utils/data-client-redirect.js.map +1 -0
- package/dist/utils/data-client-request.d.ts.map +1 -1
- package/dist/utils/data-client-request.js +5 -2
- package/dist/utils/data-client-request.js.map +1 -1
- package/dist/utils/data-client.d.ts +103 -0
- package/dist/utils/data-client.d.ts.map +1 -1
- package/dist/utils/data-client.js +271 -2
- package/dist/utils/data-client.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataClient Auto-Initialization Helper
|
|
3
|
+
* Automatically fetches configuration from server and initializes DataClient
|
|
4
|
+
*
|
|
5
|
+
* Provides zero-config client-side setup for DataClient initialization
|
|
6
|
+
*/
|
|
7
|
+
import { DataClient } from "./data-client";
|
|
8
|
+
import { DataClientConfigResponse } from "../express/client-token-endpoint";
|
|
9
|
+
/**
|
|
10
|
+
* Options for autoInitializeDataClient
|
|
11
|
+
*/
|
|
12
|
+
export interface AutoInitOptions {
|
|
13
|
+
/** Client token endpoint URI (default: '/api/v1/auth/client-token') */
|
|
14
|
+
clientTokenUri?: string;
|
|
15
|
+
/** Override baseUrl detection (auto-detected from window.location if not provided) */
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
/** Error callback */
|
|
18
|
+
onError?: (error: Error) => void;
|
|
19
|
+
/** Whether to cache config in localStorage (default: true) */
|
|
20
|
+
cacheConfig?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get cached DataClient configuration from localStorage
|
|
24
|
+
*
|
|
25
|
+
* Returns the cached configuration that was stored by autoInitializeDataClient.
|
|
26
|
+
* Useful for reading configuration without re-initializing DataClient.
|
|
27
|
+
*
|
|
28
|
+
* @returns Cached config or null if not found or expired
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { getCachedDataClientConfig } from '@aifabrix/miso-client';
|
|
33
|
+
*
|
|
34
|
+
* const cachedConfig = getCachedDataClientConfig();
|
|
35
|
+
* if (cachedConfig) {
|
|
36
|
+
* console.log('Base URL:', cachedConfig.baseUrl);
|
|
37
|
+
* console.log('Controller URL:', cachedConfig.controllerUrl);
|
|
38
|
+
* console.log('Client ID:', cachedConfig.clientId);
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function getCachedDataClientConfig(): DataClientConfigResponse | null;
|
|
43
|
+
/**
|
|
44
|
+
* Auto-initialize DataClient with server-provided configuration
|
|
45
|
+
*
|
|
46
|
+
* Automatically:
|
|
47
|
+
* 1. Detects if running in browser
|
|
48
|
+
* 2. Checks localStorage cache first
|
|
49
|
+
* 3. Fetches config from server endpoint if needed
|
|
50
|
+
* 4. Initializes DataClient with server-provided config
|
|
51
|
+
* 5. Caches config for future use
|
|
52
|
+
*
|
|
53
|
+
* @param options - Optional configuration
|
|
54
|
+
* @returns Initialized DataClient instance
|
|
55
|
+
* @throws Error if initialization fails
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* import { autoInitializeDataClient } from '@aifabrix/miso-client';
|
|
60
|
+
*
|
|
61
|
+
* // One line - everything is automatic
|
|
62
|
+
* const dataClient = await autoInitializeDataClient();
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function autoInitializeDataClient(options?: AutoInitOptions): Promise<DataClient>;
|
|
66
|
+
//# sourceMappingURL=data-client-auto-init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-client-auto-init.d.ts","sourceRoot":"","sources":["../../src/utils/data-client-auto-init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,EAAE,wBAAwB,EAAkC,MAAM,kCAAkC,CAAC;AAE5G;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA0CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,IAAI,wBAAwB,GAAG,IAAI,CAG3E;AAoFD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,UAAU,CAAC,CAyErB"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DataClient Auto-Initialization Helper
|
|
4
|
+
* Automatically fetches configuration from server and initializes DataClient
|
|
5
|
+
*
|
|
6
|
+
* Provides zero-config client-side setup for DataClient initialization
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getCachedDataClientConfig = getCachedDataClientConfig;
|
|
10
|
+
exports.autoInitializeDataClient = autoInitializeDataClient;
|
|
11
|
+
const data_client_1 = require("./data-client");
|
|
12
|
+
const data_client_utils_1 = require("./data-client-utils");
|
|
13
|
+
const client_token_endpoint_1 = require("../express/client-token-endpoint");
|
|
14
|
+
/**
|
|
15
|
+
* Get cached config from localStorage
|
|
16
|
+
*
|
|
17
|
+
* @returns Cached config or null if not found or expired
|
|
18
|
+
*/
|
|
19
|
+
function getCachedConfig() {
|
|
20
|
+
if (!(0, data_client_utils_1.isBrowser)()) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const cachedStr = (0, data_client_utils_1.getLocalStorage)("miso:dataclient-config");
|
|
25
|
+
if (!cachedStr) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const cached = JSON.parse(cachedStr);
|
|
29
|
+
// Check if expired
|
|
30
|
+
if (cached.expiresAt && cached.expiresAt < Date.now()) {
|
|
31
|
+
(0, data_client_utils_1.removeLocalStorage)("miso:dataclient-config");
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return cached;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Invalid cache, remove it
|
|
38
|
+
(0, data_client_utils_1.removeLocalStorage)("miso:dataclient-config");
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get cached DataClient configuration from localStorage
|
|
44
|
+
*
|
|
45
|
+
* Returns the cached configuration that was stored by autoInitializeDataClient.
|
|
46
|
+
* Useful for reading configuration without re-initializing DataClient.
|
|
47
|
+
*
|
|
48
|
+
* @returns Cached config or null if not found or expired
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* import { getCachedDataClientConfig } from '@aifabrix/miso-client';
|
|
53
|
+
*
|
|
54
|
+
* const cachedConfig = getCachedDataClientConfig();
|
|
55
|
+
* if (cachedConfig) {
|
|
56
|
+
* console.log('Base URL:', cachedConfig.baseUrl);
|
|
57
|
+
* console.log('Controller URL:', cachedConfig.controllerUrl);
|
|
58
|
+
* console.log('Client ID:', cachedConfig.clientId);
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
function getCachedDataClientConfig() {
|
|
63
|
+
const cached = getCachedConfig();
|
|
64
|
+
return cached?.config || null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Cache config in localStorage
|
|
68
|
+
*
|
|
69
|
+
* @param config - Config to cache
|
|
70
|
+
* @param expiresIn - Expiration time in seconds
|
|
71
|
+
*/
|
|
72
|
+
function cacheConfig(config, expiresIn) {
|
|
73
|
+
if (!(0, data_client_utils_1.isBrowser)()) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const cached = {
|
|
78
|
+
config,
|
|
79
|
+
expiresAt: Date.now() + expiresIn * 1000,
|
|
80
|
+
};
|
|
81
|
+
(0, data_client_utils_1.setLocalStorage)("miso:dataclient-config", JSON.stringify(cached));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore localStorage errors (SSR, private browsing, etc.)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fetch config from server endpoint
|
|
89
|
+
*
|
|
90
|
+
* @param baseUrl - Base URL for the API
|
|
91
|
+
* @param clientTokenUri - Client token endpoint URI
|
|
92
|
+
* @returns Config response
|
|
93
|
+
*/
|
|
94
|
+
async function fetchConfig(baseUrl, clientTokenUri) {
|
|
95
|
+
// Build full URL
|
|
96
|
+
const fullUrl = /^https?:\/\//i.test(clientTokenUri)
|
|
97
|
+
? clientTokenUri
|
|
98
|
+
: `${baseUrl}${clientTokenUri}`;
|
|
99
|
+
try {
|
|
100
|
+
// Try POST first (standard), fallback to GET
|
|
101
|
+
let response = await fetch(fullUrl, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: {
|
|
104
|
+
"Content-Type": "application/json",
|
|
105
|
+
},
|
|
106
|
+
credentials: "include",
|
|
107
|
+
});
|
|
108
|
+
// If POST fails with 405, try GET
|
|
109
|
+
if (response.status === 405) {
|
|
110
|
+
response = await fetch(fullUrl, {
|
|
111
|
+
method: "GET",
|
|
112
|
+
credentials: "include",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const errorText = await response.text();
|
|
117
|
+
throw new Error(`Failed to fetch config: ${response.status} ${response.statusText}. ${errorText}`);
|
|
118
|
+
}
|
|
119
|
+
const data = (await response.json());
|
|
120
|
+
// Check if response has config
|
|
121
|
+
if (!(0, client_token_endpoint_1.hasConfig)(data)) {
|
|
122
|
+
throw new Error("Invalid response format: config not found in response. " +
|
|
123
|
+
"Make sure your server endpoint uses createClientTokenEndpoint() helper.");
|
|
124
|
+
}
|
|
125
|
+
return data.config;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof Error) {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Network error: ${String(error)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Auto-initialize DataClient with server-provided configuration
|
|
136
|
+
*
|
|
137
|
+
* Automatically:
|
|
138
|
+
* 1. Detects if running in browser
|
|
139
|
+
* 2. Checks localStorage cache first
|
|
140
|
+
* 3. Fetches config from server endpoint if needed
|
|
141
|
+
* 4. Initializes DataClient with server-provided config
|
|
142
|
+
* 5. Caches config for future use
|
|
143
|
+
*
|
|
144
|
+
* @param options - Optional configuration
|
|
145
|
+
* @returns Initialized DataClient instance
|
|
146
|
+
* @throws Error if initialization fails
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* import { autoInitializeDataClient } from '@aifabrix/miso-client';
|
|
151
|
+
*
|
|
152
|
+
* // One line - everything is automatic
|
|
153
|
+
* const dataClient = await autoInitializeDataClient();
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
async function autoInitializeDataClient(options) {
|
|
157
|
+
// Check if running in browser
|
|
158
|
+
if (!(0, data_client_utils_1.isBrowser)()) {
|
|
159
|
+
throw new Error("autoInitializeDataClient() is only available in browser environment");
|
|
160
|
+
}
|
|
161
|
+
const opts = {
|
|
162
|
+
clientTokenUri: options?.clientTokenUri || "/api/v1/auth/client-token",
|
|
163
|
+
cacheConfig: options?.cacheConfig ?? true,
|
|
164
|
+
baseUrl: options?.baseUrl,
|
|
165
|
+
onError: options?.onError,
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
// Auto-detect baseUrl from window.location if not provided
|
|
169
|
+
const baseUrl = opts.baseUrl ||
|
|
170
|
+
((0, data_client_utils_1.isBrowser)()
|
|
171
|
+
? globalThis.window.location.origin
|
|
172
|
+
: "");
|
|
173
|
+
if (!baseUrl) {
|
|
174
|
+
throw new Error("Unable to detect baseUrl. Please provide baseUrl option.");
|
|
175
|
+
}
|
|
176
|
+
let config = null;
|
|
177
|
+
// Check cache first if enabled
|
|
178
|
+
if (opts.cacheConfig) {
|
|
179
|
+
const cached = getCachedConfig();
|
|
180
|
+
if (cached) {
|
|
181
|
+
config = cached.config;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Fetch from server if not cached
|
|
185
|
+
if (!config) {
|
|
186
|
+
config = await fetchConfig(baseUrl, opts.clientTokenUri);
|
|
187
|
+
// Cache config if enabled (use expiresIn from token response if available)
|
|
188
|
+
// Default to 30 minutes (1800 seconds) if not available
|
|
189
|
+
if (opts.cacheConfig) {
|
|
190
|
+
cacheConfig(config, 1800);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Build DataClient config
|
|
194
|
+
const dataClientConfig = {
|
|
195
|
+
baseUrl: config.baseUrl,
|
|
196
|
+
misoConfig: {
|
|
197
|
+
clientId: config.clientId,
|
|
198
|
+
controllerUrl: config.controllerUrl,
|
|
199
|
+
controllerPublicUrl: config.controllerPublicUrl,
|
|
200
|
+
clientTokenUri: config.clientTokenUri,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
// Initialize and return DataClient
|
|
204
|
+
return new data_client_1.DataClient(dataClientConfig);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
208
|
+
// Call error callback if provided
|
|
209
|
+
if (opts.onError) {
|
|
210
|
+
opts.onError(err);
|
|
211
|
+
}
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=data-client-auto-init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-client-auto-init.js","sourceRoot":"","sources":["../../src/utils/data-client-auto-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataClient redirect to login utilities
|
|
3
|
+
* Handles redirect to login flow with proper error handling
|
|
4
|
+
*/
|
|
5
|
+
import { DataClientConfig } from "../types/data-client.types";
|
|
6
|
+
/**
|
|
7
|
+
* Synchronously get and validate redirect URL
|
|
8
|
+
* Validates the URL before returning it
|
|
9
|
+
* @param url - URL to validate and return
|
|
10
|
+
* @param fallbackUrl - Optional fallback URL if primary is invalid
|
|
11
|
+
* @returns Validated URL string or null if both are invalid
|
|
12
|
+
*/
|
|
13
|
+
export declare function getValidatedRedirectUrl(url: string | null, fallbackUrl?: string | null): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Redirect to login page via controller
|
|
16
|
+
* Calls the controller login endpoint with redirect parameter and x-client-token header
|
|
17
|
+
* @param config - DataClient configuration
|
|
18
|
+
* @param getClientTokenFn - Function to get client token
|
|
19
|
+
* @param redirectUrl - Optional redirect URL to return to after login (defaults to current page URL)
|
|
20
|
+
*/
|
|
21
|
+
export declare function redirectToLogin(config: DataClientConfig, getClientTokenFn: () => Promise<string | null>, redirectUrl?: string): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=data-client-redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-client-redirect.d.ts","sourceRoot":"","sources":["../../src/utils/data-client-redirect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AA0P9D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAiBtG;AAoGD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAC9C,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DataClient redirect to login utilities
|
|
4
|
+
* Handles redirect to login flow with proper error handling
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getValidatedRedirectUrl = getValidatedRedirectUrl;
|
|
8
|
+
exports.redirectToLogin = redirectToLogin;
|
|
9
|
+
const data_client_auth_1 = require("./data-client-auth");
|
|
10
|
+
const data_client_utils_1 = require("./data-client-utils");
|
|
11
|
+
/**
|
|
12
|
+
* Build login request headers with client token
|
|
13
|
+
*/
|
|
14
|
+
function buildLoginHeaders(clientToken) {
|
|
15
|
+
const headers = {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
};
|
|
18
|
+
if (clientToken) {
|
|
19
|
+
headers["x-client-token"] = clientToken;
|
|
20
|
+
}
|
|
21
|
+
return headers;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build login endpoint URL with redirect parameter
|
|
25
|
+
*/
|
|
26
|
+
function buildLoginEndpointUrl(controllerUrl, redirectUrl) {
|
|
27
|
+
const loginEndpoint = `${controllerUrl}/api/v1/auth/login`;
|
|
28
|
+
const url = new URL(loginEndpoint);
|
|
29
|
+
url.searchParams.set("redirect", redirectUrl);
|
|
30
|
+
return url.toString();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Handle non-OK response from controller login endpoint
|
|
34
|
+
*/
|
|
35
|
+
function handleLoginErrorResponse(response, controllerUrl, clientToken) {
|
|
36
|
+
const errorTextPromise = response.text().catch(() => 'Unable to read error response');
|
|
37
|
+
// Create user-friendly error message based on status code
|
|
38
|
+
let userFriendlyMessage = `Login request failed: ${response.status} ${response.statusText}`;
|
|
39
|
+
if (response.status === 401 || response.status === 403) {
|
|
40
|
+
userFriendlyMessage = "Authentication failed: Invalid client credentials. Please check your configuration.";
|
|
41
|
+
}
|
|
42
|
+
else if (response.status === 404) {
|
|
43
|
+
userFriendlyMessage = `Authentication endpoint not found at ${controllerUrl}/api/v1/auth/login. Please verify your controller URL configuration.`;
|
|
44
|
+
}
|
|
45
|
+
else if (response.status >= 500) {
|
|
46
|
+
userFriendlyMessage = "Authentication server error. Please try again later or contact support.";
|
|
47
|
+
}
|
|
48
|
+
return errorTextPromise.then((errorText) => {
|
|
49
|
+
const error = new Error(userFriendlyMessage);
|
|
50
|
+
error.details = {
|
|
51
|
+
status: response.status,
|
|
52
|
+
statusText: response.statusText,
|
|
53
|
+
errorText,
|
|
54
|
+
controllerUrl,
|
|
55
|
+
hasClientToken: !!clientToken,
|
|
56
|
+
};
|
|
57
|
+
throw error;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract login URL from controller response
|
|
62
|
+
*/
|
|
63
|
+
function extractLoginUrl(data) {
|
|
64
|
+
return data.data?.loginUrl || data.loginUrl || null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create user-friendly error message for network errors
|
|
68
|
+
*/
|
|
69
|
+
function createNetworkErrorMessage(errorMessage, controllerUrl, clientToken) {
|
|
70
|
+
if (errorMessage.includes("Failed to fetch") || errorMessage.includes("NetworkError") || errorMessage.includes("ERR_CONNECTION_REFUSED")) {
|
|
71
|
+
return `Cannot connect to authentication server at ${controllerUrl || 'unknown'}. Please check your network connection and server configuration.`;
|
|
72
|
+
}
|
|
73
|
+
if (!clientToken) {
|
|
74
|
+
return "Client token is missing. Please initialize DataClient with proper credentials.";
|
|
75
|
+
}
|
|
76
|
+
return `Login failed: ${errorMessage}`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validate controller URL configuration
|
|
80
|
+
*/
|
|
81
|
+
function validateControllerUrl(config) {
|
|
82
|
+
const controllerUrl = (0, data_client_auth_1.getControllerUrl)(config.misoConfig);
|
|
83
|
+
if (!controllerUrl) {
|
|
84
|
+
const error = new Error("Controller URL is not configured. Please configure controllerUrl or controllerPublicUrl in your DataClient configuration.");
|
|
85
|
+
error.details = {
|
|
86
|
+
hasMisoConfig: !!config.misoConfig,
|
|
87
|
+
controllerUrl: config.misoConfig?.controllerUrl,
|
|
88
|
+
controllerPublicUrl: config.misoConfig?.controllerPublicUrl,
|
|
89
|
+
loginUrl: config.loginUrl,
|
|
90
|
+
};
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
return controllerUrl;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Call controller login endpoint
|
|
97
|
+
*/
|
|
98
|
+
async function callControllerLoginEndpoint(endpointUrl, headers, controllerUrl, clientToken) {
|
|
99
|
+
let response;
|
|
100
|
+
// Add timeout to prevent hanging (30 seconds)
|
|
101
|
+
const timeout = 30000;
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timeoutId = setTimeout(() => {
|
|
104
|
+
controller.abort();
|
|
105
|
+
}, timeout);
|
|
106
|
+
try {
|
|
107
|
+
response = await fetch(endpointUrl, {
|
|
108
|
+
method: "GET",
|
|
109
|
+
headers,
|
|
110
|
+
credentials: "include",
|
|
111
|
+
signal: controller.signal,
|
|
112
|
+
redirect: "manual", // Don't follow redirects automatically - we'll handle them explicitly
|
|
113
|
+
});
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
}
|
|
116
|
+
catch (fetchError) {
|
|
117
|
+
clearTimeout(timeoutId);
|
|
118
|
+
const errorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
119
|
+
const errorName = fetchError instanceof Error ? fetchError.name : "Unknown";
|
|
120
|
+
const isAbortError = errorName === "AbortError" || errorMessage.includes("aborted");
|
|
121
|
+
const isNetworkError = errorName === "TypeError" || errorMessage.includes("Failed to fetch");
|
|
122
|
+
// Create user-friendly error message
|
|
123
|
+
let userFriendlyMessage = `Failed to fetch login endpoint: ${errorMessage}`;
|
|
124
|
+
if (isAbortError) {
|
|
125
|
+
userFriendlyMessage = `Request timeout: The login endpoint did not respond within ${timeout}ms. Please check your network connection and server status.`;
|
|
126
|
+
}
|
|
127
|
+
else if (isNetworkError) {
|
|
128
|
+
if (errorMessage.includes("CORS") || errorMessage.includes("cross-origin")) {
|
|
129
|
+
userFriendlyMessage = `CORS error: Cannot connect to ${controllerUrl}. The server may not allow cross-origin requests. Please check CORS configuration.`;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
userFriendlyMessage = `Network error: Cannot connect to ${controllerUrl}. Please check your network connection and ensure the server is running.`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Re-throw with more context
|
|
136
|
+
const networkError = new Error(userFriendlyMessage);
|
|
137
|
+
networkError.details = {
|
|
138
|
+
endpointUrl,
|
|
139
|
+
hasClientToken: !!clientToken,
|
|
140
|
+
originalError: errorMessage,
|
|
141
|
+
errorName,
|
|
142
|
+
isTimeout: isAbortError,
|
|
143
|
+
isCorsError: isNetworkError && (errorMessage.includes("CORS") || errorMessage.includes("cross-origin")),
|
|
144
|
+
};
|
|
145
|
+
throw networkError;
|
|
146
|
+
}
|
|
147
|
+
// Handle case where response is undefined (shouldn't happen, but be safe)
|
|
148
|
+
if (!response) {
|
|
149
|
+
throw new Error("Failed to fetch login endpoint: response is undefined");
|
|
150
|
+
}
|
|
151
|
+
// Check for redirect status codes - these should be handled, not followed automatically
|
|
152
|
+
if (response.status >= 300 && response.status < 400) {
|
|
153
|
+
// Don't follow redirect automatically - treat as error
|
|
154
|
+
throw new Error(`Server returned redirect status ${response.status}. This should not happen for login endpoint.`);
|
|
155
|
+
}
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
throw await handleLoginErrorResponse(response, controllerUrl, clientToken);
|
|
158
|
+
}
|
|
159
|
+
let data;
|
|
160
|
+
try {
|
|
161
|
+
data = (await response.json());
|
|
162
|
+
}
|
|
163
|
+
catch (jsonError) {
|
|
164
|
+
const errorMessage = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
165
|
+
throw new Error(`Failed to parse login response: ${errorMessage}. Status: ${response.status} ${response.statusText}`);
|
|
166
|
+
}
|
|
167
|
+
return data;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Validate redirect URL for safety and format
|
|
171
|
+
* Checks for valid URL format and safe protocols (http, https)
|
|
172
|
+
* @param url - URL to validate
|
|
173
|
+
* @returns Validated URL string or null if invalid
|
|
174
|
+
*/
|
|
175
|
+
function validateRedirectUrl(url) {
|
|
176
|
+
if (!url || typeof url !== 'string' || url.trim() === '') {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const trimmedUrl = url.trim();
|
|
180
|
+
// Check for dangerous protocols (javascript:, data:, etc.)
|
|
181
|
+
const dangerousProtocols = ['javascript:', 'data:', 'vbscript:', 'file:', 'about:'];
|
|
182
|
+
const lowerUrl = trimmedUrl.toLowerCase();
|
|
183
|
+
for (const protocol of dangerousProtocols) {
|
|
184
|
+
if (lowerUrl.startsWith(protocol)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Try to parse as URL
|
|
189
|
+
let parsedUrl;
|
|
190
|
+
try {
|
|
191
|
+
// If it's a relative URL, create a full URL using current origin
|
|
192
|
+
if (trimmedUrl.startsWith('/') || !trimmedUrl.includes('://')) {
|
|
193
|
+
const origin = globalThis.window.location.origin;
|
|
194
|
+
parsedUrl = new URL(trimmedUrl, origin);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
parsedUrl = new URL(trimmedUrl);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
// Only allow http and https protocols
|
|
204
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
// Return the validated URL (use href to get full URL)
|
|
208
|
+
const validatedUrl = parsedUrl.href;
|
|
209
|
+
return validatedUrl;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Synchronously get and validate redirect URL
|
|
213
|
+
* Validates the URL before returning it
|
|
214
|
+
* @param url - URL to validate and return
|
|
215
|
+
* @param fallbackUrl - Optional fallback URL if primary is invalid
|
|
216
|
+
* @returns Validated URL string or null if both are invalid
|
|
217
|
+
*/
|
|
218
|
+
function getValidatedRedirectUrl(url, fallbackUrl) {
|
|
219
|
+
// Try primary URL first
|
|
220
|
+
const validatedUrl = validateRedirectUrl(url);
|
|
221
|
+
if (validatedUrl) {
|
|
222
|
+
return validatedUrl;
|
|
223
|
+
}
|
|
224
|
+
// Try fallback URL if provided
|
|
225
|
+
if (fallbackUrl) {
|
|
226
|
+
const validatedFallback = validateRedirectUrl(fallbackUrl);
|
|
227
|
+
if (validatedFallback) {
|
|
228
|
+
return validatedFallback;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Both URLs invalid
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handle successful login response
|
|
236
|
+
* IMPORTANT: Only redirects if controller returns valid loginUrl - NO fallback redirects
|
|
237
|
+
*/
|
|
238
|
+
function handleSuccessfulLoginResponse(loginUrl, controllerUrl, data, clientToken, _config) {
|
|
239
|
+
// CRITICAL: Only use loginUrl from controller - do NOT use fallback URL
|
|
240
|
+
// If controller doesn't return a valid URL, throw error instead of redirecting
|
|
241
|
+
const validatedUrl = getValidatedRedirectUrl(loginUrl);
|
|
242
|
+
if (validatedUrl) {
|
|
243
|
+
try {
|
|
244
|
+
globalThis.window.location.href = validatedUrl;
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
catch (redirectError) {
|
|
248
|
+
const error = new Error("Failed to redirect to login URL. The URL may be blocked by browser security policies.");
|
|
249
|
+
error.details = {
|
|
250
|
+
validatedUrl,
|
|
251
|
+
originalLoginUrl: loginUrl,
|
|
252
|
+
redirectError: redirectError instanceof Error ? redirectError.message : String(redirectError),
|
|
253
|
+
};
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Controller did not return a valid login URL - throw error (do NOT use fallback)
|
|
258
|
+
const error = new Error("Controller did not return a valid login URL. Please check your controller configuration and ensure the login endpoint is working correctly.");
|
|
259
|
+
error.details = {
|
|
260
|
+
controllerUrl,
|
|
261
|
+
responseData: data,
|
|
262
|
+
hasClientToken: !!clientToken,
|
|
263
|
+
originalLoginUrl: loginUrl,
|
|
264
|
+
validationFailed: true,
|
|
265
|
+
};
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Handle login errors
|
|
270
|
+
* IMPORTANT: Does NOT redirect on error - throws error instead
|
|
271
|
+
* Redirects should only happen on successful responses
|
|
272
|
+
* @param error - The error that occurred
|
|
273
|
+
* @param controllerUrl - Controller URL
|
|
274
|
+
* @param clientToken - Client token if available
|
|
275
|
+
* @param config - DataClient configuration
|
|
276
|
+
* @param skipLogging - If true, skip logging (error already logged)
|
|
277
|
+
*/
|
|
278
|
+
function handleLoginError(error, controllerUrl, clientToken, config, skipLogging = false) {
|
|
279
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
280
|
+
const errorName = error instanceof Error ? error.name : "Unknown";
|
|
281
|
+
const _errorStack = error instanceof Error ? error.stack : undefined;
|
|
282
|
+
const errorDetails = error?.details;
|
|
283
|
+
// Log error - test expects Error object as second parameter
|
|
284
|
+
if (!skipLogging) {
|
|
285
|
+
const logError = error instanceof Error ? error : new Error(String(error));
|
|
286
|
+
console.error("[redirectToLogin] Failed to get login URL from controller:", logError);
|
|
287
|
+
}
|
|
288
|
+
// DO NOT redirect on error - throw error instead
|
|
289
|
+
// Redirects should only happen when fetch succeeds and returns valid URL
|
|
290
|
+
// For non-network errors, throw immediately
|
|
291
|
+
if (error instanceof Error && error.message && !error.message.includes("Failed to fetch")) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
const fullErrorDetails = {
|
|
295
|
+
message: errorMessage,
|
|
296
|
+
name: errorName,
|
|
297
|
+
originalDetails: errorDetails,
|
|
298
|
+
controllerUrl,
|
|
299
|
+
hasClientToken: !!clientToken,
|
|
300
|
+
config: {
|
|
301
|
+
loginUrl: config.loginUrl,
|
|
302
|
+
hasMisoConfig: !!config.misoConfig,
|
|
303
|
+
controllerUrl: config.misoConfig?.controllerUrl,
|
|
304
|
+
controllerPublicUrl: config.misoConfig?.controllerPublicUrl,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
const userFriendlyMessage = createNetworkErrorMessage(errorMessage, controllerUrl, clientToken);
|
|
308
|
+
const loginError = new Error(userFriendlyMessage);
|
|
309
|
+
loginError.details = fullErrorDetails;
|
|
310
|
+
throw loginError;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Redirect to login page via controller
|
|
314
|
+
* Calls the controller login endpoint with redirect parameter and x-client-token header
|
|
315
|
+
* @param config - DataClient configuration
|
|
316
|
+
* @param getClientTokenFn - Function to get client token
|
|
317
|
+
* @param redirectUrl - Optional redirect URL to return to after login (defaults to current page URL)
|
|
318
|
+
*/
|
|
319
|
+
async function redirectToLogin(config, getClientTokenFn, redirectUrl) {
|
|
320
|
+
if (!(0, data_client_utils_1.isBrowser)()) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const currentUrl = globalThis.window.location.href;
|
|
324
|
+
const finalRedirectUrl = redirectUrl || currentUrl;
|
|
325
|
+
const controllerUrl = validateControllerUrl(config);
|
|
326
|
+
let clientToken = null;
|
|
327
|
+
try {
|
|
328
|
+
clientToken = await getClientTokenFn();
|
|
329
|
+
const endpointUrl = buildLoginEndpointUrl(controllerUrl, finalRedirectUrl);
|
|
330
|
+
const headers = buildLoginHeaders(clientToken);
|
|
331
|
+
// Fetch login URL from controller - this MUST succeed for redirect to happen
|
|
332
|
+
const data = await callControllerLoginEndpoint(endpointUrl, headers, controllerUrl, clientToken);
|
|
333
|
+
const loginUrl = extractLoginUrl(data);
|
|
334
|
+
// Only redirect if we got a valid response - handleSuccessfulLoginResponse will validate URL
|
|
335
|
+
handleSuccessfulLoginResponse(loginUrl, controllerUrl, data, clientToken, config);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
// CRITICAL: Do NOT redirect on error - throw error instead
|
|
339
|
+
// Redirects should ONLY happen when fetch succeeds and returns valid URL
|
|
340
|
+
handleLoginError(error, controllerUrl, clientToken, config);
|
|
341
|
+
// This line should never be reached since handleLoginError always throws
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=data-client-redirect.js.map
|