@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 +127 -0
- package/dist/auth-client-CAHMjodm.d.ts +32 -0
- package/dist/auth-sdk-error-DKM7PyKC.d.ts +26 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +483 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +47 -0
- package/dist/next.js +899 -0
- package/dist/next.js.map +1 -0
- package/dist/react.d.ts +50 -0
- package/dist/react.js +681 -0
- package/dist/react.js.map +1 -0
- package/docs/api-reference.md +145 -0
- package/docs/architecture.md +62 -0
- package/docs/development-and-ci.md +53 -0
- package/docs/index.md +22 -0
- package/docs/integration-guide.md +142 -0
- package/docs/overview.md +41 -0
- package/package.json +66 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/auth-endpoints.ts","../src/constants/http-status.ts","../src/errors/auth-sdk-error.ts","../src/types/auth-error-code.ts","../src/constants/auth-keys.ts","../src/core/object-guards.ts","../src/core/session-parser.ts","../src/core/broadcast-sync.ts","../src/core/callback-url.ts","../src/core/http-client.ts","../src/core/runtime.ts","../src/core/return-to-storage.ts","../src/core/session-store.ts","../src/core/auth-client.ts","../src/adapters/react/auth-callback-handler.tsx","../src/adapters/react/auth-context.tsx","../src/adapters/react/auth-provider.tsx","../src/adapters/react/use-auth.ts","../src/adapters/react/auth-session-gate.tsx"],"names":["useMemo","useState","useEffect","jsx","Fragment"],"mappings":"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","file":"react.js","sourcesContent":["const authApiPrefix = \"/v1/auth\";\n\nexport const authEndpoints = {\n loginStart: `${authApiPrefix}/keycloak/login`,\n validateKeycloakToken: `${authApiPrefix}/keycloak/validate`,\n session: `${authApiPrefix}/me`,\n logout: `${authApiPrefix}/logout`,\n} as const;\n","export const httpStatus = {\n ok: 200,\n found: 302,\n noContent: 204,\n badRequest: 400,\n unauthorized: 401,\n} as const;\n","import type { AuthErrorCode } from \"../types/auth-error-code\";\n\nexport interface AuthSdkErrorPayload {\n code: AuthErrorCode;\n details: unknown;\n message: string;\n status: number | null;\n}\n\nexport class AuthSdkError extends Error {\n readonly code: AuthErrorCode;\n readonly details: unknown;\n readonly status: number | null;\n\n constructor(payload: AuthSdkErrorPayload) {\n super(payload.message);\n this.name = \"AuthSdkError\";\n this.code = payload.code;\n this.details = payload.details;\n this.status = payload.status;\n }\n}\n","export const authErrorCode = {\n callbackMissingToken: \"callback_missing_token\",\n callbackInvalidToken: \"callback_invalid_token\",\n callbackValidationFailed: \"callback_validation_failed\",\n unauthorized: \"unauthorized\",\n networkError: \"network_error\",\n apiError: \"api_error\",\n broadcastChannelUnsupported: \"broadcast_channel_unsupported\",\n serverOriginResolutionFailed: \"server_origin_resolution_failed\",\n} as const;\n\nexport type AuthErrorCode = (typeof authErrorCode)[keyof typeof authErrorCode];\n","export const authQueryKeys = {\n token: \"token\",\n} as const;\n\nexport const authStorageKeys = {\n returnTo: \"ghostly-auth:return-to\",\n} as const;\n\nexport const authBroadcast = {\n channelName: \"ghostly-auth-channel\",\n sessionUpdatedEvent: \"session-updated\",\n} as const;\n\nexport const authRoutes = {\n root: \"/\",\n callback: \"/auth/callback\",\n} as const;\n","export function isObjectRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isStringValue(value: unknown): value is string {\n return typeof value === \"string\";\n}\n","import type { GhostlySession } from \"../types/ghostly-session\";\nimport { isObjectRecord, isStringValue } from \"./object-guards\";\n\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => isStringValue(entry));\n}\n\nexport function isGhostlySession(value: unknown): value is GhostlySession {\n if (!isObjectRecord(value)) {\n return false;\n }\n\n return (\n isStringValue(value.id) &&\n isStringValue(value.username) &&\n (value.firstName === null || isStringValue(value.firstName)) &&\n (value.lastName === null || isStringValue(value.lastName)) &&\n isStringValue(value.email) &&\n isStringValue(value.role) &&\n isStringArray(value.permissions)\n );\n}\n","import { authBroadcast } from \"../constants/auth-keys\";\nimport { AuthSdkError } from \"../errors/auth-sdk-error\";\nimport { authErrorCode } from \"../types/auth-error-code\";\nimport type { GhostlySession } from \"../types/ghostly-session\";\nimport { isObjectRecord, isStringValue } from \"./object-guards\";\nimport { isGhostlySession } from \"./session-parser\";\n\ninterface SessionUpdatedMessage {\n session: GhostlySession | null;\n type: typeof authBroadcast.sessionUpdatedEvent;\n}\n\nexport interface BroadcastSync {\n close(): void;\n publishSession(session: GhostlySession | null): void;\n}\n\ninterface CreateBroadcastSyncOptions {\n onSessionUpdated: (session: GhostlySession | null) => void;\n}\n\nfunction isSessionUpdatedMessage(value: unknown): value is SessionUpdatedMessage {\n if (!isObjectRecord(value)) {\n return false;\n }\n\n if (!isStringValue(value.type)) {\n return false;\n }\n\n if (value.type !== authBroadcast.sessionUpdatedEvent) {\n return false;\n }\n\n return value.session === null || isGhostlySession(value.session);\n}\n\nfunction createUnsupportedBroadcastChannelError(): AuthSdkError {\n return new AuthSdkError({\n code: authErrorCode.broadcastChannelUnsupported,\n details: null,\n message: \"BroadcastChannel is unavailable in this runtime.\",\n status: null,\n });\n}\n\nexport function createBroadcastSync(options: CreateBroadcastSyncOptions): BroadcastSync {\n if (typeof BroadcastChannel === \"undefined\") {\n throw createUnsupportedBroadcastChannelError();\n }\n\n const channel = new BroadcastChannel(authBroadcast.channelName);\n\n const onMessage: EventListener = (event) => {\n const messageEvent = event as MessageEvent<unknown>;\n\n if (!isSessionUpdatedMessage(messageEvent.data)) {\n return;\n }\n\n options.onSessionUpdated(messageEvent.data.session);\n };\n\n channel.addEventListener(\"message\", onMessage);\n\n return {\n close() {\n channel.removeEventListener(\"message\", onMessage);\n channel.close();\n },\n publishSession(session) {\n const payload: SessionUpdatedMessage = {\n session,\n type: authBroadcast.sessionUpdatedEvent,\n };\n\n channel.postMessage(payload);\n },\n };\n}\n","import { authQueryKeys } from \"../constants/auth-keys\";\n\nexport function readCallbackToken(url: URL): string | null {\n return url.searchParams.get(authQueryKeys.token);\n}\n\nexport function removeCallbackToken(url: URL): URL {\n const nextUrl = new URL(url.toString());\n nextUrl.searchParams.delete(authQueryKeys.token);\n return nextUrl;\n}\n\nexport function replaceBrowserHistory(url: URL): void {\n window.history.replaceState(null, \"\", url.toString());\n}\n","import { httpStatus } from \"../constants/http-status\";\nimport { AuthSdkError } from \"../errors/auth-sdk-error\";\nimport { authErrorCode } from \"../types/auth-error-code\";\nimport { isObjectRecord, isStringValue } from \"./object-guards\";\n\nconst jsonContentType = \"application/json\";\nconst jsonHeaderName = \"content-type\";\nconst includeCredentials: RequestCredentials = \"include\";\nconst noStoreCache: RequestCache = \"no-store\";\n\ninterface RequestOptions<TBody> {\n body?: TBody;\n expectedStatus?: number;\n method: \"GET\" | \"POST\";\n path: string;\n}\n\ninterface ParsedErrorPayload {\n code: string | null;\n details: unknown;\n message: string | null;\n}\n\nfunction toTypedValue<TValue>(value: unknown): TValue {\n return value as TValue;\n}\n\nfunction mapHttpStatusToAuthErrorCode(status: number) {\n if (status === httpStatus.unauthorized) {\n return authErrorCode.unauthorized;\n }\n\n return authErrorCode.apiError;\n}\n\nasync function parseJsonPayload(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nasync function parseErrorPayload(response: Response): Promise<ParsedErrorPayload> {\n const payload = await parseJsonPayload(response);\n\n if (!isObjectRecord(payload)) {\n return {\n code: null,\n details: null,\n message: null,\n };\n }\n\n const maybeCode = payload.code;\n const maybeMessage = payload.message;\n\n return {\n code: isStringValue(maybeCode) ? maybeCode : null,\n details: \"details\" in payload ? payload.details : null,\n message: isStringValue(maybeMessage) ? maybeMessage : null,\n };\n}\n\nfunction buildApiErrorMessage(method: RequestOptions<unknown>[\"method\"], path: string): string {\n return `Auth API request failed: ${method} ${path}`;\n}\n\nfunction buildNetworkErrorMessage(method: RequestOptions<unknown>[\"method\"], path: string): string {\n return `Auth API network failure: ${method} ${path}`;\n}\n\nasync function request<TResponse, TBody = undefined>(\n options: RequestOptions<TBody>,\n): Promise<TResponse> {\n const expectedStatus = options.expectedStatus ?? httpStatus.ok;\n const headers = new Headers();\n const hasBody = typeof options.body !== \"undefined\";\n\n if (hasBody) {\n headers.set(jsonHeaderName, jsonContentType);\n }\n\n const requestInit: RequestInit = {\n cache: noStoreCache,\n credentials: includeCredentials,\n headers,\n method: options.method,\n };\n\n if (hasBody) {\n requestInit.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n\n try {\n response = await fetch(options.path, requestInit);\n } catch (error) {\n throw new AuthSdkError({\n code: authErrorCode.networkError,\n details: error,\n message: buildNetworkErrorMessage(options.method, options.path),\n status: null,\n });\n }\n\n if (response.status !== expectedStatus) {\n const parsed = await parseErrorPayload(response);\n\n throw new AuthSdkError({\n code: mapHttpStatusToAuthErrorCode(response.status),\n details: {\n apiCode: parsed.code,\n apiDetails: parsed.details,\n },\n message: parsed.message ?? buildApiErrorMessage(options.method, options.path),\n status: response.status,\n });\n }\n\n if (response.status === httpStatus.noContent) {\n return toTypedValue<TResponse>(null);\n }\n\n const payload = await parseJsonPayload(response);\n return toTypedValue<TResponse>(payload);\n}\n\nexport function getJson<TResponse>(path: string): Promise<TResponse> {\n return request<TResponse>({\n method: \"GET\",\n path,\n });\n}\n\nexport function postJson<TResponse, TBody>(path: string, body: TBody): Promise<TResponse> {\n return request<TResponse, TBody>({\n body,\n method: \"POST\",\n path,\n });\n}\n\nexport function postEmpty(path: string): Promise<null> {\n return request<null>({\n expectedStatus: httpStatus.noContent,\n method: \"POST\",\n path,\n });\n}\n","import { AuthSdkError } from \"../errors/auth-sdk-error\";\nimport { authErrorCode } from \"../types/auth-error-code\";\n\nconst browserRuntimeErrorMessage = \"Browser runtime is required for this auth operation.\";\n\nexport function isBrowserRuntime(): boolean {\n return typeof window !== \"undefined\";\n}\n\nexport function assertBrowserRuntime(): void {\n if (isBrowserRuntime()) {\n return;\n }\n\n throw new AuthSdkError({\n code: authErrorCode.apiError,\n details: null,\n message: browserRuntimeErrorMessage,\n status: null,\n });\n}\n","import { authRoutes, authStorageKeys } from \"../constants/auth-keys\";\nimport { assertBrowserRuntime } from \"./runtime\";\n\nfunction sanitizeReturnTo(value: string | null | undefined): string {\n if (!value) {\n return authRoutes.root;\n }\n\n if (!value.startsWith(authRoutes.root)) {\n return authRoutes.root;\n }\n\n const protocolRelativePrefix = \"//\";\n\n if (value.startsWith(protocolRelativePrefix)) {\n return authRoutes.root;\n }\n\n return value;\n}\n\nfunction getCurrentBrowserPath(): string {\n return `${window.location.pathname}${window.location.search}${window.location.hash}`;\n}\n\nexport function saveReturnToPath(returnTo: string | undefined): string {\n assertBrowserRuntime();\n\n const fallbackPath = getCurrentBrowserPath();\n const sanitized = sanitizeReturnTo(returnTo ?? fallbackPath);\n window.sessionStorage.setItem(authStorageKeys.returnTo, sanitized);\n return sanitized;\n}\n\nexport function consumeReturnToPath(): string {\n assertBrowserRuntime();\n\n const value = window.sessionStorage.getItem(authStorageKeys.returnTo);\n window.sessionStorage.removeItem(authStorageKeys.returnTo);\n return sanitizeReturnTo(value);\n}\n","import type { SessionListener } from \"../types/auth-client\";\nimport type { GhostlySession } from \"../types/ghostly-session\";\n\ntype SessionResolveState = \"pending\" | \"resolved\";\n\nexport class SessionStore {\n private listeners = new Set<SessionListener>();\n private resolvedSession: GhostlySession | null = null;\n private resolveState: SessionResolveState = \"pending\";\n\n getSessionIfResolved(): GhostlySession | null {\n if (this.resolveState === \"pending\") {\n return null;\n }\n\n return this.resolvedSession;\n }\n\n hasResolvedSession(): boolean {\n return this.resolveState === \"resolved\";\n }\n\n setSession(session: GhostlySession | null): void {\n this.resolveState = \"resolved\";\n this.resolvedSession = session;\n\n for (const listener of this.listeners) {\n listener(session);\n }\n }\n\n subscribe(listener: SessionListener): () => void {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n}\n","import { authEndpoints } from \"../constants/auth-endpoints\";\nimport { httpStatus } from \"../constants/http-status\";\nimport { AuthSdkError } from \"../errors/auth-sdk-error\";\nimport type {\n AuthClient,\n LoginOptions,\n ProcessCallbackResult,\n SessionRequestOptions,\n} from \"../types/auth-client\";\nimport { authErrorCode } from \"../types/auth-error-code\";\nimport type { GhostlySession } from \"../types/ghostly-session\";\nimport type { BroadcastSync } from \"./broadcast-sync\";\nimport { createBroadcastSync } from \"./broadcast-sync\";\nimport { readCallbackToken, removeCallbackToken, replaceBrowserHistory } from \"./callback-url\";\nimport { getJson, postEmpty, postJson } from \"./http-client\";\nimport { consumeReturnToPath, saveReturnToPath } from \"./return-to-storage\";\nimport { assertBrowserRuntime } from \"./runtime\";\nimport { isGhostlySession } from \"./session-parser\";\nimport { SessionStore } from \"./session-store\";\n\ninterface ValidateTokenRequest {\n token: string;\n}\n\ninterface ValidateTokenResponse {\n session: unknown;\n}\n\nfunction createPendingRedirectPromise<TValue>(): Promise<TValue> {\n return new Promise(() => {\n // Location redirect never resolves within current runtime execution.\n });\n}\n\nfunction createInvalidSessionPayloadError(path: string): AuthSdkError {\n return new AuthSdkError({\n code: authErrorCode.apiError,\n details: null,\n message: `Auth API response has invalid session shape: ${path}`,\n status: null,\n });\n}\n\nfunction toValidatedSession(payload: unknown, path: string): GhostlySession {\n if (!isGhostlySession(payload)) {\n throw createInvalidSessionPayloadError(path);\n }\n\n return payload;\n}\n\nfunction toCallbackFailure(error: unknown): AuthSdkError {\n if (error instanceof AuthSdkError) {\n if (error.status === httpStatus.unauthorized) {\n return new AuthSdkError({\n code: authErrorCode.callbackInvalidToken,\n details: error.details,\n message: \"Callback JWT is invalid or expired.\",\n status: error.status,\n });\n }\n\n return new AuthSdkError({\n code: authErrorCode.callbackValidationFailed,\n details: error.details,\n message: \"Keycloak callback validation failed.\",\n status: error.status,\n });\n }\n\n return new AuthSdkError({\n code: authErrorCode.callbackValidationFailed,\n details: error,\n message: \"Keycloak callback validation failed.\",\n status: null,\n });\n}\n\nfunction createNoopBroadcastSync(): BroadcastSync {\n return {\n close() {},\n publishSession() {},\n };\n}\n\nfunction createSafeBroadcastSync(\n onSessionUpdated: (session: GhostlySession | null) => void,\n): BroadcastSync {\n try {\n return createBroadcastSync({\n onSessionUpdated,\n });\n } catch (error) {\n if (error instanceof AuthSdkError && error.code === authErrorCode.broadcastChannelUnsupported) {\n return createNoopBroadcastSync();\n }\n\n throw error;\n }\n}\n\nasync function fetchCurrentSessionFromApi(): Promise<GhostlySession | null> {\n const payload = await getJson<unknown>(authEndpoints.session);\n\n if (payload === null) {\n return null;\n }\n\n return toValidatedSession(payload, authEndpoints.session);\n}\n\nexport function createAuthClient(): AuthClient {\n assertBrowserRuntime();\n\n const sessionStore = new SessionStore();\n const broadcastSync = createSafeBroadcastSync((session) => {\n sessionStore.setSession(session);\n });\n\n const getSession = async (options?: SessionRequestOptions): Promise<GhostlySession | null> => {\n const forceRefresh = options?.forceRefresh ?? false;\n\n if (sessionStore.hasResolvedSession() && !forceRefresh) {\n return sessionStore.getSessionIfResolved();\n }\n\n const session = await fetchCurrentSessionFromApi();\n sessionStore.setSession(session);\n broadcastSync.publishSession(session);\n\n return session;\n };\n\n const requireSession = async (): Promise<GhostlySession> => {\n const session = await getSession();\n\n if (session) {\n return session;\n }\n\n throw new AuthSdkError({\n code: authErrorCode.unauthorized,\n details: null,\n message: \"Authenticated session is required.\",\n status: httpStatus.unauthorized,\n });\n };\n\n const login = (options?: LoginOptions): void => {\n saveReturnToPath(options?.returnTo);\n window.location.assign(authEndpoints.loginStart);\n };\n\n const processCallback = async (): Promise<ProcessCallbackResult> => {\n const currentUrl = new URL(window.location.href);\n const token = readCallbackToken(currentUrl);\n\n if (!token) {\n throw new AuthSdkError({\n code: authErrorCode.callbackMissingToken,\n details: null,\n message: \"Missing callback token query parameter.\",\n status: httpStatus.badRequest,\n });\n }\n\n const cleanedUrl = removeCallbackToken(currentUrl);\n replaceBrowserHistory(cleanedUrl);\n\n try {\n const payload = await postJson<ValidateTokenResponse, ValidateTokenRequest>(\n authEndpoints.validateKeycloakToken,\n { token },\n );\n\n const session = toValidatedSession(payload.session, authEndpoints.validateKeycloakToken);\n sessionStore.setSession(session);\n broadcastSync.publishSession(session);\n\n return {\n redirectTo: consumeReturnToPath(),\n session,\n };\n } catch (error) {\n throw toCallbackFailure(error);\n }\n };\n\n const completeCallbackRedirect = async (): Promise<never> => {\n const result = await processCallback();\n window.location.replace(result.redirectTo);\n return createPendingRedirectPromise<never>();\n };\n\n const logout = async (): Promise<void> => {\n await postEmpty(authEndpoints.logout);\n sessionStore.setSession(null);\n broadcastSync.publishSession(null);\n };\n\n const subscribe = sessionStore.subscribe.bind(sessionStore);\n\n return {\n completeCallbackRedirect,\n getSession,\n login,\n logout,\n processCallback,\n requireSession,\n subscribe,\n };\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { createAuthClient } from \"../../core/auth-client\";\nimport { AuthSdkError } from \"../../errors/auth-sdk-error\";\nimport type { AuthClient } from \"../../types/auth-client\";\n\nexport type AuthCallbackStatus = \"processing\" | \"failed\";\n\nexport interface UseAuthCallbackRedirectOptions {\n client?: AuthClient;\n}\n\nexport interface UseAuthCallbackRedirectResult {\n error: AuthSdkError | null;\n status: AuthCallbackStatus;\n}\n\nfunction normalizeAuthError(error: unknown): AuthSdkError {\n if (error instanceof AuthSdkError) {\n return error;\n }\n\n return new AuthSdkError({\n code: \"callback_validation_failed\",\n details: error,\n message: \"Auth callback redirect failed.\",\n status: null,\n });\n}\n\nexport function useAuthCallbackRedirect(\n options: UseAuthCallbackRedirectOptions = {},\n): UseAuthCallbackRedirectResult {\n const client = useMemo(() => options.client ?? createAuthClient(), [options.client]);\n const [error, setError] = useState<AuthSdkError | null>(null);\n\n useEffect(() => {\n let isActive = true;\n\n void client.completeCallbackRedirect().catch((caughtError) => {\n if (!isActive) {\n return;\n }\n\n setError(normalizeAuthError(caughtError));\n });\n\n return () => {\n isActive = false;\n };\n }, [client]);\n\n if (error) {\n return {\n error,\n status: \"failed\",\n };\n }\n\n return {\n error: null,\n status: \"processing\",\n };\n}\n\nexport interface AuthCallbackHandlerProps {\n client?: AuthClient;\n processing: ReactNode;\n renderError: (error: AuthSdkError) => ReactNode;\n}\n\nexport function AuthCallbackHandler(props: AuthCallbackHandlerProps) {\n const state = useAuthCallbackRedirect({\n client: props.client,\n });\n\n if (state.status === \"failed\" && state.error) {\n return props.renderError(state.error);\n }\n\n return <>{props.processing}</>;\n}\n","\"use client\";\n\nimport { createContext } from \"react\";\nimport type { AuthSdkError } from \"../../errors/auth-sdk-error\";\nimport type { LoginOptions } from \"../../types/auth-client\";\nimport type { GhostlySession } from \"../../types/ghostly-session\";\n\nexport interface AuthContextValue {\n error: AuthSdkError | null;\n isLoading: boolean;\n login: (options?: LoginOptions) => void;\n logout: () => Promise<void>;\n refresh: () => Promise<GhostlySession | null>;\n session: GhostlySession | null;\n}\n\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n","\"use client\";\n\nimport { type ReactNode, useCallback, useEffect, useMemo, useState } from \"react\";\nimport { createAuthClient } from \"../../core/auth-client\";\nimport { AuthSdkError } from \"../../errors/auth-sdk-error\";\nimport type { AuthClient, LoginOptions } from \"../../types/auth-client\";\nimport type { GhostlySession } from \"../../types/ghostly-session\";\nimport { AuthContext } from \"./auth-context\";\n\nconst initialLoadingState = true;\n\nfunction toAuthError(error: unknown): AuthSdkError {\n if (error instanceof AuthSdkError) {\n return error;\n }\n\n return new AuthSdkError({\n code: \"api_error\",\n details: error,\n message: \"Unexpected auth adapter error.\",\n status: null,\n });\n}\n\nfunction createDeferredAuthClient(): AuthClient {\n let authClient: AuthClient | null = null;\n\n const resolveClient = (): AuthClient => {\n authClient ??= createAuthClient();\n return authClient;\n };\n\n return {\n completeCallbackRedirect() {\n return resolveClient().completeCallbackRedirect();\n },\n getSession(options) {\n return resolveClient().getSession(options);\n },\n login(options?: LoginOptions) {\n resolveClient().login(options);\n },\n logout() {\n return resolveClient().logout();\n },\n processCallback() {\n return resolveClient().processCallback();\n },\n requireSession() {\n return resolveClient().requireSession();\n },\n subscribe(listener) {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n\n return resolveClient().subscribe(listener);\n },\n };\n}\n\nexport interface AuthProviderProps {\n children: ReactNode;\n client?: AuthClient;\n}\n\nexport function AuthProvider(props: AuthProviderProps) {\n const authClient = useMemo(() => props.client ?? createDeferredAuthClient(), [props.client]);\n const [session, setSession] = useState<GhostlySession | null>(null);\n const [error, setError] = useState<AuthSdkError | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(initialLoadingState);\n\n useEffect(() => {\n let isActive = true;\n\n const unsubscribe = authClient.subscribe((nextSession) => {\n if (!isActive) {\n return;\n }\n\n setSession(nextSession);\n setIsLoading(false);\n });\n\n void authClient\n .getSession()\n .then((nextSession) => {\n if (!isActive) {\n return;\n }\n\n setSession(nextSession);\n })\n .catch((caughtError: unknown) => {\n if (!isActive) {\n return;\n }\n\n setError(toAuthError(caughtError));\n })\n .finally(() => {\n if (!isActive) {\n return;\n }\n\n setIsLoading(false);\n });\n\n return () => {\n isActive = false;\n unsubscribe();\n };\n }, [authClient]);\n\n const login = useCallback(\n (options?: LoginOptions) => {\n setError(null);\n authClient.login(options);\n },\n [authClient],\n );\n\n const logout = useCallback(async () => {\n setError(null);\n\n try {\n await authClient.logout();\n setSession(null);\n } catch (caughtError) {\n const normalizedError = toAuthError(caughtError);\n setError(normalizedError);\n throw normalizedError;\n }\n }, [authClient]);\n\n const refresh = useCallback(async () => {\n setError(null);\n\n try {\n const nextSession = await authClient.getSession({ forceRefresh: true });\n setSession(nextSession);\n return nextSession;\n } catch (caughtError) {\n const normalizedError = toAuthError(caughtError);\n setError(normalizedError);\n throw normalizedError;\n }\n }, [authClient]);\n\n const contextValue = useMemo(\n () => ({\n error,\n isLoading,\n login,\n logout,\n refresh,\n session,\n }),\n [error, isLoading, login, logout, refresh, session],\n );\n\n return <AuthContext.Provider value={contextValue}>{props.children}</AuthContext.Provider>;\n}\n","\"use client\";\n\nimport { useContext } from \"react\";\nimport { AuthContext } from \"./auth-context\";\n\nconst missingProviderErrorMessage = \"useAuth must be used inside AuthProvider.\";\n\nexport function useAuth() {\n const context = useContext(AuthContext);\n\n if (context) {\n return context;\n }\n\n throw new Error(missingProviderErrorMessage);\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport type { LoginOptions } from \"../../types/auth-client\";\nimport type { GhostlySession } from \"../../types/ghostly-session\";\nimport { useAuth } from \"./use-auth\";\n\ninterface UnauthorizedRenderProps {\n login: (options?: LoginOptions) => void;\n}\n\nexport interface AuthSessionGateProps {\n authorized: (session: GhostlySession) => ReactNode;\n loading?: ReactNode;\n unauthorized: ReactNode | ((props: UnauthorizedRenderProps) => ReactNode);\n}\n\nfunction resolveUnauthorizedContent(\n input: AuthSessionGateProps[\"unauthorized\"],\n props: UnauthorizedRenderProps,\n): ReactNode {\n if (typeof input === \"function\") {\n return input(props);\n }\n\n return input;\n}\n\nexport function AuthSessionGate(props: AuthSessionGateProps) {\n const auth = useAuth();\n\n if (auth.isLoading) {\n return <>{props.loading ?? null}</>;\n }\n\n if (!auth.session) {\n return (\n <>\n {resolveUnauthorizedContent(props.unauthorized, {\n login: auth.login,\n })}\n </>\n );\n }\n\n return <>{props.authorized(auth.session)}</>;\n}\n"]}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## Core Types
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
export interface GhostlySession {
|
|
7
|
+
id: string;
|
|
8
|
+
username: string;
|
|
9
|
+
firstName: string | null;
|
|
10
|
+
lastName: string | null;
|
|
11
|
+
email: string;
|
|
12
|
+
role: string;
|
|
13
|
+
permissions: string[];
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
export interface KeycloakValidateRequest {
|
|
19
|
+
token: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface KeycloakValidateResponse {
|
|
23
|
+
session: GhostlySession;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AuthErrorPayload {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
details: unknown;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
export const authErrorCode = {
|
|
35
|
+
callbackMissingToken: "callback_missing_token",
|
|
36
|
+
callbackInvalidToken: "callback_invalid_token",
|
|
37
|
+
callbackValidationFailed: "callback_validation_failed",
|
|
38
|
+
unauthorized: "unauthorized",
|
|
39
|
+
networkError: "network_error",
|
|
40
|
+
apiError: "api_error",
|
|
41
|
+
broadcastChannelUnsupported: "broadcast_channel_unsupported",
|
|
42
|
+
serverOriginResolutionFailed: "server_origin_resolution_failed",
|
|
43
|
+
} as const;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
export class AuthSdkError extends Error {
|
|
48
|
+
readonly code: AuthErrorCode;
|
|
49
|
+
readonly status: number | null;
|
|
50
|
+
readonly details: unknown;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Core Client
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
export interface AuthClient {
|
|
58
|
+
login(options?: { returnTo?: string }): void;
|
|
59
|
+
processCallback(): Promise<{ session: GhostlySession; redirectTo: string }>;
|
|
60
|
+
completeCallbackRedirect(): Promise<never>;
|
|
61
|
+
getSession(options?: { forceRefresh?: boolean }): Promise<GhostlySession | null>;
|
|
62
|
+
requireSession(): Promise<GhostlySession>;
|
|
63
|
+
logout(): Promise<void>;
|
|
64
|
+
subscribe(listener: (session: GhostlySession | null) => void): () => void;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `createAuthClient()`
|
|
69
|
+
|
|
70
|
+
Creates a browser runtime client. Throws `AuthSdkError` when browser requirements are not met.
|
|
71
|
+
|
|
72
|
+
### `login(options?)`
|
|
73
|
+
|
|
74
|
+
- Stores `returnTo` in `sessionStorage`.
|
|
75
|
+
- Redirects to `GET /v1/auth/keycloak/login`.
|
|
76
|
+
|
|
77
|
+
### `processCallback()`
|
|
78
|
+
|
|
79
|
+
- Reads `token` from callback URL query.
|
|
80
|
+
- Removes token from URL using `history.replaceState`.
|
|
81
|
+
- Sends `POST /v1/auth/keycloak/validate` with `{ token }`.
|
|
82
|
+
- Returns `{ session, redirectTo }`.
|
|
83
|
+
|
|
84
|
+
### `completeCallbackRedirect()`
|
|
85
|
+
|
|
86
|
+
Processes callback and performs `location.replace(redirectTo)`.
|
|
87
|
+
|
|
88
|
+
### `getSession(options?)`
|
|
89
|
+
|
|
90
|
+
- Lazy by default: returns cached resolved session.
|
|
91
|
+
- With `forceRefresh: true`: re-fetches `GET /v1/auth/me`.
|
|
92
|
+
|
|
93
|
+
### `requireSession()`
|
|
94
|
+
|
|
95
|
+
Returns session or throws `AuthSdkError` with `code = "unauthorized"`.
|
|
96
|
+
|
|
97
|
+
### `logout()`
|
|
98
|
+
|
|
99
|
+
Calls `POST /v1/auth/logout`, clears session state, and broadcasts update.
|
|
100
|
+
|
|
101
|
+
### `subscribe(listener)`
|
|
102
|
+
|
|
103
|
+
Subscribes to in-memory session updates for the current tab.
|
|
104
|
+
|
|
105
|
+
## React Adapter
|
|
106
|
+
|
|
107
|
+
Entry: `@ghostly-solutions/auth/react`
|
|
108
|
+
|
|
109
|
+
- `AuthProvider`
|
|
110
|
+
- `useAuth`
|
|
111
|
+
- `AuthCallbackHandler`
|
|
112
|
+
- `useAuthCallbackRedirect`
|
|
113
|
+
- `AuthSessionGate`
|
|
114
|
+
|
|
115
|
+
`useAuth()` returns:
|
|
116
|
+
|
|
117
|
+
- `session: GhostlySession | null`
|
|
118
|
+
- `isLoading: boolean`
|
|
119
|
+
- `error: AuthSdkError | null`
|
|
120
|
+
- `login(options?)`
|
|
121
|
+
- `logout()`
|
|
122
|
+
- `refresh()`
|
|
123
|
+
|
|
124
|
+
`useAuthCallbackRedirect()` returns:
|
|
125
|
+
|
|
126
|
+
- `status: "processing" | "failed"`
|
|
127
|
+
- `error: AuthSdkError | null`
|
|
128
|
+
|
|
129
|
+
## Next Adapter
|
|
130
|
+
|
|
131
|
+
Entry: `@ghostly-solutions/auth/next`
|
|
132
|
+
|
|
133
|
+
- `getServerSession(options)`
|
|
134
|
+
- `requireServerSession(options)`
|
|
135
|
+
- `ensureClientAuthenticated(client?)`
|
|
136
|
+
- `getNextServerSession(options?)`
|
|
137
|
+
- `requireNextServerSession(options?)`
|
|
138
|
+
- `createNextAuthRouteHandlers(options)`
|
|
139
|
+
|
|
140
|
+
`getServerSession` requires incoming request `Headers` to resolve origin and forward auth headers.
|
|
141
|
+
|
|
142
|
+
`createNextAuthRouteHandlers` supports:
|
|
143
|
+
|
|
144
|
+
- `mode: "mock"`: built-in cookie/session mock contract for `/v1/auth/*`
|
|
145
|
+
- `mode: "proxy"`: pass-through to external auth gateway (`proxy.baseUrl`)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Layer Map
|
|
4
|
+
|
|
5
|
+
- `src/constants`: fixed external contract values (routes, keys, status constants).
|
|
6
|
+
- `src/types`: public and internal type contracts.
|
|
7
|
+
- `src/errors`: canonical SDK error model.
|
|
8
|
+
- `src/core`: runtime-independent auth behavior and state management.
|
|
9
|
+
- `src/adapters/react`: React provider and hook adapter.
|
|
10
|
+
- `src/adapters/next`: Next.js server/client integration helpers.
|
|
11
|
+
|
|
12
|
+
## Core Runtime Model
|
|
13
|
+
|
|
14
|
+
### Session State
|
|
15
|
+
|
|
16
|
+
`SessionStore` keeps the resolved session state in memory.
|
|
17
|
+
|
|
18
|
+
- Initial state: pending.
|
|
19
|
+
- Resolved state: `GhostlySession | null`.
|
|
20
|
+
- Updates notify subscribers.
|
|
21
|
+
|
|
22
|
+
### HTTP Layer
|
|
23
|
+
|
|
24
|
+
`http-client.ts` standardizes API calls:
|
|
25
|
+
|
|
26
|
+
- `credentials: "include"`
|
|
27
|
+
- `cache: "no-store"`
|
|
28
|
+
- normalized error mapping to `AuthSdkError`
|
|
29
|
+
|
|
30
|
+
### Callback Processing
|
|
31
|
+
|
|
32
|
+
`auth-client.ts` callback sequence:
|
|
33
|
+
|
|
34
|
+
1. Parse `token` from URL.
|
|
35
|
+
2. Remove `token` from browser URL immediately.
|
|
36
|
+
3. Validate token via API.
|
|
37
|
+
4. Save session in memory.
|
|
38
|
+
5. Resolve `returnTo` and return result.
|
|
39
|
+
|
|
40
|
+
### Cross-tab Sync
|
|
41
|
+
|
|
42
|
+
`broadcast-sync.ts` sends `session-updated` events through `BroadcastChannel`.
|
|
43
|
+
|
|
44
|
+
- Login/session refresh and logout publish updates.
|
|
45
|
+
- Other tabs update in-memory state in reaction to events.
|
|
46
|
+
|
|
47
|
+
## Security Decisions
|
|
48
|
+
|
|
49
|
+
- No JS-level token persistence.
|
|
50
|
+
- Callback token is removed from URL before API validation response handling.
|
|
51
|
+
- `returnTo` is sanitized to internal absolute paths only.
|
|
52
|
+
- Unauthorized state is explicit and typed.
|
|
53
|
+
|
|
54
|
+
## Packaging
|
|
55
|
+
|
|
56
|
+
The package exposes three entrypoints:
|
|
57
|
+
|
|
58
|
+
- `@ghostly-solutions/auth`
|
|
59
|
+
- `@ghostly-solutions/auth/react`
|
|
60
|
+
- `@ghostly-solutions/auth/next`
|
|
61
|
+
|
|
62
|
+
Build output is generated to `dist/` via `tsup` with ESM and declarations.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Development and CI
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Node.js 22+
|
|
6
|
+
- npm 11+
|
|
7
|
+
|
|
8
|
+
Bun is optional for local convenience and is not part of CI requirements.
|
|
9
|
+
|
|
10
|
+
## Local Commands
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm run lint
|
|
14
|
+
npm run typecheck
|
|
15
|
+
npm run test
|
|
16
|
+
npm run build
|
|
17
|
+
npm run check
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Optional Bun Commands
|
|
21
|
+
|
|
22
|
+
Optional Bun aliases are defined in `bun.json`.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun run lint
|
|
28
|
+
bun run test
|
|
29
|
+
bun run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
These are convenience-only wrappers around the npm-first toolchain.
|
|
33
|
+
|
|
34
|
+
## CI (`.gitlab-ci.yml`)
|
|
35
|
+
|
|
36
|
+
Pipeline stages:
|
|
37
|
+
|
|
38
|
+
1. `validate`: lint and typecheck.
|
|
39
|
+
2. `test`: unit test run.
|
|
40
|
+
3. `build`: package build and `npm pack --dry-run` verification.
|
|
41
|
+
|
|
42
|
+
Key CI characteristics:
|
|
43
|
+
|
|
44
|
+
- Uses `node:22-bookworm`.
|
|
45
|
+
- Uses `npm ci` with lockfile-based cache.
|
|
46
|
+
- Produces `dist/` as build artifact.
|
|
47
|
+
|
|
48
|
+
## Release Checklist
|
|
49
|
+
|
|
50
|
+
1. `npm run check`
|
|
51
|
+
2. `npm run build`
|
|
52
|
+
3. `npm pack --dry-run`
|
|
53
|
+
4. Verify `dist/` contains `index`, `react`, and `next` outputs with `.d.ts` files.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Ghostly Auth SDK Documentation
|
|
2
|
+
|
|
3
|
+
## Documents
|
|
4
|
+
|
|
5
|
+
- [Overview](./overview.md)
|
|
6
|
+
- [API Reference](./api-reference.md)
|
|
7
|
+
- [Integration Guide](./integration-guide.md)
|
|
8
|
+
- [Architecture](./architecture.md)
|
|
9
|
+
- [Development and CI](./development-and-ci.md)
|
|
10
|
+
- [Interactive Demo](../demo/README.md)
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
This SDK provides a fixed authentication flow for Ghostly Solutions products:
|
|
15
|
+
|
|
16
|
+
1. Redirect user to Ghostly API login start endpoint.
|
|
17
|
+
2. Receive JWT on a dedicated callback page.
|
|
18
|
+
3. Validate JWT through Ghostly API.
|
|
19
|
+
4. Resolve and expose a typed session.
|
|
20
|
+
5. Keep tabs synchronized and support logout.
|
|
21
|
+
|
|
22
|
+
The SDK intentionally avoids runtime endpoint configuration and fallback logic.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Integration Guide
|
|
2
|
+
|
|
3
|
+
## 1. Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @ghostly-solutions/auth
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 2. Core Browser Integration
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createAuthClient } from "@ghostly-solutions/auth";
|
|
13
|
+
|
|
14
|
+
const authClient = createAuthClient();
|
|
15
|
+
|
|
16
|
+
export function onLoginClick(): void {
|
|
17
|
+
authClient.login();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function loadCurrentSession() {
|
|
21
|
+
return authClient.getSession();
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 3. Callback Route (`/auth/callback`)
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createAuthClient } from "@ghostly-solutions/auth";
|
|
29
|
+
|
|
30
|
+
const authClient = createAuthClient();
|
|
31
|
+
|
|
32
|
+
await authClient.completeCallbackRedirect();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or use the ready React helper:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { AuthCallbackHandler } from "@ghostly-solutions/auth/react";
|
|
39
|
+
|
|
40
|
+
export default function AuthCallbackPage() {
|
|
41
|
+
return (
|
|
42
|
+
<AuthCallbackHandler
|
|
43
|
+
processing={<div>Signing in...</div>}
|
|
44
|
+
renderError={() => <div>Sign in failed. Please retry.</div>}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Alternative when app controls redirect manually:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const result = await authClient.processCallback();
|
|
54
|
+
window.location.replace(result.redirectTo);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 4. React Integration
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { AuthProvider, AuthSessionGate } from "@ghostly-solutions/auth/react";
|
|
61
|
+
|
|
62
|
+
function SessionGate() {
|
|
63
|
+
return (
|
|
64
|
+
<AuthSessionGate
|
|
65
|
+
loading={<div>Loading...</div>}
|
|
66
|
+
unauthorized={({ login }) => <button onClick={() => login()}>Sign in</button>}
|
|
67
|
+
authorized={(session) => <div>{session.email}</div>}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function AppRoot() {
|
|
73
|
+
return (
|
|
74
|
+
<AuthProvider>
|
|
75
|
+
<SessionGate />
|
|
76
|
+
</AuthProvider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 5. Next.js App Router Integration
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { requireServerSession } from "@ghostly-solutions/auth/next";
|
|
85
|
+
|
|
86
|
+
export async function loadProtectedData(requestHeaders: Headers) {
|
|
87
|
+
const session = await requireServerSession({
|
|
88
|
+
headers: requestHeaders,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
actorId: session.id,
|
|
93
|
+
actorRole: session.role,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Client-side guard:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { ensureClientAuthenticated } from "@ghostly-solutions/auth/next";
|
|
102
|
+
|
|
103
|
+
await ensureClientAuthenticated();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
No-glue server helper:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { getNextServerSession } from "@ghostly-solutions/auth/next";
|
|
110
|
+
|
|
111
|
+
const session = await getNextServerSession();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Ready route handlers (mock/proxy):
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { createNextAuthRouteHandlers } from "@ghostly-solutions/auth/next";
|
|
118
|
+
|
|
119
|
+
const authHandlers = createNextAuthRouteHandlers({
|
|
120
|
+
mode: "mock",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
export const GET = authHandlers.keycloakLoginGet;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 6. Error Handling Pattern
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { AuthSdkError } from "@ghostly-solutions/auth";
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
await authClient.processCallback();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof AuthSdkError) {
|
|
135
|
+
if (error.code === "callback_invalid_token") {
|
|
136
|
+
// show explicit re-login state
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
```
|
package/docs/overview.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
`@ghostly-solutions/auth` is a focused authentication SDK for Ghostly Solutions products.
|
|
4
|
+
|
|
5
|
+
## Design Goals
|
|
6
|
+
|
|
7
|
+
- Keep authentication behavior deterministic and product-wide consistent.
|
|
8
|
+
- Expose strict TypeScript contracts for sessions and errors.
|
|
9
|
+
- Work in browser apps, React apps, and Next.js App Router environments.
|
|
10
|
+
- Keep API shape minimal: login, callback processing, session, logout, and guards.
|
|
11
|
+
|
|
12
|
+
## Fixed External Contract
|
|
13
|
+
|
|
14
|
+
The SDK uses fixed API routes and does not accept runtime endpoint configuration:
|
|
15
|
+
|
|
16
|
+
- `GET /v1/auth/keycloak/login`
|
|
17
|
+
- `POST /v1/auth/keycloak/validate`
|
|
18
|
+
- `GET /v1/auth/me`
|
|
19
|
+
- `POST /v1/auth/logout`
|
|
20
|
+
|
|
21
|
+
Callback route and token input are fixed:
|
|
22
|
+
|
|
23
|
+
- Callback path: `/auth/callback`
|
|
24
|
+
- Callback JWT query param: `token`
|
|
25
|
+
|
|
26
|
+
## What The SDK Handles
|
|
27
|
+
|
|
28
|
+
- Redirect start to login endpoint.
|
|
29
|
+
- Callback token extraction and immediate URL cleanup.
|
|
30
|
+
- JWT validation request through Ghostly API.
|
|
31
|
+
- Session fetch and lazy revalidation.
|
|
32
|
+
- In-memory session state and subscription API.
|
|
33
|
+
- Cross-tab synchronization via `BroadcastChannel`.
|
|
34
|
+
- React state adapter and Next.js guard helpers.
|
|
35
|
+
|
|
36
|
+
## What The SDK Does Not Do
|
|
37
|
+
|
|
38
|
+
- No fallback endpoint discovery.
|
|
39
|
+
- No dynamic base URL/runtime host config.
|
|
40
|
+
- No localStorage token persistence.
|
|
41
|
+
- No background polling refresh loop.
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ghostly-solutions/auth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Authentication SDK for Ghostly Solutions products using Keycloak redirect and Ghostly Auth API session validation.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"packageManager": "npm@11.6.3",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.0.0",
|
|
10
|
+
"npm": ">=11.0.0"
|
|
11
|
+
},
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"docs"
|
|
17
|
+
],
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./react": {
|
|
28
|
+
"types": "./dist/react.d.ts",
|
|
29
|
+
"import": "./dist/react.js",
|
|
30
|
+
"default": "./dist/react.js"
|
|
31
|
+
},
|
|
32
|
+
"./next": {
|
|
33
|
+
"types": "./dist/next.d.ts",
|
|
34
|
+
"import": "./dist/next.js",
|
|
35
|
+
"default": "./dist/next.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"build:watch": "tsup --watch",
|
|
44
|
+
"lint": "biome check src tests docs",
|
|
45
|
+
"lint:fix": "biome check src tests docs --write",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"check": "npm run lint && npm run typecheck && npm run test && npm run build",
|
|
51
|
+
"demo": "bun --hot demo/server.ts"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
55
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@biomejs/biome": "2.4.5",
|
|
59
|
+
"@types/node": "^22.15.34",
|
|
60
|
+
"@types/react": "^19.2.14",
|
|
61
|
+
"@types/react-dom": "^19.2.3",
|
|
62
|
+
"tsup": "^8.5.0",
|
|
63
|
+
"typescript": "^5.9.2",
|
|
64
|
+
"vitest": "^2.1.8"
|
|
65
|
+
}
|
|
66
|
+
}
|