@ghostly-solutions/auth 0.1.1 → 0.2.1
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 +68 -84
- package/dist/{auth-client-CAHMjodm.d.ts → auth-client-Cdkp07ii.d.ts} +14 -6
- package/dist/{auth-sdk-error-DKM7PyKC.d.ts → auth-sdk-error-D3gsfK9d.d.ts} +0 -3
- package/dist/extension.d.ts +16 -0
- package/dist/extension.js +502 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +12 -19
- package/dist/index.js +105 -119
- package/dist/index.js.map +1 -1
- package/dist/next.d.ts +4 -27
- package/dist/next.js +125 -383
- package/dist/next.js.map +1 -1
- package/dist/react.d.ts +4 -20
- package/dist/react.js +129 -176
- package/dist/react.js.map +1 -1
- package/docs/api-reference.md +65 -89
- package/docs/architecture.md +28 -46
- package/docs/development-and-ci.md +15 -19
- package/docs/index.md +1 -15
- package/docs/integration-guide.md +46 -81
- package/docs/overview.md +24 -30
- package/package.json +8 -2
package/dist/next.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
// src/constants/auth-endpoints.ts
|
|
2
|
-
var authApiPrefix = "/v1/auth";
|
|
3
|
-
var authEndpoints = {
|
|
4
|
-
loginStart: `${authApiPrefix}/keycloak/login`,
|
|
5
|
-
validateKeycloakToken: `${authApiPrefix}/keycloak/validate`,
|
|
6
|
-
session: `${authApiPrefix}/me`,
|
|
7
|
-
logout: `${authApiPrefix}/logout`
|
|
8
|
-
};
|
|
9
|
-
|
|
10
1
|
// src/constants/http-status.ts
|
|
11
2
|
var httpStatus = {
|
|
12
3
|
ok: 200,
|
|
13
|
-
found: 302,
|
|
14
4
|
noContent: 204,
|
|
15
|
-
badRequest: 400,
|
|
16
5
|
unauthorized: 401
|
|
17
6
|
};
|
|
18
7
|
|
|
@@ -32,9 +21,6 @@ var AuthSdkError = class extends Error {
|
|
|
32
21
|
|
|
33
22
|
// src/types/auth-error-code.ts
|
|
34
23
|
var authErrorCode = {
|
|
35
|
-
callbackMissingToken: "callback_missing_token",
|
|
36
|
-
callbackInvalidToken: "callback_invalid_token",
|
|
37
|
-
callbackValidationFailed: "callback_validation_failed",
|
|
38
24
|
unauthorized: "unauthorized",
|
|
39
25
|
networkError: "network_error",
|
|
40
26
|
apiError: "api_error",
|
|
@@ -42,6 +28,55 @@ var authErrorCode = {
|
|
|
42
28
|
serverOriginResolutionFailed: "server_origin_resolution_failed"
|
|
43
29
|
};
|
|
44
30
|
|
|
31
|
+
// src/constants/auth-endpoints.ts
|
|
32
|
+
var authApiPrefix = "/oauth";
|
|
33
|
+
var authEndpoints = {
|
|
34
|
+
authorize: `${authApiPrefix}/authorize`,
|
|
35
|
+
session: `${authApiPrefix}/session`,
|
|
36
|
+
refresh: `${authApiPrefix}/refresh`,
|
|
37
|
+
logout: `${authApiPrefix}/logout`
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/core/api-origin.ts
|
|
41
|
+
var slash = "/";
|
|
42
|
+
function normalizeApiOrigin(apiOrigin) {
|
|
43
|
+
const trimmed = apiOrigin.trim();
|
|
44
|
+
if (!trimmed) {
|
|
45
|
+
throw new AuthSdkError({
|
|
46
|
+
code: authErrorCode.apiError,
|
|
47
|
+
details: null,
|
|
48
|
+
message: "Auth API origin must be a non-empty absolute URL.",
|
|
49
|
+
status: null
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = new URL(trimmed);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new AuthSdkError({
|
|
57
|
+
code: authErrorCode.apiError,
|
|
58
|
+
details: error,
|
|
59
|
+
message: "Auth API origin must be a valid absolute URL.",
|
|
60
|
+
status: null
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (parsed.pathname !== slash || parsed.search || parsed.hash) {
|
|
64
|
+
throw new AuthSdkError({
|
|
65
|
+
code: authErrorCode.apiError,
|
|
66
|
+
details: null,
|
|
67
|
+
message: "Auth API origin must not include path, query, or hash.",
|
|
68
|
+
status: null
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return parsed.origin;
|
|
72
|
+
}
|
|
73
|
+
function resolveApiEndpoint(path, apiOrigin) {
|
|
74
|
+
if (!apiOrigin) {
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
77
|
+
return `${normalizeApiOrigin(apiOrigin)}${path}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
45
80
|
// src/core/object-guards.ts
|
|
46
81
|
function isObjectRecord(value) {
|
|
47
82
|
return typeof value === "object" && value !== null;
|
|
@@ -67,7 +102,15 @@ var fallbackHostHeaderName = "host";
|
|
|
67
102
|
var forwardedProtoHeaderName = "x-forwarded-proto";
|
|
68
103
|
var defaultProtocol = "https";
|
|
69
104
|
var protocolSeparator = "://";
|
|
70
|
-
var forwardedSessionHeaderNames = [
|
|
105
|
+
var forwardedSessionHeaderNames = [
|
|
106
|
+
"accept-language",
|
|
107
|
+
"authorization",
|
|
108
|
+
"cookie",
|
|
109
|
+
"user-agent",
|
|
110
|
+
"x-forwarded-for",
|
|
111
|
+
"x-real-ip",
|
|
112
|
+
"x-request-id"
|
|
113
|
+
];
|
|
71
114
|
function resolveServerOrigin(options) {
|
|
72
115
|
const host = options.headers.get(hostHeaderName) ?? options.headers.get(fallbackHostHeaderName);
|
|
73
116
|
if (!host) {
|
|
@@ -107,8 +150,9 @@ function parseSessionPayload(payload) {
|
|
|
107
150
|
}
|
|
108
151
|
async function getServerSession(options) {
|
|
109
152
|
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
110
|
-
const
|
|
111
|
-
const
|
|
153
|
+
const fallbackOrigin = options.apiOrigin ? void 0 : resolveServerOrigin(options);
|
|
154
|
+
const endpoint = resolveApiEndpoint(authEndpoints.session, options.apiOrigin ?? fallbackOrigin);
|
|
155
|
+
const response = await fetchImplementation(endpoint, {
|
|
112
156
|
cache: "no-store",
|
|
113
157
|
credentials: "include",
|
|
114
158
|
headers: forwardSessionHeaders(options.headers),
|
|
@@ -147,110 +191,6 @@ async function requireServerSession(options) {
|
|
|
147
191
|
}
|
|
148
192
|
|
|
149
193
|
// src/adapters/next/auth-kit.ts
|
|
150
|
-
var defaultCallbackToken = "mock-keycloak-token";
|
|
151
|
-
var defaultCookieName = "gs_auth_session";
|
|
152
|
-
var defaultCookieValue = "mock-session-super-admin";
|
|
153
|
-
var defaultFrontendCallbackPath = "/auth/callback";
|
|
154
|
-
var callbackCodePrefix = "mock_code_";
|
|
155
|
-
var callbackStatePrefix = "mock_state_";
|
|
156
|
-
var clearCookieDate = "Thu, 01 Jan 1970 00:00:00 GMT";
|
|
157
|
-
var proxyForwardedHeaderNames = [
|
|
158
|
-
"accept-language",
|
|
159
|
-
"authorization",
|
|
160
|
-
"cookie",
|
|
161
|
-
"content-type",
|
|
162
|
-
"x-request-id"
|
|
163
|
-
];
|
|
164
|
-
function defaultMockSessionFactory() {
|
|
165
|
-
return {
|
|
166
|
-
id: "123",
|
|
167
|
-
username: "john_doe",
|
|
168
|
-
firstName: "John",
|
|
169
|
-
lastName: "Doe",
|
|
170
|
-
email: "john.doe@ghostlysolutions.ae",
|
|
171
|
-
role: "superAdmin",
|
|
172
|
-
permissions: [
|
|
173
|
-
"users:view",
|
|
174
|
-
"tasks:view",
|
|
175
|
-
"analytics:view",
|
|
176
|
-
"settings:view",
|
|
177
|
-
"audit-logs:view",
|
|
178
|
-
"admins:view"
|
|
179
|
-
]
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function createErrorPayload(code, message, details = null) {
|
|
183
|
-
return { code, message, details };
|
|
184
|
-
}
|
|
185
|
-
function toJsonResponse(payload, status) {
|
|
186
|
-
return new Response(JSON.stringify(payload), {
|
|
187
|
-
status,
|
|
188
|
-
headers: {
|
|
189
|
-
"content-type": "application/json"
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
function parseCookieValue(cookieHeader, cookieName) {
|
|
194
|
-
if (!cookieHeader) {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
const prefix = `${cookieName}=`;
|
|
198
|
-
const pairs = cookieHeader.split(";").map((pair) => pair.trim());
|
|
199
|
-
const match = pairs.find((pair) => pair.startsWith(prefix));
|
|
200
|
-
return match ? match.slice(prefix.length) : null;
|
|
201
|
-
}
|
|
202
|
-
function shouldForwardBody(method) {
|
|
203
|
-
const normalized = method.toUpperCase();
|
|
204
|
-
return normalized !== "GET" && normalized !== "HEAD";
|
|
205
|
-
}
|
|
206
|
-
function resolveSecureCookie(requestUrl, secureMode) {
|
|
207
|
-
if (typeof secureMode === "boolean") {
|
|
208
|
-
return secureMode;
|
|
209
|
-
}
|
|
210
|
-
if (requestUrl.protocol !== "https:") {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
const hostname = requestUrl.hostname.toLowerCase();
|
|
214
|
-
return hostname !== "localhost" && hostname !== "127.0.0.1";
|
|
215
|
-
}
|
|
216
|
-
function toSetCookieHeader(options) {
|
|
217
|
-
const attributes = ["Path=/", "HttpOnly", "SameSite=Lax"];
|
|
218
|
-
if (options.secure) {
|
|
219
|
-
attributes.push("Secure");
|
|
220
|
-
}
|
|
221
|
-
if (options.clear) {
|
|
222
|
-
attributes.push("Max-Age=0");
|
|
223
|
-
attributes.push(`Expires=${clearCookieDate}`);
|
|
224
|
-
}
|
|
225
|
-
return `${options.cookieName}=${options.cookieValue}; ${attributes.join("; ")}`;
|
|
226
|
-
}
|
|
227
|
-
async function proxyRequest(baseUrl, request2) {
|
|
228
|
-
const incomingUrl = new URL(request2.url);
|
|
229
|
-
const targetUrl = new URL(incomingUrl.pathname + incomingUrl.search, baseUrl);
|
|
230
|
-
const proxyHeaders = new Headers();
|
|
231
|
-
for (const headerName of proxyForwardedHeaderNames) {
|
|
232
|
-
const value = request2.headers.get(headerName);
|
|
233
|
-
if (value) {
|
|
234
|
-
proxyHeaders.set(headerName, value);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const body = shouldForwardBody(request2.method) ? await request2.text() : void 0;
|
|
238
|
-
const upstreamResponse = await fetch(targetUrl, {
|
|
239
|
-
method: request2.method,
|
|
240
|
-
headers: proxyHeaders,
|
|
241
|
-
cache: "no-store",
|
|
242
|
-
redirect: "manual",
|
|
243
|
-
...body !== void 0 ? { body } : {}
|
|
244
|
-
});
|
|
245
|
-
const responseHeaders = new Headers();
|
|
246
|
-
for (const [headerName, headerValue] of upstreamResponse.headers.entries()) {
|
|
247
|
-
responseHeaders.set(headerName, headerValue);
|
|
248
|
-
}
|
|
249
|
-
return new Response(upstreamResponse.body, {
|
|
250
|
-
status: upstreamResponse.status,
|
|
251
|
-
headers: responseHeaders
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
194
|
async function resolveNextHeaders() {
|
|
255
195
|
try {
|
|
256
196
|
const importNextHeaders = new Function("return import('next/headers')");
|
|
@@ -281,26 +221,11 @@ function resolveNextProtocol(headers, explicitProtocol) {
|
|
|
281
221
|
const normalizedHost = host.toLowerCase();
|
|
282
222
|
return normalizedHost.includes("localhost") || normalizedHost.includes("127.0.0.1") ? "http" : "https";
|
|
283
223
|
}
|
|
284
|
-
function normalizeMockToken(payload) {
|
|
285
|
-
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
const recordPayload = payload;
|
|
289
|
-
const keys = Object.keys(recordPayload);
|
|
290
|
-
if (keys.length !== 1 || !keys.includes("token")) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
const tokenValue = recordPayload.token;
|
|
294
|
-
if (typeof tokenValue !== "string") {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
const normalized = tokenValue.trim();
|
|
298
|
-
return normalized.length > 0 ? normalized : null;
|
|
299
|
-
}
|
|
300
224
|
async function getNextServerSession(options = {}) {
|
|
301
225
|
const headers = options.headers ?? await resolveNextHeaders();
|
|
302
226
|
const protocol = resolveNextProtocol(headers, options.protocol);
|
|
303
227
|
return getServerSession({
|
|
228
|
+
apiOrigin: options.apiOrigin,
|
|
304
229
|
headers,
|
|
305
230
|
protocol,
|
|
306
231
|
...options.fetchImplementation ? { fetchImplementation: options.fetchImplementation } : {}
|
|
@@ -325,154 +250,15 @@ async function requireNextServerSession(options = {}) {
|
|
|
325
250
|
status: httpStatus.unauthorized
|
|
326
251
|
});
|
|
327
252
|
}
|
|
328
|
-
function createNextAuthRouteHandlers(options) {
|
|
329
|
-
const callbackToken = options.mock?.callbackToken ?? defaultCallbackToken;
|
|
330
|
-
const cookieName = options.mock?.cookieName ?? defaultCookieName;
|
|
331
|
-
const cookieValue = options.mock?.cookieValue ?? defaultCookieValue;
|
|
332
|
-
const cookieSecure = options.mock?.cookieSecure ?? "auto";
|
|
333
|
-
const frontendCallbackPath = options.mock?.frontendCallbackPath ?? defaultFrontendCallbackPath;
|
|
334
|
-
const createSession = options.mock?.createSession ?? defaultMockSessionFactory;
|
|
335
|
-
const proxyOptions = options.proxy;
|
|
336
|
-
if (options.mode === "proxy" && !proxyOptions?.baseUrl) {
|
|
337
|
-
throw new AuthSdkError({
|
|
338
|
-
code: authErrorCode.apiError,
|
|
339
|
-
details: null,
|
|
340
|
-
message: "proxy.baseUrl is required in proxy mode.",
|
|
341
|
-
status: null
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
const proxyIfNeeded = async (request2) => {
|
|
345
|
-
if (options.mode !== "proxy") {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
return proxyRequest(proxyOptions?.baseUrl ?? "", request2);
|
|
349
|
-
};
|
|
350
|
-
return {
|
|
351
|
-
keycloakLoginGet: async (request2) => {
|
|
352
|
-
const proxied = await proxyIfNeeded(request2);
|
|
353
|
-
if (proxied) {
|
|
354
|
-
return proxied;
|
|
355
|
-
}
|
|
356
|
-
const code = `${callbackCodePrefix}${crypto.randomUUID()}`;
|
|
357
|
-
const state = `${callbackStatePrefix}${crypto.randomUUID()}`;
|
|
358
|
-
const redirectTarget = `${authEndpoints.loginStart.replace("/login", "/callback")}?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`;
|
|
359
|
-
return new Response(null, {
|
|
360
|
-
status: httpStatus.found,
|
|
361
|
-
headers: {
|
|
362
|
-
location: redirectTarget
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
},
|
|
366
|
-
keycloakCallbackGet: async (request2) => {
|
|
367
|
-
const proxied = await proxyIfNeeded(request2);
|
|
368
|
-
if (proxied) {
|
|
369
|
-
return proxied;
|
|
370
|
-
}
|
|
371
|
-
const requestUrl = new URL(request2.url);
|
|
372
|
-
const code = requestUrl.searchParams.get("code")?.trim();
|
|
373
|
-
const state = requestUrl.searchParams.get("state")?.trim();
|
|
374
|
-
if (!(code && state)) {
|
|
375
|
-
return toJsonResponse(
|
|
376
|
-
createErrorPayload("bad_request", "code and state are required."),
|
|
377
|
-
httpStatus.badRequest
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
const callbackUrl = `${frontendCallbackPath}?token=${encodeURIComponent(callbackToken)}`;
|
|
381
|
-
return new Response(null, {
|
|
382
|
-
status: httpStatus.found,
|
|
383
|
-
headers: {
|
|
384
|
-
location: callbackUrl
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
},
|
|
388
|
-
keycloakValidatePost: async (request2) => {
|
|
389
|
-
const proxied = await proxyIfNeeded(request2);
|
|
390
|
-
if (proxied) {
|
|
391
|
-
return proxied;
|
|
392
|
-
}
|
|
393
|
-
let payload;
|
|
394
|
-
try {
|
|
395
|
-
payload = await request2.json();
|
|
396
|
-
} catch {
|
|
397
|
-
return toJsonResponse(
|
|
398
|
-
createErrorPayload("bad_request", "Request payload must be valid JSON."),
|
|
399
|
-
httpStatus.badRequest
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
const token = normalizeMockToken(payload);
|
|
403
|
-
if (!token) {
|
|
404
|
-
return toJsonResponse(
|
|
405
|
-
createErrorPayload(
|
|
406
|
-
"bad_request",
|
|
407
|
-
"Request payload must contain only non-empty token field."
|
|
408
|
-
),
|
|
409
|
-
httpStatus.badRequest
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
if (token !== callbackToken) {
|
|
413
|
-
return toJsonResponse(
|
|
414
|
-
createErrorPayload("unauthorized", "Callback token is invalid or expired."),
|
|
415
|
-
httpStatus.unauthorized
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
const session = createSession();
|
|
419
|
-
const responsePayload = { session };
|
|
420
|
-
const secure = resolveSecureCookie(new URL(request2.url), cookieSecure);
|
|
421
|
-
return new Response(JSON.stringify(responsePayload), {
|
|
422
|
-
status: httpStatus.ok,
|
|
423
|
-
headers: {
|
|
424
|
-
"content-type": "application/json",
|
|
425
|
-
"set-cookie": toSetCookieHeader({
|
|
426
|
-
cookieName,
|
|
427
|
-
cookieValue,
|
|
428
|
-
secure
|
|
429
|
-
})
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
},
|
|
433
|
-
meGet: async (request2) => {
|
|
434
|
-
const proxied = await proxyIfNeeded(request2);
|
|
435
|
-
if (proxied) {
|
|
436
|
-
return proxied;
|
|
437
|
-
}
|
|
438
|
-
const cookie = request2.headers.get("cookie");
|
|
439
|
-
const session = parseCookieValue(cookie, cookieName) === cookieValue ? createSession() : null;
|
|
440
|
-
return toJsonResponse(session, httpStatus.ok);
|
|
441
|
-
},
|
|
442
|
-
logoutPost: async (request2) => {
|
|
443
|
-
const proxied = await proxyIfNeeded(request2);
|
|
444
|
-
if (proxied) {
|
|
445
|
-
return proxied;
|
|
446
|
-
}
|
|
447
|
-
const secure = resolveSecureCookie(new URL(request2.url), cookieSecure);
|
|
448
|
-
return new Response(null, {
|
|
449
|
-
status: httpStatus.noContent,
|
|
450
|
-
headers: {
|
|
451
|
-
"set-cookie": toSetCookieHeader({
|
|
452
|
-
cookieName,
|
|
453
|
-
cookieValue: "",
|
|
454
|
-
secure,
|
|
455
|
-
clear: true
|
|
456
|
-
})
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
253
|
|
|
463
254
|
// src/constants/auth-keys.ts
|
|
464
|
-
var authQueryKeys = {
|
|
465
|
-
token: "token"
|
|
466
|
-
};
|
|
467
|
-
var authStorageKeys = {
|
|
468
|
-
returnTo: "ghostly-auth:return-to"
|
|
469
|
-
};
|
|
470
255
|
var authBroadcast = {
|
|
471
256
|
channelName: "ghostly-auth-channel",
|
|
472
257
|
sessionUpdatedEvent: "session-updated"
|
|
473
258
|
};
|
|
474
259
|
var authRoutes = {
|
|
475
|
-
root: "/"
|
|
260
|
+
root: "/"
|
|
261
|
+
};
|
|
476
262
|
|
|
477
263
|
// src/core/broadcast-sync.ts
|
|
478
264
|
function isSessionUpdatedMessage(value) {
|
|
@@ -523,19 +309,6 @@ function createBroadcastSync(options) {
|
|
|
523
309
|
};
|
|
524
310
|
}
|
|
525
311
|
|
|
526
|
-
// src/core/callback-url.ts
|
|
527
|
-
function readCallbackToken(url) {
|
|
528
|
-
return url.searchParams.get(authQueryKeys.token);
|
|
529
|
-
}
|
|
530
|
-
function removeCallbackToken(url) {
|
|
531
|
-
const nextUrl = new URL(url.toString());
|
|
532
|
-
nextUrl.searchParams.delete(authQueryKeys.token);
|
|
533
|
-
return nextUrl;
|
|
534
|
-
}
|
|
535
|
-
function replaceBrowserHistory(url) {
|
|
536
|
-
window.history.replaceState(null, "", url.toString());
|
|
537
|
-
}
|
|
538
|
-
|
|
539
312
|
// src/core/http-client.ts
|
|
540
313
|
var jsonContentType = "application/json";
|
|
541
314
|
var jsonHeaderName = "content-type";
|
|
@@ -631,9 +404,8 @@ function getJson(path) {
|
|
|
631
404
|
path
|
|
632
405
|
});
|
|
633
406
|
}
|
|
634
|
-
function
|
|
407
|
+
function postJsonWithoutBody(path) {
|
|
635
408
|
return request({
|
|
636
|
-
body,
|
|
637
409
|
method: "POST",
|
|
638
410
|
path
|
|
639
411
|
});
|
|
@@ -680,18 +452,10 @@ function sanitizeReturnTo(value) {
|
|
|
680
452
|
function getCurrentBrowserPath() {
|
|
681
453
|
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
682
454
|
}
|
|
683
|
-
function
|
|
455
|
+
function resolveReturnToPath(returnTo) {
|
|
684
456
|
assertBrowserRuntime();
|
|
685
457
|
const fallbackPath = getCurrentBrowserPath();
|
|
686
|
-
|
|
687
|
-
window.sessionStorage.setItem(authStorageKeys.returnTo, sanitized);
|
|
688
|
-
return sanitized;
|
|
689
|
-
}
|
|
690
|
-
function consumeReturnToPath() {
|
|
691
|
-
assertBrowserRuntime();
|
|
692
|
-
const value = window.sessionStorage.getItem(authStorageKeys.returnTo);
|
|
693
|
-
window.sessionStorage.removeItem(authStorageKeys.returnTo);
|
|
694
|
-
return sanitizeReturnTo(value);
|
|
458
|
+
return sanitizeReturnTo(returnTo ?? fallbackPath);
|
|
695
459
|
}
|
|
696
460
|
|
|
697
461
|
// src/core/session-store.ts
|
|
@@ -724,10 +488,6 @@ var SessionStore = class {
|
|
|
724
488
|
};
|
|
725
489
|
|
|
726
490
|
// src/core/auth-client.ts
|
|
727
|
-
function createPendingRedirectPromise() {
|
|
728
|
-
return new Promise(() => {
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
491
|
function createInvalidSessionPayloadError(path) {
|
|
732
492
|
return new AuthSdkError({
|
|
733
493
|
code: authErrorCode.apiError,
|
|
@@ -742,29 +502,11 @@ function toValidatedSession(payload, path) {
|
|
|
742
502
|
}
|
|
743
503
|
return payload;
|
|
744
504
|
}
|
|
745
|
-
function
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
return new AuthSdkError({
|
|
749
|
-
code: authErrorCode.callbackInvalidToken,
|
|
750
|
-
details: error.details,
|
|
751
|
-
message: "Callback JWT is invalid or expired.",
|
|
752
|
-
status: error.status
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
return new AuthSdkError({
|
|
756
|
-
code: authErrorCode.callbackValidationFailed,
|
|
757
|
-
details: error.details,
|
|
758
|
-
message: "Keycloak callback validation failed.",
|
|
759
|
-
status: error.status
|
|
760
|
-
});
|
|
505
|
+
function toSessionPayload(payload, path) {
|
|
506
|
+
if (payload === null) {
|
|
507
|
+
return null;
|
|
761
508
|
}
|
|
762
|
-
return
|
|
763
|
-
code: authErrorCode.callbackValidationFailed,
|
|
764
|
-
details: error,
|
|
765
|
-
message: "Keycloak callback validation failed.",
|
|
766
|
-
status: null
|
|
767
|
-
});
|
|
509
|
+
return toValidatedSession(payload, path);
|
|
768
510
|
}
|
|
769
511
|
function createNoopBroadcastSync() {
|
|
770
512
|
return {
|
|
@@ -786,25 +528,53 @@ function createSafeBroadcastSync(onSessionUpdated) {
|
|
|
786
528
|
throw error;
|
|
787
529
|
}
|
|
788
530
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}
|
|
794
|
-
return toValidatedSession(payload, authEndpoints.session);
|
|
531
|
+
function toInitResult(session) {
|
|
532
|
+
return {
|
|
533
|
+
session,
|
|
534
|
+
status: session ? "authenticated" : "unauthenticated"
|
|
535
|
+
};
|
|
795
536
|
}
|
|
796
|
-
function createAuthClient() {
|
|
797
|
-
|
|
537
|
+
function createAuthClient(options = {}) {
|
|
538
|
+
let initPromise = null;
|
|
539
|
+
const defaultApplication = options.application?.trim() || "";
|
|
798
540
|
const sessionStore = new SessionStore();
|
|
799
541
|
const broadcastSync = createSafeBroadcastSync((session) => {
|
|
800
542
|
sessionStore.setSession(session);
|
|
801
543
|
});
|
|
802
|
-
const
|
|
803
|
-
|
|
544
|
+
const resolveEndpoint = (path) => resolveApiEndpoint(path, options.apiOrigin);
|
|
545
|
+
const loadSession = async () => {
|
|
546
|
+
const payload = await getJson(resolveEndpoint(authEndpoints.session));
|
|
547
|
+
return toSessionPayload(payload, authEndpoints.session);
|
|
548
|
+
};
|
|
549
|
+
const init = async (initOptions) => {
|
|
550
|
+
const forceRefresh = initOptions?.forceRefresh ?? false;
|
|
804
551
|
if (sessionStore.hasResolvedSession() && !forceRefresh) {
|
|
805
|
-
return sessionStore.getSessionIfResolved();
|
|
552
|
+
return toInitResult(sessionStore.getSessionIfResolved());
|
|
553
|
+
}
|
|
554
|
+
if (initPromise) {
|
|
555
|
+
return initPromise;
|
|
806
556
|
}
|
|
807
|
-
|
|
557
|
+
initPromise = (async () => {
|
|
558
|
+
const session = await loadSession();
|
|
559
|
+
sessionStore.setSession(session);
|
|
560
|
+
broadcastSync.publishSession(session);
|
|
561
|
+
return toInitResult(session);
|
|
562
|
+
})();
|
|
563
|
+
try {
|
|
564
|
+
return await initPromise;
|
|
565
|
+
} finally {
|
|
566
|
+
initPromise = null;
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
const getSession = async (requestOptions) => {
|
|
570
|
+
const result = await init({
|
|
571
|
+
forceRefresh: requestOptions?.forceRefresh ?? false
|
|
572
|
+
});
|
|
573
|
+
return result.session;
|
|
574
|
+
};
|
|
575
|
+
const refresh = async () => {
|
|
576
|
+
const payload = await postJsonWithoutBody(resolveEndpoint(authEndpoints.refresh));
|
|
577
|
+
const session = toSessionPayload(payload, authEndpoints.refresh);
|
|
808
578
|
sessionStore.setSession(session);
|
|
809
579
|
broadcastSync.publishSession(session);
|
|
810
580
|
return session;
|
|
@@ -821,63 +591,35 @@ function createAuthClient() {
|
|
|
821
591
|
status: httpStatus.unauthorized
|
|
822
592
|
});
|
|
823
593
|
};
|
|
824
|
-
const login = (
|
|
825
|
-
|
|
826
|
-
window.location.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (!token) {
|
|
832
|
-
throw new AuthSdkError({
|
|
833
|
-
code: authErrorCode.callbackMissingToken,
|
|
834
|
-
details: null,
|
|
835
|
-
message: "Missing callback token query parameter.",
|
|
836
|
-
status: httpStatus.badRequest
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
const cleanedUrl = removeCallbackToken(currentUrl);
|
|
840
|
-
replaceBrowserHistory(cleanedUrl);
|
|
841
|
-
try {
|
|
842
|
-
const payload = await postJson(
|
|
843
|
-
authEndpoints.validateKeycloakToken,
|
|
844
|
-
{ token }
|
|
845
|
-
);
|
|
846
|
-
const session = toValidatedSession(payload.session, authEndpoints.validateKeycloakToken);
|
|
847
|
-
sessionStore.setSession(session);
|
|
848
|
-
broadcastSync.publishSession(session);
|
|
849
|
-
return {
|
|
850
|
-
redirectTo: consumeReturnToPath(),
|
|
851
|
-
session
|
|
852
|
-
};
|
|
853
|
-
} catch (error) {
|
|
854
|
-
throw toCallbackFailure(error);
|
|
594
|
+
const login = (loginOptions) => {
|
|
595
|
+
const returnTo = resolveReturnToPath(loginOptions?.returnTo);
|
|
596
|
+
const authorizeUrl = new URL(resolveEndpoint(authEndpoints.authorize), window.location.origin);
|
|
597
|
+
authorizeUrl.searchParams.set("return_to", returnTo);
|
|
598
|
+
const application = loginOptions?.application?.trim() || defaultApplication;
|
|
599
|
+
if (application) {
|
|
600
|
+
authorizeUrl.searchParams.set("app", application);
|
|
855
601
|
}
|
|
856
|
-
|
|
857
|
-
const completeCallbackRedirect = async () => {
|
|
858
|
-
const result = await processCallback();
|
|
859
|
-
window.location.replace(result.redirectTo);
|
|
860
|
-
return createPendingRedirectPromise();
|
|
602
|
+
window.location.assign(authorizeUrl.toString());
|
|
861
603
|
};
|
|
862
604
|
const logout = async () => {
|
|
863
|
-
await postEmpty(authEndpoints.logout);
|
|
605
|
+
await postEmpty(resolveEndpoint(authEndpoints.logout));
|
|
864
606
|
sessionStore.setSession(null);
|
|
865
607
|
broadcastSync.publishSession(null);
|
|
866
608
|
};
|
|
867
609
|
const subscribe = sessionStore.subscribe.bind(sessionStore);
|
|
868
610
|
return {
|
|
869
|
-
|
|
611
|
+
init,
|
|
870
612
|
getSession,
|
|
871
613
|
login,
|
|
872
614
|
logout,
|
|
873
|
-
|
|
615
|
+
refresh,
|
|
874
616
|
requireSession,
|
|
875
617
|
subscribe
|
|
876
618
|
};
|
|
877
619
|
}
|
|
878
620
|
|
|
879
621
|
// src/adapters/next/client-guard.ts
|
|
880
|
-
function
|
|
622
|
+
function createPendingRedirectPromise() {
|
|
881
623
|
return new Promise(() => {
|
|
882
624
|
});
|
|
883
625
|
}
|
|
@@ -888,12 +630,12 @@ async function ensureClientAuthenticated(client) {
|
|
|
888
630
|
} catch (error) {
|
|
889
631
|
if (error instanceof AuthSdkError && error.code === authErrorCode.unauthorized) {
|
|
890
632
|
authClient.login();
|
|
891
|
-
return
|
|
633
|
+
return createPendingRedirectPromise();
|
|
892
634
|
}
|
|
893
635
|
throw error;
|
|
894
636
|
}
|
|
895
637
|
}
|
|
896
638
|
|
|
897
|
-
export {
|
|
639
|
+
export { ensureClientAuthenticated, getNextServerSession, getServerSession, requireNextServerSession, requireServerSession, tryGetNextServerSession };
|
|
898
640
|
//# sourceMappingURL=next.js.map
|
|
899
641
|
//# sourceMappingURL=next.js.map
|