@ghostly-solutions/auth 0.1.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/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # @ghostly-solutions/auth
2
+
3
+ Authentication SDK for Ghostly Solutions products.
4
+
5
+ The SDK implements a fixed Keycloak redirect flow with Ghostly Auth API validation and typed session state.
6
+
7
+ ## Highlights
8
+
9
+ - Fixed API contract (no runtime endpoint configuration).
10
+ - Typed core client with deterministic error codes.
11
+ - Dedicated callback flow with immediate token URL cleanup.
12
+ - Session state with lazy revalidation.
13
+ - Cross-tab synchronization via `BroadcastChannel`.
14
+ - React adapter (`AuthProvider`, `useAuth`).
15
+ - React callback helpers (`AuthCallbackHandler`, `useAuthCallbackRedirect`).
16
+ - React session gate (`AuthSessionGate`).
17
+ - Next adapter (server and client guards).
18
+ - Next Auth Kit with ready route handlers (`mock` or `proxy`) and server-session helpers.
19
+
20
+ ## Fixed API Endpoints
21
+
22
+ - `GET /v1/auth/keycloak/login`
23
+ - `POST /v1/auth/keycloak/validate`
24
+ - `GET /v1/auth/me`
25
+ - `POST /v1/auth/logout`
26
+
27
+ ## Entry Points
28
+
29
+ - `@ghostly-solutions/auth`
30
+ - `@ghostly-solutions/auth/react`
31
+ - `@ghostly-solutions/auth/next`
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install @ghostly-solutions/auth
37
+ ```
38
+
39
+ ## Development
40
+
41
+ ```bash
42
+ npm run lint
43
+ npm run typecheck
44
+ npm run test
45
+ npm run build
46
+ ```
47
+
48
+ All CI and official workflows are npm-first.
49
+
50
+ Bun is optional for local use through aliases in `bun.json`.
51
+
52
+ ## Quick Start
53
+
54
+ ```ts
55
+ import { createAuthClient } from "@ghostly-solutions/auth";
56
+
57
+ const authClient = createAuthClient();
58
+
59
+ // Start login flow
60
+ authClient.login();
61
+
62
+ // In /auth/callback route
63
+ await authClient.completeCallbackRedirect();
64
+ ```
65
+
66
+ ## Next.js "No-Glue" Integration
67
+
68
+ Use the high-level Next adapter in `@ghostly-solutions/auth/next`:
69
+
70
+ - `getNextServerSession()`
71
+ - `requireNextServerSession()`
72
+ - `createNextAuthRouteHandlers({ mode: "mock" | "proxy" })`
73
+
74
+ This removes custom auth boilerplate from application code and keeps app-level integration thin.
75
+
76
+ ## Callback Page Helper
77
+
78
+ ```tsx
79
+ import { AuthCallbackHandler } from "@ghostly-solutions/auth/react";
80
+
81
+ export default function AuthCallbackPage() {
82
+ return (
83
+ <AuthCallbackHandler
84
+ processing={<div>Signing you in...</div>}
85
+ renderError={() => <div>Could not complete sign in.</div>}
86
+ />
87
+ );
88
+ }
89
+ ```
90
+
91
+ ## Error Handling
92
+
93
+ ```ts
94
+ import { AuthSdkError } from "@ghostly-solutions/auth";
95
+
96
+ try {
97
+ await authClient.requireSession();
98
+ } catch (error) {
99
+ if (error instanceof AuthSdkError) {
100
+ if (error.code === "unauthorized") {
101
+ authClient.login();
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## Documentation
108
+
109
+ - [Docs Index](./docs/index.md)
110
+ - [Overview](./docs/overview.md)
111
+ - [API Reference](./docs/api-reference.md)
112
+ - [Integration Guide](./docs/integration-guide.md)
113
+ - [Architecture](./docs/architecture.md)
114
+ - [Development and CI](./docs/development-and-ci.md)
115
+
116
+ ## Interactive Demo
117
+
118
+ Run the simulated authentication page in this repository:
119
+
120
+ ```bash
121
+ npm run demo
122
+ ```
123
+
124
+ Then open `http://localhost:4100/` to test login, callback processing, session retrieval,
125
+ and logout.
126
+
127
+ Detailed demo docs: [demo/README.md](./demo/README.md).
@@ -0,0 +1,32 @@
1
+ interface GhostlySession {
2
+ id: string;
3
+ username: string;
4
+ firstName: string | null;
5
+ lastName: string | null;
6
+ email: string;
7
+ role: string;
8
+ permissions: string[];
9
+ }
10
+
11
+ interface ProcessCallbackResult {
12
+ redirectTo: string;
13
+ session: GhostlySession;
14
+ }
15
+ interface LoginOptions {
16
+ returnTo?: string;
17
+ }
18
+ interface SessionRequestOptions {
19
+ forceRefresh?: boolean;
20
+ }
21
+ type SessionListener = (session: GhostlySession | null) => void;
22
+ interface AuthClient {
23
+ completeCallbackRedirect(): Promise<never>;
24
+ getSession(options?: SessionRequestOptions): Promise<GhostlySession | null>;
25
+ login(options?: LoginOptions): void;
26
+ logout(): Promise<void>;
27
+ processCallback(): Promise<ProcessCallbackResult>;
28
+ requireSession(): Promise<GhostlySession>;
29
+ subscribe(listener: SessionListener): () => void;
30
+ }
31
+
32
+ export type { AuthClient as A, GhostlySession as G, LoginOptions as L, ProcessCallbackResult as P, SessionListener as S, SessionRequestOptions as a };
@@ -0,0 +1,26 @@
1
+ declare const authErrorCode: {
2
+ readonly callbackMissingToken: "callback_missing_token";
3
+ readonly callbackInvalidToken: "callback_invalid_token";
4
+ readonly callbackValidationFailed: "callback_validation_failed";
5
+ readonly unauthorized: "unauthorized";
6
+ readonly networkError: "network_error";
7
+ readonly apiError: "api_error";
8
+ readonly broadcastChannelUnsupported: "broadcast_channel_unsupported";
9
+ readonly serverOriginResolutionFailed: "server_origin_resolution_failed";
10
+ };
11
+ type AuthErrorCode = (typeof authErrorCode)[keyof typeof authErrorCode];
12
+
13
+ interface AuthSdkErrorPayload {
14
+ code: AuthErrorCode;
15
+ details: unknown;
16
+ message: string;
17
+ status: number | null;
18
+ }
19
+ declare class AuthSdkError extends Error {
20
+ readonly code: AuthErrorCode;
21
+ readonly details: unknown;
22
+ readonly status: number | null;
23
+ constructor(payload: AuthSdkErrorPayload);
24
+ }
25
+
26
+ export { type AuthErrorCode as A, AuthSdkError as a, type AuthSdkErrorPayload as b, authErrorCode as c };
@@ -0,0 +1,34 @@
1
+ import { A as AuthClient, G as GhostlySession } from './auth-client-CAHMjodm.js';
2
+ export { L as LoginOptions, P as ProcessCallbackResult, S as SessionListener, a as SessionRequestOptions } from './auth-client-CAHMjodm.js';
3
+ export { A as AuthErrorCode, a as AuthSdkError, b as AuthSdkErrorPayload, c as authErrorCode } from './auth-sdk-error-DKM7PyKC.js';
4
+
5
+ declare const authEndpoints: {
6
+ readonly loginStart: "/v1/auth/keycloak/login";
7
+ readonly validateKeycloakToken: "/v1/auth/keycloak/validate";
8
+ readonly session: "/v1/auth/me";
9
+ readonly logout: "/v1/auth/logout";
10
+ };
11
+
12
+ declare const authQueryKeys: {
13
+ readonly token: "token";
14
+ };
15
+ declare const authRoutes: {
16
+ readonly root: "/";
17
+ readonly callback: "/auth/callback";
18
+ };
19
+
20
+ declare function createAuthClient(): AuthClient;
21
+
22
+ interface KeycloakValidateRequest {
23
+ token: string;
24
+ }
25
+ interface KeycloakValidateResponse {
26
+ session: GhostlySession;
27
+ }
28
+ interface AuthErrorPayload {
29
+ code: string;
30
+ details: unknown;
31
+ message: string;
32
+ }
33
+
34
+ export { AuthClient, type AuthErrorPayload, GhostlySession, type KeycloakValidateRequest, type KeycloakValidateResponse, authEndpoints, authQueryKeys, authRoutes, createAuthClient };
package/dist/index.js ADDED
@@ -0,0 +1,483 @@
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
+ // src/constants/auth-keys.ts
11
+ var authQueryKeys = {
12
+ token: "token"
13
+ };
14
+ var authStorageKeys = {
15
+ returnTo: "ghostly-auth:return-to"
16
+ };
17
+ var authBroadcast = {
18
+ channelName: "ghostly-auth-channel",
19
+ sessionUpdatedEvent: "session-updated"
20
+ };
21
+ var authRoutes = {
22
+ root: "/",
23
+ callback: "/auth/callback"
24
+ };
25
+
26
+ // src/constants/http-status.ts
27
+ var httpStatus = {
28
+ ok: 200,
29
+ noContent: 204,
30
+ badRequest: 400,
31
+ unauthorized: 401
32
+ };
33
+
34
+ // src/errors/auth-sdk-error.ts
35
+ var AuthSdkError = class extends Error {
36
+ code;
37
+ details;
38
+ status;
39
+ constructor(payload) {
40
+ super(payload.message);
41
+ this.name = "AuthSdkError";
42
+ this.code = payload.code;
43
+ this.details = payload.details;
44
+ this.status = payload.status;
45
+ }
46
+ };
47
+
48
+ // src/types/auth-error-code.ts
49
+ var authErrorCode = {
50
+ callbackMissingToken: "callback_missing_token",
51
+ callbackInvalidToken: "callback_invalid_token",
52
+ callbackValidationFailed: "callback_validation_failed",
53
+ unauthorized: "unauthorized",
54
+ networkError: "network_error",
55
+ apiError: "api_error",
56
+ broadcastChannelUnsupported: "broadcast_channel_unsupported",
57
+ serverOriginResolutionFailed: "server_origin_resolution_failed"
58
+ };
59
+
60
+ // src/core/object-guards.ts
61
+ function isObjectRecord(value) {
62
+ return typeof value === "object" && value !== null;
63
+ }
64
+ function isStringValue(value) {
65
+ return typeof value === "string";
66
+ }
67
+
68
+ // src/core/session-parser.ts
69
+ function isStringArray(value) {
70
+ return Array.isArray(value) && value.every((entry) => isStringValue(entry));
71
+ }
72
+ function isGhostlySession(value) {
73
+ if (!isObjectRecord(value)) {
74
+ return false;
75
+ }
76
+ return isStringValue(value.id) && isStringValue(value.username) && (value.firstName === null || isStringValue(value.firstName)) && (value.lastName === null || isStringValue(value.lastName)) && isStringValue(value.email) && isStringValue(value.role) && isStringArray(value.permissions);
77
+ }
78
+
79
+ // src/core/broadcast-sync.ts
80
+ function isSessionUpdatedMessage(value) {
81
+ if (!isObjectRecord(value)) {
82
+ return false;
83
+ }
84
+ if (!isStringValue(value.type)) {
85
+ return false;
86
+ }
87
+ if (value.type !== authBroadcast.sessionUpdatedEvent) {
88
+ return false;
89
+ }
90
+ return value.session === null || isGhostlySession(value.session);
91
+ }
92
+ function createUnsupportedBroadcastChannelError() {
93
+ return new AuthSdkError({
94
+ code: authErrorCode.broadcastChannelUnsupported,
95
+ details: null,
96
+ message: "BroadcastChannel is unavailable in this runtime.",
97
+ status: null
98
+ });
99
+ }
100
+ function createBroadcastSync(options) {
101
+ if (typeof BroadcastChannel === "undefined") {
102
+ throw createUnsupportedBroadcastChannelError();
103
+ }
104
+ const channel = new BroadcastChannel(authBroadcast.channelName);
105
+ const onMessage = (event) => {
106
+ const messageEvent = event;
107
+ if (!isSessionUpdatedMessage(messageEvent.data)) {
108
+ return;
109
+ }
110
+ options.onSessionUpdated(messageEvent.data.session);
111
+ };
112
+ channel.addEventListener("message", onMessage);
113
+ return {
114
+ close() {
115
+ channel.removeEventListener("message", onMessage);
116
+ channel.close();
117
+ },
118
+ publishSession(session) {
119
+ const payload = {
120
+ session,
121
+ type: authBroadcast.sessionUpdatedEvent
122
+ };
123
+ channel.postMessage(payload);
124
+ }
125
+ };
126
+ }
127
+
128
+ // src/core/callback-url.ts
129
+ function readCallbackToken(url) {
130
+ return url.searchParams.get(authQueryKeys.token);
131
+ }
132
+ function removeCallbackToken(url) {
133
+ const nextUrl = new URL(url.toString());
134
+ nextUrl.searchParams.delete(authQueryKeys.token);
135
+ return nextUrl;
136
+ }
137
+ function replaceBrowserHistory(url) {
138
+ window.history.replaceState(null, "", url.toString());
139
+ }
140
+
141
+ // src/core/http-client.ts
142
+ var jsonContentType = "application/json";
143
+ var jsonHeaderName = "content-type";
144
+ var includeCredentials = "include";
145
+ var noStoreCache = "no-store";
146
+ function toTypedValue(value) {
147
+ return value;
148
+ }
149
+ function mapHttpStatusToAuthErrorCode(status) {
150
+ if (status === httpStatus.unauthorized) {
151
+ return authErrorCode.unauthorized;
152
+ }
153
+ return authErrorCode.apiError;
154
+ }
155
+ async function parseJsonPayload(response) {
156
+ try {
157
+ return await response.json();
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+ async function parseErrorPayload(response) {
163
+ const payload = await parseJsonPayload(response);
164
+ if (!isObjectRecord(payload)) {
165
+ return {
166
+ code: null,
167
+ details: null,
168
+ message: null
169
+ };
170
+ }
171
+ const maybeCode = payload.code;
172
+ const maybeMessage = payload.message;
173
+ return {
174
+ code: isStringValue(maybeCode) ? maybeCode : null,
175
+ details: "details" in payload ? payload.details : null,
176
+ message: isStringValue(maybeMessage) ? maybeMessage : null
177
+ };
178
+ }
179
+ function buildApiErrorMessage(method, path) {
180
+ return `Auth API request failed: ${method} ${path}`;
181
+ }
182
+ function buildNetworkErrorMessage(method, path) {
183
+ return `Auth API network failure: ${method} ${path}`;
184
+ }
185
+ async function request(options) {
186
+ const expectedStatus = options.expectedStatus ?? httpStatus.ok;
187
+ const headers = new Headers();
188
+ const hasBody = typeof options.body !== "undefined";
189
+ if (hasBody) {
190
+ headers.set(jsonHeaderName, jsonContentType);
191
+ }
192
+ const requestInit = {
193
+ cache: noStoreCache,
194
+ credentials: includeCredentials,
195
+ headers,
196
+ method: options.method
197
+ };
198
+ if (hasBody) {
199
+ requestInit.body = JSON.stringify(options.body);
200
+ }
201
+ let response;
202
+ try {
203
+ response = await fetch(options.path, requestInit);
204
+ } catch (error) {
205
+ throw new AuthSdkError({
206
+ code: authErrorCode.networkError,
207
+ details: error,
208
+ message: buildNetworkErrorMessage(options.method, options.path),
209
+ status: null
210
+ });
211
+ }
212
+ if (response.status !== expectedStatus) {
213
+ const parsed = await parseErrorPayload(response);
214
+ throw new AuthSdkError({
215
+ code: mapHttpStatusToAuthErrorCode(response.status),
216
+ details: {
217
+ apiCode: parsed.code,
218
+ apiDetails: parsed.details
219
+ },
220
+ message: parsed.message ?? buildApiErrorMessage(options.method, options.path),
221
+ status: response.status
222
+ });
223
+ }
224
+ if (response.status === httpStatus.noContent) {
225
+ return toTypedValue(null);
226
+ }
227
+ const payload = await parseJsonPayload(response);
228
+ return toTypedValue(payload);
229
+ }
230
+ function getJson(path) {
231
+ return request({
232
+ method: "GET",
233
+ path
234
+ });
235
+ }
236
+ function postJson(path, body) {
237
+ return request({
238
+ body,
239
+ method: "POST",
240
+ path
241
+ });
242
+ }
243
+ function postEmpty(path) {
244
+ return request({
245
+ expectedStatus: httpStatus.noContent,
246
+ method: "POST",
247
+ path
248
+ });
249
+ }
250
+
251
+ // src/core/runtime.ts
252
+ var browserRuntimeErrorMessage = "Browser runtime is required for this auth operation.";
253
+ function isBrowserRuntime() {
254
+ return typeof window !== "undefined";
255
+ }
256
+ function assertBrowserRuntime() {
257
+ if (isBrowserRuntime()) {
258
+ return;
259
+ }
260
+ throw new AuthSdkError({
261
+ code: authErrorCode.apiError,
262
+ details: null,
263
+ message: browserRuntimeErrorMessage,
264
+ status: null
265
+ });
266
+ }
267
+
268
+ // src/core/return-to-storage.ts
269
+ function sanitizeReturnTo(value) {
270
+ if (!value) {
271
+ return authRoutes.root;
272
+ }
273
+ if (!value.startsWith(authRoutes.root)) {
274
+ return authRoutes.root;
275
+ }
276
+ const protocolRelativePrefix = "//";
277
+ if (value.startsWith(protocolRelativePrefix)) {
278
+ return authRoutes.root;
279
+ }
280
+ return value;
281
+ }
282
+ function getCurrentBrowserPath() {
283
+ return `${window.location.pathname}${window.location.search}${window.location.hash}`;
284
+ }
285
+ function saveReturnToPath(returnTo) {
286
+ assertBrowserRuntime();
287
+ const fallbackPath = getCurrentBrowserPath();
288
+ const sanitized = sanitizeReturnTo(returnTo ?? fallbackPath);
289
+ window.sessionStorage.setItem(authStorageKeys.returnTo, sanitized);
290
+ return sanitized;
291
+ }
292
+ function consumeReturnToPath() {
293
+ assertBrowserRuntime();
294
+ const value = window.sessionStorage.getItem(authStorageKeys.returnTo);
295
+ window.sessionStorage.removeItem(authStorageKeys.returnTo);
296
+ return sanitizeReturnTo(value);
297
+ }
298
+
299
+ // src/core/session-store.ts
300
+ var SessionStore = class {
301
+ listeners = /* @__PURE__ */ new Set();
302
+ resolvedSession = null;
303
+ resolveState = "pending";
304
+ getSessionIfResolved() {
305
+ if (this.resolveState === "pending") {
306
+ return null;
307
+ }
308
+ return this.resolvedSession;
309
+ }
310
+ hasResolvedSession() {
311
+ return this.resolveState === "resolved";
312
+ }
313
+ setSession(session) {
314
+ this.resolveState = "resolved";
315
+ this.resolvedSession = session;
316
+ for (const listener of this.listeners) {
317
+ listener(session);
318
+ }
319
+ }
320
+ subscribe(listener) {
321
+ this.listeners.add(listener);
322
+ return () => {
323
+ this.listeners.delete(listener);
324
+ };
325
+ }
326
+ };
327
+
328
+ // src/core/auth-client.ts
329
+ function createPendingRedirectPromise() {
330
+ return new Promise(() => {
331
+ });
332
+ }
333
+ function createInvalidSessionPayloadError(path) {
334
+ return new AuthSdkError({
335
+ code: authErrorCode.apiError,
336
+ details: null,
337
+ message: `Auth API response has invalid session shape: ${path}`,
338
+ status: null
339
+ });
340
+ }
341
+ function toValidatedSession(payload, path) {
342
+ if (!isGhostlySession(payload)) {
343
+ throw createInvalidSessionPayloadError(path);
344
+ }
345
+ return payload;
346
+ }
347
+ function toCallbackFailure(error) {
348
+ if (error instanceof AuthSdkError) {
349
+ if (error.status === httpStatus.unauthorized) {
350
+ return new AuthSdkError({
351
+ code: authErrorCode.callbackInvalidToken,
352
+ details: error.details,
353
+ message: "Callback JWT is invalid or expired.",
354
+ status: error.status
355
+ });
356
+ }
357
+ return new AuthSdkError({
358
+ code: authErrorCode.callbackValidationFailed,
359
+ details: error.details,
360
+ message: "Keycloak callback validation failed.",
361
+ status: error.status
362
+ });
363
+ }
364
+ return new AuthSdkError({
365
+ code: authErrorCode.callbackValidationFailed,
366
+ details: error,
367
+ message: "Keycloak callback validation failed.",
368
+ status: null
369
+ });
370
+ }
371
+ function createNoopBroadcastSync() {
372
+ return {
373
+ close() {
374
+ },
375
+ publishSession() {
376
+ }
377
+ };
378
+ }
379
+ function createSafeBroadcastSync(onSessionUpdated) {
380
+ try {
381
+ return createBroadcastSync({
382
+ onSessionUpdated
383
+ });
384
+ } catch (error) {
385
+ if (error instanceof AuthSdkError && error.code === authErrorCode.broadcastChannelUnsupported) {
386
+ return createNoopBroadcastSync();
387
+ }
388
+ throw error;
389
+ }
390
+ }
391
+ async function fetchCurrentSessionFromApi() {
392
+ const payload = await getJson(authEndpoints.session);
393
+ if (payload === null) {
394
+ return null;
395
+ }
396
+ return toValidatedSession(payload, authEndpoints.session);
397
+ }
398
+ function createAuthClient() {
399
+ assertBrowserRuntime();
400
+ const sessionStore = new SessionStore();
401
+ const broadcastSync = createSafeBroadcastSync((session) => {
402
+ sessionStore.setSession(session);
403
+ });
404
+ const getSession = async (options) => {
405
+ const forceRefresh = options?.forceRefresh ?? false;
406
+ if (sessionStore.hasResolvedSession() && !forceRefresh) {
407
+ return sessionStore.getSessionIfResolved();
408
+ }
409
+ const session = await fetchCurrentSessionFromApi();
410
+ sessionStore.setSession(session);
411
+ broadcastSync.publishSession(session);
412
+ return session;
413
+ };
414
+ const requireSession = async () => {
415
+ const session = await getSession();
416
+ if (session) {
417
+ return session;
418
+ }
419
+ throw new AuthSdkError({
420
+ code: authErrorCode.unauthorized,
421
+ details: null,
422
+ message: "Authenticated session is required.",
423
+ status: httpStatus.unauthorized
424
+ });
425
+ };
426
+ const login = (options) => {
427
+ saveReturnToPath(options?.returnTo);
428
+ window.location.assign(authEndpoints.loginStart);
429
+ };
430
+ const processCallback = async () => {
431
+ const currentUrl = new URL(window.location.href);
432
+ const token = readCallbackToken(currentUrl);
433
+ if (!token) {
434
+ throw new AuthSdkError({
435
+ code: authErrorCode.callbackMissingToken,
436
+ details: null,
437
+ message: "Missing callback token query parameter.",
438
+ status: httpStatus.badRequest
439
+ });
440
+ }
441
+ const cleanedUrl = removeCallbackToken(currentUrl);
442
+ replaceBrowserHistory(cleanedUrl);
443
+ try {
444
+ const payload = await postJson(
445
+ authEndpoints.validateKeycloakToken,
446
+ { token }
447
+ );
448
+ const session = toValidatedSession(payload.session, authEndpoints.validateKeycloakToken);
449
+ sessionStore.setSession(session);
450
+ broadcastSync.publishSession(session);
451
+ return {
452
+ redirectTo: consumeReturnToPath(),
453
+ session
454
+ };
455
+ } catch (error) {
456
+ throw toCallbackFailure(error);
457
+ }
458
+ };
459
+ const completeCallbackRedirect = async () => {
460
+ const result = await processCallback();
461
+ window.location.replace(result.redirectTo);
462
+ return createPendingRedirectPromise();
463
+ };
464
+ const logout = async () => {
465
+ await postEmpty(authEndpoints.logout);
466
+ sessionStore.setSession(null);
467
+ broadcastSync.publishSession(null);
468
+ };
469
+ const subscribe = sessionStore.subscribe.bind(sessionStore);
470
+ return {
471
+ completeCallbackRedirect,
472
+ getSession,
473
+ login,
474
+ logout,
475
+ processCallback,
476
+ requireSession,
477
+ subscribe
478
+ };
479
+ }
480
+
481
+ export { AuthSdkError, authEndpoints, authErrorCode, authQueryKeys, authRoutes, createAuthClient };
482
+ //# sourceMappingURL=index.js.map
483
+ //# sourceMappingURL=index.js.map