@securitydept/basic-auth-context-client 0.3.0-beta.1 → 0.3.0-beta.10
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 +6 -0
- package/dist/index.d.mts +280 -11
- package/dist/index.mjs +778 -3
- package/dist/index.mjs.map +1 -0
- package/package.json +12 -11
- package/dist/client-DU98lpa_.d.mts +0 -91
- package/dist/client-D_0eAARH.mjs +0 -136
- package/dist/client-D_0eAARH.mjs.map +0 -1
- package/dist/server/index.d.mts +0 -73
- package/dist/server/index.mjs +0 -47
- package/dist/server/index.mjs.map +0 -1
- package/dist/types-2IoKrgnl.mjs +0 -20
- package/dist/types-2IoKrgnl.mjs.map +0 -1
- package/dist/web/index.d.mts +0 -45
- package/dist/web/index.mjs +0 -37
- package/dist/web/index.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["ClientError","ClientErrorKind","encodeUint8ArrayToBase64","createBasicAuthorizationHeaderValue","options","username","includes","kind","Configuration","code","message","source","TextEncoder","encode","password","ClientError","BasicAuthContextErrorCode","OperationFailed","InvalidConfig","ClientDisposed","RouterUnavailable","ProbePathRequired","ZoneNotFound","ZoneRequired","BasicAuthContextSource","BasicAuthContext","clientErrorFromBasicAuthError","error","options","fromUnknown","code","message","source","span","type","defineType","BasicAuthZoneConfigSchema","zonePrefix","BasicAuthContextClientConfigSchema","baseUrl","zones","array","atLeastLength","SecuritydeptInjectionToken","BASIC_AUTH_CONTEXT_CLIENT","BASIC_AUTH_CONTEXT_CLIENT_CONFIG","BasicAuthBoundaryKind","Authenticated","Challenge","Unauthorized","BasicAuthContextEventType","BoundaryRefreshStarted","BoundaryRefreshSucceeded","BoundaryRefreshFailed","LogoutStarted","LogoutSucceeded","LogoutFailed","LoginRedirectStarted","LoginRedirectSucceeded","LoginRedirectFailed","AuthGuardResultKind","Ok","Redirect","AuthGuardRedirectStatus","Found","SeeOther","TemporaryRedirect","ClientError","ClientErrorKind","createCancellationTokenSource","createLinkedCancellationToken","createOnceAsyncLockCallable","createSignal","defineInstrumentMethodDecorator","ENVIRONMENT_TOKEN","injectDisposableStackFrom","mapResource","ResourceSnapshotUpdateKind","ResourceStatus","RouterNavigationIntent","RouterNavigationMode","readonlySignal","reduceResourceSnapshot","resourceFromSnapshots","SecuritydeptDestroyRef","SpanSharedAttributeName","SYMBOL_DISPOSE","throwValidationClientError","UriReferenceString","validateTraitInput","withDisposableStack","commandResponseData","concatCommand","dispatchCommandLocallyToStream","RxEventSubject","RxStateSignal","filter","from","lastValueFrom","take","takeUntil","v7","uuidv7","BasicAuthContextErrorCode","BasicAuthContextSource","clientErrorFromBasicAuthError","BasicAuthContextClientConfigSchema","BASIC_AUTH_CONTEXT_CLIENT_CONFIG","AuthGuardRedirectStatus","AuthGuardResultKind","BasicAuthBoundaryKind","BasicAuthBoundaryKindValues","BasicAuthContextEventType","DEFAULT_LOGIN_SUBPATH","POST_AUTH_REDIRECT_PARAM","instrumentBasicAuthMethod","factoryArgs","operation","environment","span","name","config","tracing","prefix","target","traceAttributes","clientErrorFromUnknown","error","options","resolveZone","zonePrefix","replace","loginSub","loginSubpath","loginPath","readBasicAuthBoundaryKind","status","Authenticated","challengeHeader","Challenge","Unauthorized","BasicAuthContextClient","_start","refresh","_refresh","logout","_logout","loginWithRedirect","_loginWithRedirect","defaultOptions","_environment","_config","_span","_rootCancellation","_destroyed","fromInitialValue","destroyed$","pipe","value","_boundarySnapshotSignal","Idle","_operationSignals","startPending","refreshPending","logoutPending","loginRedirectPending","_eventSubject","_refreshCommandSubject","_refreshResponseSubject","_startOnce","cancellationToken","operationSpan","throwIfCancellationRequested","set","_dispatchRefresh","_resolveRefreshOptions","id","zones","boundarySnapshot","boundaryResource","isAuthenticated","operations","events","fromEnvironmentConfig","fromInjector","injector","client","get","onDestroy","dispose","bundledSchema","onInvalid","failure","code","InvalidConfig","source","BasicAuthContext","messagePrefix","resolvedConfig","baseUrl","map","probePath","fork","attributes","ClientName","ClientId","snapshot","authenticated","subscribe","cancel","kind","Cancelled","ClientDisposed","message","command","_executeRefresh","payload","autoStart","start","catch","token","disposableStack","use","_options","_emitEvent","type","LogoutStarted","_readCurrentSnapshot","Resolved","setTraceAttributes","localProjectionCleared","LogoutSucceeded","clientError","LogoutFailed","_operationSpan","router","Configuration","RouterUnavailable","zone","_resolveZoneForAction","postAuthRedirectUri","currentPath","LoginRedirectStarted","navigate","url","parse","loginUrl","intent","AuthRedirect","mode","External","LoginRedirectSucceeded","LoginRedirectFailed","zoneForPath","path","matchedZone","matches","startsWith","length","zoneForPrefix","find","isInZone","undefined","base","params","URLSearchParams","toString","loginUrlForZonePrefix","handleUnauthorized","responseStatus","Ok","Redirect","Found","location","complete","ProbePathRequired","action","ZoneNotFound","ZoneRequired","requestStream","responseStream","createCommandExtra","previous","loadingSnapshot","Load","BoundaryRefreshStarted","response","transport","execute","method","headers","accept","fromHttpResponse","body","_snapshotFromResponse","Resolve","boundaryKind","BoundaryRefreshSucceeded","Fail","BoundaryRefreshFailed","requestPath","Reloading","Error","input","next","at","time","now","INJECTOR_TOKEN","BasicAuthContextClient","BASIC_AUTH_CONTEXT_CLIENT","BASIC_AUTH_CONTEXT_CLIENT_CONFIG","provideBasicAuthContext","options","provide","useValue","config","useFactory","injector","fromInjector","deps"],"sources":["../src/authorization-header.ts","../src/error.ts","../src/schemas.ts","../src/tokens.ts","../src/types.ts","../src/client.ts","../src/providers.ts"],"sourcesContent":["import {\n\tClientError,\n\tClientErrorKind,\n\tencodeUint8ArrayToBase64,\n} from \"@securitydept/client\";\n\nexport interface CreateBasicAuthorizationHeaderValueOptions {\n\treadonly username: string;\n\treadonly password: string;\n}\n\nexport function createBasicAuthorizationHeaderValue(\n\toptions: CreateBasicAuthorizationHeaderValueOptions,\n): string {\n\tif (options.username.includes(\":\")) {\n\t\tthrow new ClientError({\n\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\tcode: \"basic_auth.authorization_header.username_contains_colon\",\n\t\t\tmessage: \"A Basic authentication username cannot contain a colon.\",\n\t\t\tsource: \"basic_auth\",\n\t\t});\n\t}\n\n\treturn `Basic ${encodeUint8ArrayToBase64(\n\t\tnew TextEncoder().encode(`${options.username}:${options.password}`),\n\t)}`;\n}\n","import { ClientError, type SpanTrait } from \"@securitydept/client\";\n\nexport const BasicAuthContextErrorCode = {\n\tOperationFailed: \"basic_auth.operation_failed\",\n\tInvalidConfig: \"basic_auth.invalid_config\",\n\tClientDisposed: \"basic_auth.client_disposed\",\n\tRouterUnavailable: \"basic_auth.router_unavailable\",\n\tProbePathRequired: \"basic_auth.probe_path_required\",\n\tZoneNotFound: \"basic_auth.zone_not_found\",\n\tZoneRequired: \"basic_auth.zone_required\",\n} as const;\n\nexport type BasicAuthContextErrorCode =\n\t(typeof BasicAuthContextErrorCode)[keyof typeof BasicAuthContextErrorCode];\n\nexport const BasicAuthContextSource = {\n\tBasicAuthContext: \"basic-auth-context\",\n} as const;\n\nexport type BasicAuthContextSource =\n\t(typeof BasicAuthContextSource)[keyof typeof BasicAuthContextSource];\n\nexport function clientErrorFromBasicAuthError(\n\terror: unknown,\n\toptions: { span: SpanTrait | undefined },\n): ClientError {\n\treturn ClientError.fromUnknown(error, {\n\t\tcode: BasicAuthContextErrorCode.OperationFailed,\n\t\tmessage: \"The Basic Auth operation failed unexpectedly\",\n\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\tspan: options.span,\n\t});\n}\n","import { type as defineType } from \"arktype\";\n\nexport const BasicAuthZoneConfigSchema = defineType({\n\tzonePrefix: \"string > 0\",\n\t\"loginSubpath?\": \"string\",\n});\n\nexport const BasicAuthContextClientConfigSchema = defineType({\n\t\"id?\": \"string > 0\",\n\tbaseUrl: \"string > 0\",\n\tzones: BasicAuthZoneConfigSchema.array().atLeastLength(1),\n\t\"probePath?\": \"string > 0\",\n\t\"autoStart?\": \"boolean\",\n\t\"tracing?\": {\n\t\t\"target?\": \"string > 0\",\n\t\t\"prefix?\": \"string > 0\",\n\t},\n});\n","import { SecuritydeptInjectionToken } from \"@securitydept/client\";\nimport { type BasicAuthContextClient } from \"./client\";\nimport { type BasicAuthContextClientConfig } from \"./types\";\n\nexport const BASIC_AUTH_CONTEXT_CLIENT =\n\tnew SecuritydeptInjectionToken<BasicAuthContextClient>(\n\t\t\"BASIC_AUTH_CONTEXT_CLIENT\",\n\t);\n\nexport const BASIC_AUTH_CONTEXT_CLIENT_CONFIG =\n\tnew SecuritydeptInjectionToken<BasicAuthContextClientConfig>(\n\t\t\"BASIC_AUTH_CONTEXT_CLIENT_CONFIG\",\n\t);\n","// --- Basic Auth Context Client types ---\n\nimport {\n\ttype CancellationTokenOptions,\n\ttype ClientError,\n\ttype ReadableSignalTrait,\n\ttype ResourceSnapshot,\n\ttype ResourceTrait,\n} from \"@securitydept/client\";\n\nexport interface BasicAuthZoneConfig {\n\t/** URL path prefix for this zone (e.g. \"/basic\"). */\n\tzonePrefix: string;\n\t/** Login subpath relative to zone prefix (default: \"/login\"). */\n\tloginSubpath?: string;\n}\n\n/** Configuration for the Basic Auth Context Client. */\nexport interface BasicAuthContextClientConfig {\n\tid?: string;\n\t/** Base URL of the SecurityDept server. */\n\tbaseUrl: string;\n\t/** One or more Basic Auth zones to manage. */\n\tzones: BasicAuthZoneConfig[];\n\t/** Protected endpoint used by start()/refresh() to observe the boundary. */\n\tprobePath?: string;\n\tautoStart?: boolean;\n\ttracing?: BasicAuthContextClientTracingOptions;\n}\n\nexport interface BasicAuthContextClientTracingOptions {\n\ttarget?: string;\n\tprefix?: string;\n}\n\nexport interface ResolvedBasicAuthContextClientConfig {\n\tid: string;\n\tbaseUrl: string;\n\tzones: readonly ResolvedBasicAuthZone[];\n\tprobePath?: string;\n\ttracing: Required<BasicAuthContextClientTracingOptions>;\n}\n\nexport const BasicAuthBoundaryKind = {\n\tAuthenticated: \"authenticated\",\n\tChallenge: \"challenge\",\n\tUnauthorized: \"unauthorized\",\n} as const;\n\nexport type BasicAuthBoundaryKind =\n\t(typeof BasicAuthBoundaryKind)[keyof typeof BasicAuthBoundaryKind];\n\nexport interface BasicAuthBoundaryObservation {\n\tstatus: number;\n\tchallengeHeader?: string | null;\n\trequestPath?: string;\n}\n\nexport interface BasicAuthBoundarySnapshot {\n\tauthenticated: boolean;\n\tboundaryKind: BasicAuthBoundaryKind;\n\tstatus: number;\n\tpath: string;\n\tchallengeHeader: string | null;\n\tzone?: ResolvedBasicAuthZone;\n}\n\nexport const BasicAuthContextEventType = {\n\tBoundaryRefreshStarted: \"basic_auth.boundary.refresh.started\",\n\tBoundaryRefreshSucceeded: \"basic_auth.boundary.refresh.succeeded\",\n\tBoundaryRefreshFailed: \"basic_auth.boundary.refresh.failed\",\n\t/** These logout events describe local boundary-projection clearing only. */\n\tLogoutStarted: \"basic_auth.logout.started\",\n\tLogoutSucceeded: \"basic_auth.logout.succeeded\",\n\tLogoutFailed: \"basic_auth.logout.failed\",\n\tLoginRedirectStarted: \"basic_auth.login.redirect.started\",\n\tLoginRedirectSucceeded: \"basic_auth.login.redirect.succeeded\",\n\tLoginRedirectFailed: \"basic_auth.login.redirect.failed\",\n} as const;\n\nexport type BasicAuthContextEventType =\n\t(typeof BasicAuthContextEventType)[keyof typeof BasicAuthContextEventType];\n\ninterface BasicAuthContextEventBase {\n\treadonly at: number;\n\treadonly client: {\n\t\treadonly id: string;\n\t};\n}\n\ntype BasicAuthContextEventData =\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.BoundaryRefreshStarted;\n\t\t\treadonly snapshot: BasicAuthBoundarySnapshot | null;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.BoundaryRefreshSucceeded;\n\t\t\treadonly snapshot: BasicAuthBoundarySnapshot;\n\t\t\treadonly zone?: ResolvedBasicAuthZone;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.BoundaryRefreshFailed;\n\t\t\treadonly snapshot: BasicAuthBoundarySnapshot | null;\n\t\t\treadonly error: ClientError;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.LogoutStarted;\n\t\t\treadonly snapshot: BasicAuthBoundarySnapshot | null;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.LogoutSucceeded;\n\t\t\treadonly snapshot: null;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.LogoutFailed;\n\t\t\treadonly snapshot: BasicAuthBoundarySnapshot | null;\n\t\t\treadonly error: ClientError;\n\t }\n\t| {\n\t\t\treadonly type:\n\t\t\t\t| typeof BasicAuthContextEventType.LoginRedirectStarted\n\t\t\t\t| typeof BasicAuthContextEventType.LoginRedirectSucceeded;\n\t\t\treadonly zone: ResolvedBasicAuthZone;\n\t }\n\t| {\n\t\t\treadonly type: typeof BasicAuthContextEventType.LoginRedirectFailed;\n\t\t\treadonly zone: ResolvedBasicAuthZone;\n\t\t\treadonly error: ClientError;\n\t };\n\nexport type BasicAuthContextEvent = BasicAuthContextEventBase &\n\tBasicAuthContextEventData;\n\nexport type BasicAuthContextEventInput = BasicAuthContextEventData;\n\nexport interface BasicAuthContextOperationSignals {\n\treadonly startPending: ReadableSignalTrait<boolean>;\n\treadonly refreshPending: ReadableSignalTrait<boolean>;\n\treadonly logoutPending: ReadableSignalTrait<boolean>;\n\treadonly loginRedirectPending: ReadableSignalTrait<boolean>;\n}\n\nexport interface BasicAuthRefreshOptions extends CancellationTokenOptions {\n\tpath?: string;\n}\n\nexport type BasicAuthZoneSelectionOptions =\n\t| {\n\t\t\tcurrentPath: string;\n\t\t\tzonePrefix?: never;\n\t }\n\t| {\n\t\t\tzonePrefix: string;\n\t\t\tcurrentPath?: never;\n\t };\n\n/** Options for clearing the client's in-memory boundary projection. */\nexport interface BasicAuthLogoutOptions extends CancellationTokenOptions {}\n\nexport type BasicAuthLoginWithRedirectOptions = BasicAuthZoneSelectionOptions &\n\tCancellationTokenOptions & {\n\t\tpostAuthRedirectUri?: string;\n\t};\n\nexport const AuthGuardResultKind = {\n\tOk: \"ok\",\n\tRedirect: \"redirect\",\n} as const;\n\nexport type AuthGuardResultKind =\n\t(typeof AuthGuardResultKind)[keyof typeof AuthGuardResultKind];\n\nexport const AuthGuardRedirectStatus = {\n\tFound: 302,\n\tSeeOther: 303,\n\tTemporaryRedirect: 307,\n} as const;\n\nexport type AuthGuardRedirectStatus =\n\t(typeof AuthGuardRedirectStatus)[keyof typeof AuthGuardRedirectStatus];\n\n/** Redirect instruction — framework-neutral result. */\nexport type AuthGuardResult<T> =\n\t| { kind: typeof AuthGuardResultKind.Ok; value: T }\n\t| {\n\t\t\tkind: typeof AuthGuardResultKind.Redirect;\n\t\t\tstatus: AuthGuardRedirectStatus;\n\t\t\tlocation: string;\n\t };\n\n/** Resolved zone with computed paths. */\nexport interface ResolvedBasicAuthZone {\n\tzonePrefix: string;\n\tloginPath: string;\n}\n\nexport interface BasicAuthContextClientStateSignals {\n\treadonly boundarySnapshot: ReadableSignalTrait<\n\t\tResourceSnapshot<BasicAuthBoundarySnapshot | null>\n\t>;\n\treadonly boundaryResource: ResourceTrait<BasicAuthBoundarySnapshot | null>;\n\treadonly isAuthenticated: ResourceTrait<boolean>;\n}\n","import {\n\ttype CancellationTokenSourceTrait,\n\ttype CancellationTokenTrait,\n\tClientError,\n\tClientErrorKind,\n\tcreateCancellationTokenSource,\n\tcreateLinkedCancellationToken,\n\tcreateOnceAsyncLockCallable,\n\tcreateSignal,\n\ttype DisposableTrait,\n\tdefineInstrumentMethodDecorator,\n\tENVIRONMENT_TOKEN,\n\ttype EventStreamTrait,\n\ttype FoundationEnvironment,\n\tinjectDisposableStackFrom,\n\tmapResource,\n\ttype OperationSpanTrait,\n\ttype ReadableSignalTrait,\n\ttype ResourceSnapshot,\n\tResourceSnapshotUpdateKind,\n\tResourceStatus,\n\ttype ResourceTrait,\n\tRouterNavigationIntent,\n\tRouterNavigationMode,\n\treadonlySignal,\n\treduceResourceSnapshot,\n\tresourceFromSnapshots,\n\tSecuritydeptDestroyRef,\n\ttype SecuritydeptInjectorTrait,\n\tSpanSharedAttributeName,\n\ttype SpanTrait,\n\tSYMBOL_DISPOSE,\n\tthrowValidationClientError,\n\tUriReferenceString,\n\tvalidateTraitInput,\n\ttype WritableSignalTrait,\n\twithDisposableStack,\n} from \"@securitydept/client\";\nimport {\n\ttype Command,\n\ttype CommandResponse,\n\tcommandResponseData,\n\tconcatCommand,\n\tdispatchCommandLocallyToStream,\n\tRxEventSubject,\n\tRxStateSignal,\n} from \"@securitydept/client/rx\";\nimport { filter, from, lastValueFrom, take, takeUntil } from \"rxjs\";\nimport { v7 as uuidv7 } from \"uuid\";\nimport {\n\tBasicAuthContextErrorCode,\n\tBasicAuthContextSource,\n\tclientErrorFromBasicAuthError,\n} from \"./error\";\nimport { BasicAuthContextClientConfigSchema } from \"./schemas\";\nimport { BASIC_AUTH_CONTEXT_CLIENT_CONFIG } from \"./tokens\";\nimport {\n\tAuthGuardRedirectStatus,\n\ttype AuthGuardResult,\n\tAuthGuardResultKind,\n\ttype BasicAuthBoundaryKind,\n\tBasicAuthBoundaryKind as BasicAuthBoundaryKindValues,\n\ttype BasicAuthBoundaryObservation,\n\ttype BasicAuthBoundarySnapshot,\n\ttype BasicAuthContextClientConfig,\n\ttype BasicAuthContextEvent,\n\ttype BasicAuthContextEventInput,\n\tBasicAuthContextEventType,\n\ttype BasicAuthContextOperationSignals,\n\ttype BasicAuthLoginWithRedirectOptions,\n\ttype BasicAuthLogoutOptions,\n\ttype BasicAuthRefreshOptions,\n\ttype BasicAuthZoneConfig,\n\ttype BasicAuthZoneSelectionOptions,\n\ttype ResolvedBasicAuthContextClientConfig,\n\ttype ResolvedBasicAuthZone,\n} from \"./types\";\n\nconst DEFAULT_LOGIN_SUBPATH = \"/login\";\nconst POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\n\ninterface BasicAuthCommandExtra {\n\tcancellationToken: CancellationTokenTrait;\n\toperationSpan?: OperationSpanTrait;\n}\n\ntype ResolvedBasicAuthRefreshOptions = { path: string };\n\ntype BasicAuthRefreshCommand = Command<\n\tResolvedBasicAuthRefreshOptions,\n\tBasicAuthCommandExtra\n>;\n\nconst instrumentBasicAuthMethod = defineInstrumentMethodDecorator<\n\t[operation: string],\n\tBasicAuthContextClient\n>(\n\t({ factoryArgs: [operation] }) =>\n\t\tfunction (this: BasicAuthContextClient) {\n\t\t\treturn {\n\t\t\t\tenvironment: this.environment,\n\t\t\t\tspan: this.span,\n\t\t\t\tname: `${this.config.tracing.prefix}.${operation}`,\n\t\t\t\ttarget: this.config.tracing.target,\n\t\t\t\ttraceAttributes: { operation },\n\t\t\t\tclientErrorFromUnknown: (error, options) =>\n\t\t\t\t\tclientErrorFromBasicAuthError(error, options),\n\t\t\t};\n\t\t},\n);\n\nfunction resolveZone(config: BasicAuthZoneConfig): ResolvedBasicAuthZone {\n\tconst prefix = config.zonePrefix.replace(/\\/+$/, \"\");\n\tconst loginSub = config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH;\n\n\treturn {\n\t\tzonePrefix: prefix,\n\t\tloginPath: prefix + loginSub,\n\t};\n}\n\nexport function readBasicAuthBoundaryKind(\n\toptions: BasicAuthBoundaryObservation,\n): BasicAuthBoundaryKind {\n\tif (options.status < 400) {\n\t\treturn BasicAuthBoundaryKindValues.Authenticated;\n\t}\n\n\tif (options.challengeHeader) {\n\t\treturn BasicAuthBoundaryKindValues.Challenge;\n\t}\n\n\treturn BasicAuthBoundaryKindValues.Unauthorized;\n}\n\n/**\n * Basic Auth Context Client.\n *\n * Owns zone-aware Basic Auth boundary observations and login navigation.\n * It does not manage browser credentials or principal data.\n */\nexport class BasicAuthContextClient implements DisposableTrait {\n\tstatic defaultOptions = {\n\t\tloginSubpath: DEFAULT_LOGIN_SUBPATH,\n\t\ttracing: {\n\t\t\ttarget: \"basic-auth-context-client\",\n\t\t\tprefix: \"basic_auth_context\",\n\t\t},\n\t} as const;\n\n\tprivate readonly _environment: FoundationEnvironment;\n\tprivate readonly _config: ResolvedBasicAuthContextClientConfig;\n\tprivate readonly _span: SpanTrait;\n\tprivate readonly _rootCancellation: CancellationTokenSourceTrait =\n\t\tcreateCancellationTokenSource();\n\tprivate readonly _destroyed = RxStateSignal.fromInitialValue(false);\n\tprivate readonly destroyed$ = from(this._destroyed).pipe(\n\t\tfilter((value): value is true => value),\n\t\ttake(1),\n\t);\n\tprivate readonly _boundarySnapshotSignal = RxStateSignal.fromInitialValue<\n\t\tResourceSnapshot<BasicAuthBoundarySnapshot | null>\n\t>({ status: ResourceStatus.Idle });\n\tprivate readonly _operationSignals: {\n\t\treadonly startPending: WritableSignalTrait<boolean>;\n\t\treadonly refreshPending: WritableSignalTrait<boolean>;\n\t\treadonly logoutPending: WritableSignalTrait<boolean>;\n\t\treadonly loginRedirectPending: WritableSignalTrait<boolean>;\n\t} = {\n\t\tstartPending: createSignal(false),\n\t\trefreshPending: createSignal(false),\n\t\tlogoutPending: createSignal(false),\n\t\tloginRedirectPending: createSignal(false),\n\t};\n\tprivate readonly _eventSubject = new RxEventSubject<BasicAuthContextEvent>();\n\tprivate readonly _refreshCommandSubject =\n\t\tnew RxEventSubject<BasicAuthRefreshCommand>();\n\tprivate readonly _refreshResponseSubject = new RxEventSubject<\n\t\tCommandResponse<BasicAuthRefreshCommand, BasicAuthBoundarySnapshot>\n\t>();\n\tprivate readonly _startOnce = createOnceAsyncLockCallable(\n\t\tasync (\n\t\t\tcancellationToken: CancellationTokenTrait,\n\t\t\toperationSpan?: OperationSpanTrait,\n\t\t) => {\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._operationSignals.startPending.set(true);\n\t\t\ttry {\n\t\t\t\treturn await this._dispatchRefresh(\n\t\t\t\t\tthis._resolveRefreshOptions({}),\n\t\t\t\t\tcancellationToken,\n\t\t\t\t\toperationSpan,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis._operationSignals.startPending.set(false);\n\t\t\t}\n\t\t},\n\t);\n\n\treadonly id: string;\n\treadonly zones: readonly ResolvedBasicAuthZone[];\n\treadonly boundarySnapshot: ReadableSignalTrait<\n\t\tResourceSnapshot<BasicAuthBoundarySnapshot | null>\n\t>;\n\treadonly boundaryResource: ResourceTrait<BasicAuthBoundarySnapshot | null>;\n\treadonly isAuthenticated: ResourceTrait<boolean>;\n\treadonly operations: BasicAuthContextOperationSignals;\n\treadonly events: EventStreamTrait<BasicAuthContextEvent>;\n\n\tprotected get environment(): FoundationEnvironment {\n\t\treturn this._environment;\n\t}\n\n\tprotected get span(): SpanTrait {\n\t\treturn this._span;\n\t}\n\n\tprotected get config(): ResolvedBasicAuthContextClientConfig {\n\t\treturn this._config;\n\t}\n\n\tstatic fromEnvironmentConfig(options: {\n\t\treadonly config: BasicAuthContextClientConfig;\n\t\treadonly environment: FoundationEnvironment;\n\t}): BasicAuthContextClient {\n\t\treturn new BasicAuthContextClient(options.config, options.environment);\n\t}\n\n\tstatic fromInjector(\n\t\tinjector: SecuritydeptInjectorTrait,\n\t): BasicAuthContextClient {\n\t\tconst client = BasicAuthContextClient.fromEnvironmentConfig({\n\t\t\tconfig: injector.get(BASIC_AUTH_CONTEXT_CLIENT_CONFIG),\n\t\t\tenvironment: injector.get(ENVIRONMENT_TOKEN),\n\t\t});\n\t\tinjector\n\t\t\t.get(SecuritydeptDestroyRef, null)\n\t\t\t?.onDestroy(() => client.dispose());\n\t\treturn client;\n\t}\n\n\tprotected constructor(\n\t\tconfig: BasicAuthContextClientConfig,\n\t\tenvironment: FoundationEnvironment,\n\t) {\n\t\tvalidateTraitInput({\n\t\t\tvalue: config,\n\t\t\tbundledSchema: BasicAuthContextClientConfigSchema,\n\t\t\tonInvalid: (failure) =>\n\t\t\t\tthrowValidationClientError({\n\t\t\t\t\tcode: BasicAuthContextErrorCode.InvalidConfig,\n\t\t\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t\t\t\tmessagePrefix: \"BasicAuthContextClient could not validate config\",\n\t\t\t\t\tfailure,\n\t\t\t\t}),\n\t\t});\n\t\tconst resolvedConfig: ResolvedBasicAuthContextClientConfig = {\n\t\t\tid: config.id ?? uuidv7(),\n\t\t\tbaseUrl: (config.baseUrl ?? \"\").replace(/\\/+$/, \"\"),\n\t\t\tzones: (config.zones ?? []).map(resolveZone),\n\t\t\tprobePath: config.probePath,\n\t\t\ttracing: {\n\t\t\t\ttarget:\n\t\t\t\t\tconfig.tracing?.target ??\n\t\t\t\t\tBasicAuthContextClient.defaultOptions.tracing.target,\n\t\t\t\tprefix:\n\t\t\t\t\tconfig.tracing?.prefix ??\n\t\t\t\t\tBasicAuthContextClient.defaultOptions.tracing.prefix,\n\t\t\t},\n\t\t};\n\n\t\tthis._environment = environment;\n\t\tthis._config = resolvedConfig;\n\t\tthis.id = resolvedConfig.id;\n\t\tthis.zones = resolvedConfig.zones;\n\t\tthis._span = environment.span.fork({\n\t\t\tattributes: {\n\t\t\t\t[SpanSharedAttributeName.ClientName]: this.constructor.name,\n\t\t\t\t[SpanSharedAttributeName.ClientId]: this.id,\n\t\t\t},\n\t\t});\n\t\tthis.boundarySnapshot = readonlySignal(this._boundarySnapshotSignal);\n\t\tthis.boundaryResource = resourceFromSnapshots(() =>\n\t\t\tthis._boundarySnapshotSignal.get(),\n\t\t);\n\t\tthis.isAuthenticated = mapResource(\n\t\t\tthis.boundaryResource,\n\t\t\t(snapshot) => snapshot?.authenticated === true,\n\t\t);\n\t\tthis.operations = {\n\t\t\tstartPending: readonlySignal(this._operationSignals.startPending),\n\t\t\trefreshPending: readonlySignal(this._operationSignals.refreshPending),\n\t\t\tlogoutPending: readonlySignal(this._operationSignals.logoutPending),\n\t\t\tloginRedirectPending: readonlySignal(\n\t\t\t\tthis._operationSignals.loginRedirectPending,\n\t\t\t),\n\t\t};\n\t\tthis.events = this._eventSubject;\n\t\tthis.destroyed$.subscribe(() => {\n\t\t\tthis._rootCancellation.cancel(\n\t\t\t\tnew ClientError({\n\t\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\t\tcode: BasicAuthContextErrorCode.ClientDisposed,\n\t\t\t\t\tmessage: \"BasicAuthContextClient has been disposed.\",\n\t\t\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.isAuthenticated.dispose();\n\t\t\tthis.boundaryResource.dispose();\n\t\t});\n\n\t\tfrom(this._refreshCommandSubject)\n\t\t\t.pipe(\n\t\t\t\ttakeUntil(this.destroyed$),\n\t\t\t\tconcatCommand((command) =>\n\t\t\t\t\tthis._executeRefresh(\n\t\t\t\t\t\tcommand.payload,\n\t\t\t\t\t\tcommand.cancellationToken,\n\t\t\t\t\t\tcommand.operationSpan,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.subscribe(this._refreshResponseSubject);\n\n\t\tif (config.autoStart === true) {\n\t\t\tthis.start().catch(() => {\n\t\t\t\t// Startup failure is reflected by boundarySnapshot.\n\t\t\t});\n\t\t}\n\t}\n\n\tasync start(): Promise<BasicAuthBoundarySnapshot> {\n\t\tconst cancellationToken = this._rootCancellation.token;\n\t\treturn await this._start(cancellationToken);\n\t}\n\n\t@instrumentBasicAuthMethod(\"start\")\n\tprivate async _start(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<BasicAuthBoundarySnapshot> {\n\t\treturn await this._startOnce(cancellationToken, operationSpan);\n\t}\n\n\t@withDisposableStack(0, true)\n\tasync refresh(\n\t\toptions: BasicAuthRefreshOptions = {},\n\t): Promise<BasicAuthBoundarySnapshot> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\treturn await this._refresh(options, cancellationToken);\n\t}\n\n\t@instrumentBasicAuthMethod(\"refresh\")\n\tprivate async _refresh(\n\t\toptions: BasicAuthRefreshOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<BasicAuthBoundarySnapshot> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\treturn await this._dispatchRefresh(\n\t\t\tthis._resolveRefreshOptions(options),\n\t\t\tcancellationToken,\n\t\t\toperationSpan,\n\t\t);\n\t}\n\n\t/**\n\t * Clears only this client's in-memory Basic Auth boundary projection.\n\t *\n\t * Browsers own HTTP Basic credentials. This method cannot revoke or clear\n\t * those credentials, and a later probe may observe an authenticated state\n\t * again when the browser continues sending them.\n\t */\n\t@withDisposableStack(0, true)\n\tasync logout(options: BasicAuthLogoutOptions = {}): Promise<void> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\treturn await this._logout(options, cancellationToken);\n\t}\n\n\t@instrumentBasicAuthMethod(\"logout\")\n\tprivate async _logout(\n\t\t_options: BasicAuthLogoutOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tthis._operationSignals.logoutPending.set(true);\n\t\tthis._emitEvent({\n\t\t\ttype: BasicAuthContextEventType.LogoutStarted,\n\t\t\tsnapshot: this._readCurrentSnapshot(),\n\t\t});\n\t\ttry {\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._boundarySnapshotSignal.set({\n\t\t\t\tstatus: ResourceStatus.Resolved,\n\t\t\t\tvalue: null,\n\t\t\t});\n\t\t\toperationSpan?.setTraceAttributes({ localProjectionCleared: true });\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.LogoutSucceeded,\n\t\t\t\tsnapshot: null,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst clientError = clientErrorFromBasicAuthError(error, {\n\t\t\t\tspan: operationSpan?.span,\n\t\t\t});\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.LogoutFailed,\n\t\t\t\tsnapshot: this._readCurrentSnapshot(),\n\t\t\t\terror: clientError,\n\t\t\t});\n\t\t\tthrow clientError;\n\t\t} finally {\n\t\t\tthis._operationSignals.logoutPending.set(false);\n\t\t}\n\t}\n\n\t@withDisposableStack(0, true)\n\tasync loginWithRedirect(\n\t\toptions: BasicAuthLoginWithRedirectOptions,\n\t): Promise<void> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\tawait this._loginWithRedirect(options, cancellationToken);\n\t}\n\n\t@instrumentBasicAuthMethod(\"login.redirect\")\n\tprivate async _loginWithRedirect(\n\t\toptions: BasicAuthLoginWithRedirectOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\t_operationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tconst router = this._environment.router;\n\t\tif (!router) {\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\t\tcode: BasicAuthContextErrorCode.RouterUnavailable,\n\t\t\t\tmessage: \"loginWithRedirect() requires environment.router.\",\n\t\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t\t});\n\t\t}\n\t\tconst zone = this._resolveZoneForAction(options, \"login redirect\");\n\t\tconst postAuthRedirectUri =\n\t\t\toptions.postAuthRedirectUri ?? options.currentPath;\n\n\t\tthis._operationSignals.loginRedirectPending.set(true);\n\t\tthis._emitEvent({\n\t\t\ttype: BasicAuthContextEventType.LoginRedirectStarted,\n\t\t\tzone,\n\t\t});\n\t\ttry {\n\t\t\tawait router.navigate({\n\t\t\t\turl: UriReferenceString.parse(this.loginUrl(zone, postAuthRedirectUri)),\n\t\t\t\tintent: RouterNavigationIntent.AuthRedirect,\n\t\t\t\tmode: RouterNavigationMode.External,\n\t\t\t});\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.LoginRedirectSucceeded,\n\t\t\t\tzone,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst clientError = clientErrorFromBasicAuthError(error, {\n\t\t\t\tspan: _operationSpan?.span,\n\t\t\t});\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.LoginRedirectFailed,\n\t\t\t\tzone,\n\t\t\t\terror: clientError,\n\t\t\t});\n\t\t\tthrow clientError;\n\t\t} finally {\n\t\t\tthis._operationSignals.loginRedirectPending.set(false);\n\t\t}\n\t}\n\n\t/** Find the zone that contains the given path. */\n\tzoneForPath(path: string): ResolvedBasicAuthZone | undefined {\n\t\tlet matchedZone: ResolvedBasicAuthZone | undefined;\n\n\t\tfor (const zone of this.zones) {\n\t\t\tconst matches =\n\t\t\t\tpath === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`);\n\t\t\tif (!matches) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!matchedZone ||\n\t\t\t\tzone.zonePrefix.length > matchedZone.zonePrefix.length\n\t\t\t) {\n\t\t\t\tmatchedZone = zone;\n\t\t\t}\n\t\t}\n\n\t\treturn matchedZone;\n\t}\n\n\tzoneForPrefix(zonePrefix: string): ResolvedBasicAuthZone | undefined {\n\t\treturn this.zones.find((zone) => zone.zonePrefix === zonePrefix);\n\t}\n\n\t/** Check whether a path falls inside any configured zone. */\n\tisInZone(path: string): boolean {\n\t\treturn this.zoneForPath(path) !== undefined;\n\t}\n\n\t/** Build the full login URL for a zone, optionally with a post-auth redirect. */\n\tloginUrl(zone: ResolvedBasicAuthZone, postAuthRedirectUri?: string): string {\n\t\tconst base = this._config.baseUrl + zone.loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[POST_AUTH_REDIRECT_PARAM]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\tloginUrlForZonePrefix(\n\t\tzonePrefix: string,\n\t\tpostAuthRedirectUri?: string,\n\t): string | null {\n\t\tconst zone = this.zoneForPrefix(zonePrefix);\n\t\treturn zone ? this.loginUrl(zone, postAuthRedirectUri) : null;\n\t}\n\n\t/**\n\t * Handle a 401 response: if the current path is inside a zone,\n\t * return a redirect instruction to the zone's login URL.\n\t */\n\thandleUnauthorized(\n\t\tcurrentPath: string,\n\t\tresponseStatus: number,\n\t): AuthGuardResult<null> {\n\t\tif (responseStatus !== 401) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\tconst zone = this.zoneForPath(currentPath);\n\t\tif (!zone) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: AuthGuardResultKind.Redirect,\n\t\t\tstatus: AuthGuardRedirectStatus.Found,\n\t\t\tlocation: this.loginUrl(zone, currentPath),\n\t\t};\n\t}\n\n\tdispose(): void {\n\t\tthis._destroyed.set(true);\n\t\tthis._eventSubject.complete();\n\t}\n\n\t[SYMBOL_DISPOSE](): void {\n\t\tthis.dispose();\n\t}\n\n\tprivate _resolveRefreshOptions(\n\t\toptions: BasicAuthRefreshOptions,\n\t): ResolvedBasicAuthRefreshOptions {\n\t\tconst path = options.path ?? this._config.probePath;\n\t\tif (!path) {\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\t\tcode: BasicAuthContextErrorCode.ProbePathRequired,\n\t\t\t\tmessage:\n\t\t\t\t\t\"BasicAuthContextClient.refresh() requires options.path or config.probePath.\",\n\t\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t\t});\n\t\t}\n\t\treturn { path };\n\t}\n\n\tprivate _resolveZoneForAction(\n\t\toptions: BasicAuthZoneSelectionOptions,\n\t\taction: string,\n\t): ResolvedBasicAuthZone {\n\t\tif (options.zonePrefix) {\n\t\t\tconst zone = this.zoneForPrefix(options.zonePrefix);\n\t\t\tif (zone) {\n\t\t\t\treturn zone;\n\t\t\t}\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\t\tcode: BasicAuthContextErrorCode.ZoneNotFound,\n\t\t\t\tmessage: `BasicAuthContextClient ${action} could not find zone \"${options.zonePrefix}\".`,\n\t\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t\t});\n\t\t}\n\n\t\tconst zone =\n\t\t\ttypeof options.currentPath === \"string\"\n\t\t\t\t? this.zoneForPath(options.currentPath)\n\t\t\t\t: undefined;\n\t\tif (zone) {\n\t\t\treturn zone;\n\t\t}\n\n\t\tthrow new ClientError({\n\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\tcode: BasicAuthContextErrorCode.ZoneRequired,\n\t\t\tmessage: `BasicAuthContextClient ${action} requires options.currentPath inside a configured zone or options.zonePrefix.`,\n\t\t\tsource: BasicAuthContextSource.BasicAuthContext,\n\t\t});\n\t}\n\n\tprivate async _dispatchRefresh(\n\t\tpayload: ResolvedBasicAuthRefreshOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<BasicAuthBoundarySnapshot> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tconst snapshot = await lastValueFrom(\n\t\t\tdispatchCommandLocallyToStream<\n\t\t\t\tResolvedBasicAuthRefreshOptions,\n\t\t\t\tBasicAuthCommandExtra,\n\t\t\t\tBasicAuthBoundarySnapshot,\n\t\t\t\tBasicAuthRefreshCommand\n\t\t\t>({\n\t\t\t\tpayload,\n\t\t\t\trequestStream: this._refreshCommandSubject,\n\t\t\t\tresponseStream: this._refreshResponseSubject,\n\t\t\t\tcreateCommandExtra: () => ({ cancellationToken, operationSpan }),\n\t\t\t}).pipe(commandResponseData()),\n\t\t);\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\treturn snapshot;\n\t}\n\n\tprivate async _executeRefresh(\n\t\toptions: ResolvedBasicAuthRefreshOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<BasicAuthBoundarySnapshot> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tthis._operationSignals.refreshPending.set(true);\n\t\tconst previous = this._boundarySnapshotSignal.get();\n\t\tconst loadingSnapshot = reduceResourceSnapshot(previous, {\n\t\t\tkind: ResourceSnapshotUpdateKind.Load,\n\t\t});\n\t\tthis._boundarySnapshotSignal.set(loadingSnapshot);\n\t\tthis._emitEvent({\n\t\t\ttype: BasicAuthContextEventType.BoundaryRefreshStarted,\n\t\t\tsnapshot: this._readCurrentSnapshot(),\n\t\t});\n\t\ttry {\n\t\t\tconst response = await this._environment.transport.execute({\n\t\t\t\turl: this._config.baseUrl + options.path,\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: { accept: \"application/json\" },\n\t\t\t\tcancellationToken,\n\t\t\t});\n\t\t\tif (\n\t\t\t\tresponse.status >= 400 &&\n\t\t\t\tresponse.status !== 401 &&\n\t\t\t\tresponse.status !== 403\n\t\t\t) {\n\t\t\t\tthrow ClientError.fromHttpResponse({\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tbody: response.body,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tconst snapshot = this._snapshotFromResponse(options.path, response);\n\t\t\tthis._boundarySnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Resolve,\n\t\t\t\t\tvalue: snapshot,\n\t\t\t\t}),\n\t\t\t);\n\t\t\toperationSpan?.setTraceAttributes({\n\t\t\t\tauthenticated: snapshot.authenticated,\n\t\t\t\tboundaryKind: snapshot.boundaryKind,\n\t\t\t});\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.BoundaryRefreshSucceeded,\n\t\t\t\tsnapshot,\n\t\t\t\tzone: snapshot.zone,\n\t\t\t});\n\t\t\treturn snapshot;\n\t\t} catch (error) {\n\t\t\tconst clientError = clientErrorFromBasicAuthError(error, {\n\t\t\t\tspan: operationSpan?.span,\n\t\t\t});\n\t\t\tthis._boundarySnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Fail,\n\t\t\t\t\terror: clientError,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis._emitEvent({\n\t\t\t\ttype: BasicAuthContextEventType.BoundaryRefreshFailed,\n\t\t\t\tsnapshot: this._readCurrentSnapshot(),\n\t\t\t\terror: clientError,\n\t\t\t});\n\t\t\tthrow clientError;\n\t\t} finally {\n\t\t\tthis._operationSignals.refreshPending.set(false);\n\t\t}\n\t}\n\n\tprivate _snapshotFromResponse(\n\t\tpath: string,\n\t\tresponse: { status: number; headers: Record<string, string> },\n\t): BasicAuthBoundarySnapshot {\n\t\tconst challengeHeader =\n\t\t\tresponse.headers[\"WWW-Authenticate\"] ??\n\t\t\tresponse.headers[\"www-authenticate\"] ??\n\t\t\tnull;\n\t\tconst zone = this.zoneForPath(path);\n\t\tconst boundaryKind = readBasicAuthBoundaryKind({\n\t\t\tstatus: response.status,\n\t\t\tchallengeHeader,\n\t\t\trequestPath: path,\n\t\t});\n\t\treturn {\n\t\t\tauthenticated: boundaryKind === BasicAuthBoundaryKindValues.Authenticated,\n\t\t\tboundaryKind,\n\t\t\tstatus: response.status,\n\t\t\tpath,\n\t\t\tchallengeHeader,\n\t\t\tzone,\n\t\t};\n\t}\n\n\tprivate _readCurrentSnapshot(): BasicAuthBoundarySnapshot | null {\n\t\tconst snapshot = this._boundarySnapshotSignal.get();\n\t\treturn snapshot.status === ResourceStatus.Reloading ||\n\t\t\tsnapshot.status === ResourceStatus.Resolved ||\n\t\t\tsnapshot.status === ResourceStatus.Error\n\t\t\t? snapshot.value\n\t\t\t: null;\n\t}\n\n\tprivate _emitEvent(input: BasicAuthContextEventInput): void {\n\t\tthis._eventSubject.next({\n\t\t\t...input,\n\t\t\tat: this._environment.time.now(),\n\t\t\tclient: {\n\t\t\t\tid: this.id,\n\t\t\t},\n\t\t});\n\t}\n}\n","import {\n\tINJECTOR_TOKEN,\n\ttype SecuritydeptInjectorTrait,\n\ttype SecuritydeptProvider,\n} from \"@securitydept/client\";\nimport { BasicAuthContextClient } from \"./client\";\nimport {\n\tBASIC_AUTH_CONTEXT_CLIENT,\n\tBASIC_AUTH_CONTEXT_CLIENT_CONFIG,\n} from \"./tokens\";\nimport { type BasicAuthContextClientConfig } from \"./types\";\n\nexport interface ProvideBasicAuthContextOptions {\n\treadonly config: BasicAuthContextClientConfig;\n}\n\nexport function provideBasicAuthContext(\n\toptions: ProvideBasicAuthContextOptions,\n): readonly SecuritydeptProvider[] {\n\treturn [\n\t\t{\n\t\t\tprovide: BASIC_AUTH_CONTEXT_CLIENT_CONFIG,\n\t\t\tuseValue: options.config,\n\t\t},\n\t\t{\n\t\t\tprovide: BASIC_AUTH_CONTEXT_CLIENT,\n\t\t\tuseFactory: (injector: SecuritydeptInjectorTrait) =>\n\t\t\t\tBasicAuthContextClient.fromInjector(injector),\n\t\t\tdeps: [INJECTOR_TOKEN],\n\t\t},\n\t];\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@securitydept/basic-auth-context-client",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.10",
|
|
4
4
|
"description": "Basic Auth Context Client of the SecurityDept TypeScript SDK, a layered authentication and authorization toolkit built as reusable Rust crates, a TypeScript client SDK workspace, and reference server/web apps.",
|
|
5
5
|
"author": "Zhou Yeheng <root@aitiotekt.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,24 +31,25 @@
|
|
|
31
31
|
".": {
|
|
32
32
|
"types": "./dist/index.d.mts",
|
|
33
33
|
"import": "./dist/index.mjs"
|
|
34
|
-
},
|
|
35
|
-
"./web": {
|
|
36
|
-
"types": "./dist/web/index.d.mts",
|
|
37
|
-
"import": "./dist/web/index.mjs"
|
|
38
|
-
},
|
|
39
|
-
"./server": {
|
|
40
|
-
"types": "./dist/server/index.d.mts",
|
|
41
|
-
"import": "./dist/server/index.mjs"
|
|
42
34
|
}
|
|
43
35
|
},
|
|
44
36
|
"files": [
|
|
45
37
|
"dist"
|
|
46
38
|
],
|
|
47
39
|
"dependencies": {
|
|
48
|
-
"
|
|
40
|
+
"arktype": "^2.2.3",
|
|
41
|
+
"rxjs": "^7.8.2",
|
|
42
|
+
"uuid": "^14.0.1",
|
|
43
|
+
"@securitydept/client": "0.3.0-beta.10"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"vitest": "^4.1.10"
|
|
49
47
|
},
|
|
50
48
|
"scripts": {
|
|
51
49
|
"build": "tsdown",
|
|
52
|
-
"
|
|
50
|
+
"test": "vitest run --config ../../vitest.package.config.ts",
|
|
51
|
+
"test:watch": "vitest --config ../../vitest.package.config.ts",
|
|
52
|
+
"typecheck": "tsc -b --pretty false",
|
|
53
|
+
"build:dep": "pnpm run build"
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
|
-
/** Configuration for a single Basic Auth zone. */
|
|
3
|
-
interface BasicAuthZoneConfig {
|
|
4
|
-
/** URL path prefix for this zone (e.g. "/basic"). */
|
|
5
|
-
zonePrefix: string;
|
|
6
|
-
/** Login subpath relative to zone prefix (default: "/login"). */
|
|
7
|
-
loginSubpath?: string;
|
|
8
|
-
/** Logout subpath relative to zone prefix (default: "/logout"). */
|
|
9
|
-
logoutSubpath?: string;
|
|
10
|
-
}
|
|
11
|
-
/** Configuration for the Basic Auth Context Client. */
|
|
12
|
-
interface BasicAuthContextClientConfig {
|
|
13
|
-
/** Base URL of the SecurityDept server. */
|
|
14
|
-
baseUrl: string;
|
|
15
|
-
/** One or more Basic Auth zones to manage. */
|
|
16
|
-
zones: BasicAuthZoneConfig[];
|
|
17
|
-
/** Name of the query parameter for post-auth redirect (default: "post_auth_redirect_uri"). */
|
|
18
|
-
postAuthRedirectParam?: string;
|
|
19
|
-
}
|
|
20
|
-
declare const BasicAuthBoundaryKind: {
|
|
21
|
-
readonly Authenticated: "authenticated";
|
|
22
|
-
readonly Challenge: "challenge";
|
|
23
|
-
readonly Unauthorized: "unauthorized";
|
|
24
|
-
readonly LogoutPoison: "logout_poison";
|
|
25
|
-
};
|
|
26
|
-
type BasicAuthBoundaryKind = (typeof BasicAuthBoundaryKind)[keyof typeof BasicAuthBoundaryKind];
|
|
27
|
-
interface BasicAuthBoundaryObservation {
|
|
28
|
-
status: number;
|
|
29
|
-
challengeHeader?: string | null;
|
|
30
|
-
requestPath?: string;
|
|
31
|
-
}
|
|
32
|
-
declare const AuthGuardResultKind: {
|
|
33
|
-
readonly Ok: "ok";
|
|
34
|
-
readonly Redirect: "redirect";
|
|
35
|
-
};
|
|
36
|
-
type AuthGuardResultKind = (typeof AuthGuardResultKind)[keyof typeof AuthGuardResultKind];
|
|
37
|
-
declare const AuthGuardRedirectStatus: {
|
|
38
|
-
readonly Found: 302;
|
|
39
|
-
readonly SeeOther: 303;
|
|
40
|
-
readonly TemporaryRedirect: 307;
|
|
41
|
-
};
|
|
42
|
-
type AuthGuardRedirectStatus = (typeof AuthGuardRedirectStatus)[keyof typeof AuthGuardRedirectStatus];
|
|
43
|
-
/** Redirect instruction — framework-neutral result. */
|
|
44
|
-
type AuthGuardResult<T> = {
|
|
45
|
-
kind: typeof AuthGuardResultKind.Ok;
|
|
46
|
-
value: T;
|
|
47
|
-
} | {
|
|
48
|
-
kind: typeof AuthGuardResultKind.Redirect;
|
|
49
|
-
status: AuthGuardRedirectStatus;
|
|
50
|
-
location: string;
|
|
51
|
-
};
|
|
52
|
-
/** Resolved zone with computed paths. */
|
|
53
|
-
interface ResolvedBasicAuthZone {
|
|
54
|
-
zonePrefix: string;
|
|
55
|
-
loginPath: string;
|
|
56
|
-
logoutPath: string;
|
|
57
|
-
}
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/client.d.ts
|
|
60
|
-
declare function readBasicAuthBoundaryKind(options: BasicAuthBoundaryObservation): BasicAuthBoundaryKind;
|
|
61
|
-
/**
|
|
62
|
-
* Basic Auth Context Client.
|
|
63
|
-
*
|
|
64
|
-
* Thin client that manages zone boundary awareness and redirect instructions.
|
|
65
|
-
* It does NOT manage credentials or HTTP auth headers — those are handled
|
|
66
|
-
* by the browser's native Basic Auth UI triggered by 401 + WWW-Authenticate.
|
|
67
|
-
*/
|
|
68
|
-
declare class BasicAuthContextClient {
|
|
69
|
-
readonly zones: readonly ResolvedBasicAuthZone[];
|
|
70
|
-
private readonly _baseUrl;
|
|
71
|
-
private readonly _postAuthRedirectParam;
|
|
72
|
-
constructor(config: BasicAuthContextClientConfig);
|
|
73
|
-
/** Find the zone that contains the given path. */
|
|
74
|
-
zoneForPath(path: string): ResolvedBasicAuthZone | undefined;
|
|
75
|
-
zoneForPrefix(zonePrefix: string): ResolvedBasicAuthZone | undefined;
|
|
76
|
-
/** Check whether a path falls inside any configured zone. */
|
|
77
|
-
isInZone(path: string): boolean;
|
|
78
|
-
/** Build the full login URL for a zone, optionally with a post-auth redirect. */
|
|
79
|
-
loginUrl(zone: ResolvedBasicAuthZone, postAuthRedirectUri?: string): string;
|
|
80
|
-
loginUrlForZonePrefix(zonePrefix: string, postAuthRedirectUri?: string): string | null;
|
|
81
|
-
/** Build the full logout URL for a zone. */
|
|
82
|
-
logoutUrl(zone: ResolvedBasicAuthZone): string;
|
|
83
|
-
/**
|
|
84
|
-
* Handle a 401 response: if the current path is inside a zone,
|
|
85
|
-
* return a redirect instruction to the zone's login URL.
|
|
86
|
-
*/
|
|
87
|
-
handleUnauthorized(currentPath: string, responseStatus: number): AuthGuardResult<null>;
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
export { AuthGuardResultKind as a, BasicAuthContextClientConfig as c, AuthGuardResult as i, BasicAuthZoneConfig as l, readBasicAuthBoundaryKind as n, BasicAuthBoundaryKind as o, AuthGuardRedirectStatus as r, BasicAuthBoundaryObservation as s, BasicAuthContextClient as t, ResolvedBasicAuthZone as u };
|
|
91
|
-
//# sourceMappingURL=client-DU98lpa_.d.mts.map
|
package/dist/client-D_0eAARH.mjs
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { n as AuthGuardResultKind, r as BasicAuthBoundaryKind, t as AuthGuardRedirectStatus } from "./types-2IoKrgnl.mjs";
|
|
2
|
-
import { createSchema, validateWithSchemaSync } from "@securitydept/client";
|
|
3
|
-
//#region src/schemas.ts
|
|
4
|
-
function isNonEmptyString(v) {
|
|
5
|
-
return typeof v === "string" && v.length > 0;
|
|
6
|
-
}
|
|
7
|
-
function isObject(v) {
|
|
8
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
9
|
-
}
|
|
10
|
-
function validateZoneConfig(input, index) {
|
|
11
|
-
const issues = [];
|
|
12
|
-
if (!isObject(input)) {
|
|
13
|
-
issues.push({ message: `zones[${index}]: expected an object` });
|
|
14
|
-
return { issues };
|
|
15
|
-
}
|
|
16
|
-
if (!isNonEmptyString(input.zonePrefix)) issues.push({ message: `zones[${index}].zonePrefix: expected a non-empty string` });
|
|
17
|
-
if (input.loginSubpath !== void 0 && typeof input.loginSubpath !== "string") issues.push({ message: `zones[${index}].loginSubpath: expected a string if provided` });
|
|
18
|
-
if (input.logoutSubpath !== void 0 && typeof input.logoutSubpath !== "string") issues.push({ message: `zones[${index}].logoutSubpath: expected a string if provided` });
|
|
19
|
-
return issues.length > 0 ? { issues } : null;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Schema for validating BasicAuthContextClientConfig.
|
|
23
|
-
*
|
|
24
|
-
* Validates:
|
|
25
|
-
* - `baseUrl` is a non-empty string
|
|
26
|
-
* - `zones` is a non-empty array of valid zone configs
|
|
27
|
-
* - each zone has a non-empty `zonePrefix`
|
|
28
|
-
* - optional fields have correct types when provided
|
|
29
|
-
*/
|
|
30
|
-
const BasicAuthContextClientConfigSchema = createSchema({ validate(input) {
|
|
31
|
-
const issues = [];
|
|
32
|
-
if (!isObject(input)) return { issues: [{ message: "Expected an object" }] };
|
|
33
|
-
if (!isNonEmptyString(input.baseUrl)) issues.push({ message: "baseUrl: expected a non-empty string" });
|
|
34
|
-
if (!Array.isArray(input.zones) || input.zones.length === 0) issues.push({ message: "zones: expected a non-empty array of zone configs" });
|
|
35
|
-
else for (let i = 0; i < input.zones.length; i++) {
|
|
36
|
-
const zoneResult = validateZoneConfig(input.zones[i], i);
|
|
37
|
-
if (zoneResult) issues.push(...zoneResult.issues);
|
|
38
|
-
}
|
|
39
|
-
if (input.postAuthRedirectParam !== void 0 && typeof input.postAuthRedirectParam !== "string") issues.push({ message: "postAuthRedirectParam: expected a string if provided" });
|
|
40
|
-
if (issues.length > 0) return { issues };
|
|
41
|
-
return { value: input };
|
|
42
|
-
} });
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region src/client.ts
|
|
45
|
-
const DEFAULT_LOGIN_SUBPATH = "/login";
|
|
46
|
-
const DEFAULT_LOGOUT_SUBPATH = "/logout";
|
|
47
|
-
const DEFAULT_POST_AUTH_REDIRECT_PARAM = "post_auth_redirect_uri";
|
|
48
|
-
function resolveZone(config) {
|
|
49
|
-
const prefix = config.zonePrefix.replace(/\/+$/, "");
|
|
50
|
-
const loginSub = config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH;
|
|
51
|
-
const logoutSub = config.logoutSubpath ?? DEFAULT_LOGOUT_SUBPATH;
|
|
52
|
-
return {
|
|
53
|
-
zonePrefix: prefix,
|
|
54
|
-
loginPath: prefix + loginSub,
|
|
55
|
-
logoutPath: prefix + logoutSub
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
function readBasicAuthBoundaryKind(options) {
|
|
59
|
-
if (options.status < 400) return BasicAuthBoundaryKind.Authenticated;
|
|
60
|
-
if (options.challengeHeader) return BasicAuthBoundaryKind.Challenge;
|
|
61
|
-
if (options.status === 401 && options.requestPath === "/basic/logout") return BasicAuthBoundaryKind.LogoutPoison;
|
|
62
|
-
return BasicAuthBoundaryKind.Unauthorized;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Basic Auth Context Client.
|
|
66
|
-
*
|
|
67
|
-
* Thin client that manages zone boundary awareness and redirect instructions.
|
|
68
|
-
* It does NOT manage credentials or HTTP auth headers — those are handled
|
|
69
|
-
* by the browser's native Basic Auth UI triggered by 401 + WWW-Authenticate.
|
|
70
|
-
*/
|
|
71
|
-
var BasicAuthContextClient = class {
|
|
72
|
-
zones;
|
|
73
|
-
_baseUrl;
|
|
74
|
-
_postAuthRedirectParam;
|
|
75
|
-
constructor(config) {
|
|
76
|
-
const result = validateWithSchemaSync(BasicAuthContextClientConfigSchema, config);
|
|
77
|
-
if (!result.success) console.warn(`[BasicAuthContextClient] Deprecated config shape detected (will become an error in a future release): ${result.issues.map((i) => i.message).join("; ")}`);
|
|
78
|
-
this._baseUrl = (config.baseUrl ?? "").replace(/\/+$/, "");
|
|
79
|
-
this._postAuthRedirectParam = config.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;
|
|
80
|
-
this.zones = (config.zones ?? []).map(resolveZone);
|
|
81
|
-
}
|
|
82
|
-
/** Find the zone that contains the given path. */
|
|
83
|
-
zoneForPath(path) {
|
|
84
|
-
let matchedZone;
|
|
85
|
-
for (const zone of this.zones) {
|
|
86
|
-
if (!(path === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`))) continue;
|
|
87
|
-
if (!matchedZone || zone.zonePrefix.length > matchedZone.zonePrefix.length) matchedZone = zone;
|
|
88
|
-
}
|
|
89
|
-
return matchedZone;
|
|
90
|
-
}
|
|
91
|
-
zoneForPrefix(zonePrefix) {
|
|
92
|
-
return this.zones.find((zone) => zone.zonePrefix === zonePrefix);
|
|
93
|
-
}
|
|
94
|
-
/** Check whether a path falls inside any configured zone. */
|
|
95
|
-
isInZone(path) {
|
|
96
|
-
return this.zoneForPath(path) !== void 0;
|
|
97
|
-
}
|
|
98
|
-
/** Build the full login URL for a zone, optionally with a post-auth redirect. */
|
|
99
|
-
loginUrl(zone, postAuthRedirectUri) {
|
|
100
|
-
const base = this._baseUrl + zone.loginPath;
|
|
101
|
-
if (postAuthRedirectUri) return `${base}?${new URLSearchParams({ [this._postAuthRedirectParam]: postAuthRedirectUri }).toString()}`;
|
|
102
|
-
return base;
|
|
103
|
-
}
|
|
104
|
-
loginUrlForZonePrefix(zonePrefix, postAuthRedirectUri) {
|
|
105
|
-
const zone = this.zoneForPrefix(zonePrefix);
|
|
106
|
-
return zone ? this.loginUrl(zone, postAuthRedirectUri) : null;
|
|
107
|
-
}
|
|
108
|
-
/** Build the full logout URL for a zone. */
|
|
109
|
-
logoutUrl(zone) {
|
|
110
|
-
return this._baseUrl + zone.logoutPath;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Handle a 401 response: if the current path is inside a zone,
|
|
114
|
-
* return a redirect instruction to the zone's login URL.
|
|
115
|
-
*/
|
|
116
|
-
handleUnauthorized(currentPath, responseStatus) {
|
|
117
|
-
if (responseStatus !== 401) return {
|
|
118
|
-
kind: AuthGuardResultKind.Ok,
|
|
119
|
-
value: null
|
|
120
|
-
};
|
|
121
|
-
const zone = this.zoneForPath(currentPath);
|
|
122
|
-
if (!zone) return {
|
|
123
|
-
kind: AuthGuardResultKind.Ok,
|
|
124
|
-
value: null
|
|
125
|
-
};
|
|
126
|
-
return {
|
|
127
|
-
kind: AuthGuardResultKind.Redirect,
|
|
128
|
-
status: AuthGuardRedirectStatus.Found,
|
|
129
|
-
location: this.loginUrl(zone, currentPath)
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
//#endregion
|
|
134
|
-
export { readBasicAuthBoundaryKind as n, BasicAuthContextClientConfigSchema as r, BasicAuthContextClient as t };
|
|
135
|
-
|
|
136
|
-
//# sourceMappingURL=client-D_0eAARH.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-D_0eAARH.mjs","names":["BasicAuthBoundaryKindValues"],"sources":["../src/schemas.ts","../src/client.ts"],"sourcesContent":["// basic-auth-context-client — config validation schema\n//\n// Uses @standard-schema via the foundation createSchema helper to validate\n// BasicAuthContextClientConfig at the cross-boundary entry point.\n\nimport { createSchema } from \"@securitydept/client\";\nimport type { BasicAuthContextClientConfig } from \"./types\";\n\nfunction isNonEmptyString(v: unknown): v is string {\n\treturn typeof v === \"string\" && v.length > 0;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction validateZoneConfig(input: unknown, index: number) {\n\tconst issues: Array<{ message: string }> = [];\n\tif (!isObject(input)) {\n\t\tissues.push({ message: `zones[${index}]: expected an object` });\n\t\treturn { issues };\n\t}\n\tif (!isNonEmptyString(input.zonePrefix)) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].zonePrefix: expected a non-empty string`,\n\t\t});\n\t}\n\tif (\n\t\tinput.loginSubpath !== undefined &&\n\t\ttypeof input.loginSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].loginSubpath: expected a string if provided`,\n\t\t});\n\t}\n\tif (\n\t\tinput.logoutSubpath !== undefined &&\n\t\ttypeof input.logoutSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].logoutSubpath: expected a string if provided`,\n\t\t});\n\t}\n\treturn issues.length > 0 ? { issues } : null;\n}\n\n/**\n * Schema for validating BasicAuthContextClientConfig.\n *\n * Validates:\n * - `baseUrl` is a non-empty string\n * - `zones` is a non-empty array of valid zone configs\n * - each zone has a non-empty `zonePrefix`\n * - optional fields have correct types when provided\n */\nexport const BasicAuthContextClientConfigSchema =\n\tcreateSchema<BasicAuthContextClientConfig>({\n\t\tvalidate(input) {\n\t\t\tconst issues: Array<{ message: string }> = [];\n\n\t\t\tif (!isObject(input)) {\n\t\t\t\treturn { issues: [{ message: \"Expected an object\" }] };\n\t\t\t}\n\n\t\t\tif (!isNonEmptyString(input.baseUrl)) {\n\t\t\t\tissues.push({ message: \"baseUrl: expected a non-empty string\" });\n\t\t\t}\n\n\t\t\tif (!Array.isArray(input.zones) || input.zones.length === 0) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"zones: expected a non-empty array of zone configs\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < input.zones.length; i++) {\n\t\t\t\t\tconst zoneResult = validateZoneConfig(input.zones[i], i);\n\t\t\t\t\tif (zoneResult) {\n\t\t\t\t\t\tissues.push(...zoneResult.issues);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tinput.postAuthRedirectParam !== undefined &&\n\t\t\t\ttypeof input.postAuthRedirectParam !== \"string\"\n\t\t\t) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"postAuthRedirectParam: expected a string if provided\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (issues.length > 0) {\n\t\t\t\treturn { issues };\n\t\t\t}\n\n\t\t\treturn { value: input as unknown as BasicAuthContextClientConfig };\n\t\t},\n\t});\n","import { validateWithSchemaSync } from \"@securitydept/client\";\nimport { BasicAuthContextClientConfigSchema } from \"./schemas\";\nimport type {\n\tAuthGuardResult,\n\tBasicAuthBoundaryKind,\n\tBasicAuthBoundaryObservation,\n\tBasicAuthContextClientConfig,\n\tBasicAuthZoneConfig,\n\tResolvedBasicAuthZone,\n} from \"./types\";\nimport {\n\tAuthGuardRedirectStatus,\n\tAuthGuardResultKind,\n\tBasicAuthBoundaryKind as BasicAuthBoundaryKindValues,\n} from \"./types\";\n\nconst DEFAULT_LOGIN_SUBPATH = \"/login\";\nconst DEFAULT_LOGOUT_SUBPATH = \"/logout\";\nconst DEFAULT_POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\n\nfunction resolveZone(config: BasicAuthZoneConfig): ResolvedBasicAuthZone {\n\tconst prefix = config.zonePrefix.replace(/\\/+$/, \"\");\n\tconst loginSub = config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH;\n\tconst logoutSub = config.logoutSubpath ?? DEFAULT_LOGOUT_SUBPATH;\n\n\treturn {\n\t\tzonePrefix: prefix,\n\t\tloginPath: prefix + loginSub,\n\t\tlogoutPath: prefix + logoutSub,\n\t};\n}\n\nexport function readBasicAuthBoundaryKind(\n\toptions: BasicAuthBoundaryObservation,\n): BasicAuthBoundaryKind {\n\tif (options.status < 400) {\n\t\treturn BasicAuthBoundaryKindValues.Authenticated;\n\t}\n\n\tif (options.challengeHeader) {\n\t\treturn BasicAuthBoundaryKindValues.Challenge;\n\t}\n\n\tif (options.status === 401 && options.requestPath === \"/basic/logout\") {\n\t\treturn BasicAuthBoundaryKindValues.LogoutPoison;\n\t}\n\n\treturn BasicAuthBoundaryKindValues.Unauthorized;\n}\n\n/**\n * Basic Auth Context Client.\n *\n * Thin client that manages zone boundary awareness and redirect instructions.\n * It does NOT manage credentials or HTTP auth headers — those are handled\n * by the browser's native Basic Auth UI triggered by 401 + WWW-Authenticate.\n */\nexport class BasicAuthContextClient {\n\treadonly zones: readonly ResolvedBasicAuthZone[];\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _postAuthRedirectParam: string;\n\n\tconstructor(config: BasicAuthContextClientConfig) {\n\t\t// Deprecation-phase validation: warn on invalid config but do not throw.\n\t\t// A future minor release will escalate this to a hard error.\n\t\t// See 110-TS_SDK_MIGRATIONS.md for the full deprecation note.\n\t\tconst result = validateWithSchemaSync(\n\t\t\tBasicAuthContextClientConfigSchema,\n\t\t\tconfig,\n\t\t);\n\t\tif (!result.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`[BasicAuthContextClient] Deprecated config shape detected (will become an error in a future release): ${result.issues.map((i) => i.message).join(\"; \")}`,\n\t\t\t);\n\t\t}\n\n\t\tthis._baseUrl = (config.baseUrl ?? \"\").replace(/\\/+$/, \"\");\n\t\tthis._postAuthRedirectParam =\n\t\t\tconfig.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;\n\t\tthis.zones = (config.zones ?? []).map(resolveZone);\n\t}\n\n\t/** Find the zone that contains the given path. */\n\tzoneForPath(path: string): ResolvedBasicAuthZone | undefined {\n\t\tlet matchedZone: ResolvedBasicAuthZone | undefined;\n\n\t\tfor (const zone of this.zones) {\n\t\t\tconst matches =\n\t\t\t\tpath === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`);\n\t\t\tif (!matches) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!matchedZone ||\n\t\t\t\tzone.zonePrefix.length > matchedZone.zonePrefix.length\n\t\t\t) {\n\t\t\t\tmatchedZone = zone;\n\t\t\t}\n\t\t}\n\n\t\treturn matchedZone;\n\t}\n\n\tzoneForPrefix(zonePrefix: string): ResolvedBasicAuthZone | undefined {\n\t\treturn this.zones.find((zone) => zone.zonePrefix === zonePrefix);\n\t}\n\n\t/** Check whether a path falls inside any configured zone. */\n\tisInZone(path: string): boolean {\n\t\treturn this.zoneForPath(path) !== undefined;\n\t}\n\n\t/** Build the full login URL for a zone, optionally with a post-auth redirect. */\n\tloginUrl(zone: ResolvedBasicAuthZone, postAuthRedirectUri?: string): string {\n\t\tconst base = this._baseUrl + zone.loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[this._postAuthRedirectParam]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\tloginUrlForZonePrefix(\n\t\tzonePrefix: string,\n\t\tpostAuthRedirectUri?: string,\n\t): string | null {\n\t\tconst zone = this.zoneForPrefix(zonePrefix);\n\t\treturn zone ? this.loginUrl(zone, postAuthRedirectUri) : null;\n\t}\n\n\t/** Build the full logout URL for a zone. */\n\tlogoutUrl(zone: ResolvedBasicAuthZone): string {\n\t\treturn this._baseUrl + zone.logoutPath;\n\t}\n\n\t/**\n\t * Handle a 401 response: if the current path is inside a zone,\n\t * return a redirect instruction to the zone's login URL.\n\t */\n\thandleUnauthorized(\n\t\tcurrentPath: string,\n\t\tresponseStatus: number,\n\t): AuthGuardResult<null> {\n\t\tif (responseStatus !== 401) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\tconst zone = this.zoneForPath(currentPath);\n\t\tif (!zone) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: AuthGuardResultKind.Redirect,\n\t\t\tstatus: AuthGuardRedirectStatus.Found,\n\t\t\tlocation: this.loginUrl(zone, currentPath),\n\t\t};\n\t}\n}\n"],"mappings":";;;AAQA,SAAS,iBAAiB,GAAyB;AAClD,QAAO,OAAO,MAAM,YAAY,EAAE,SAAS;;AAG5C,SAAS,SAAS,GAA0C;AAC3D,QAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,EAAE;;AAGhE,SAAS,mBAAmB,OAAgB,OAAe;CAC1D,MAAM,SAAqC,EAAE;AAC7C,KAAI,CAAC,SAAS,MAAM,EAAE;AACrB,SAAO,KAAK,EAAE,SAAS,SAAS,MAAM,wBAAwB,CAAC;AAC/D,SAAO,EAAE,QAAQ;;AAElB,KAAI,CAAC,iBAAiB,MAAM,WAAW,CACtC,QAAO,KAAK,EACX,SAAS,SAAS,MAAM,4CACxB,CAAC;AAEH,KACC,MAAM,iBAAiB,KAAA,KACvB,OAAO,MAAM,iBAAiB,SAE9B,QAAO,KAAK,EACX,SAAS,SAAS,MAAM,gDACxB,CAAC;AAEH,KACC,MAAM,kBAAkB,KAAA,KACxB,OAAO,MAAM,kBAAkB,SAE/B,QAAO,KAAK,EACX,SAAS,SAAS,MAAM,iDACxB,CAAC;AAEH,QAAO,OAAO,SAAS,IAAI,EAAE,QAAQ,GAAG;;;;;;;;;;;AAYzC,MAAa,qCACZ,aAA2C,EAC1C,SAAS,OAAO;CACf,MAAM,SAAqC,EAAE;AAE7C,KAAI,CAAC,SAAS,MAAM,CACnB,QAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,sBAAsB,CAAC,EAAE;AAGvD,KAAI,CAAC,iBAAiB,MAAM,QAAQ,CACnC,QAAO,KAAK,EAAE,SAAS,wCAAwC,CAAC;AAGjE,KAAI,CAAC,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,MAAM,WAAW,EACzD,QAAO,KAAK,EACX,SAAS,qDACT,CAAC;KAEF,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,MAAM,QAAQ,KAAK;EAC5C,MAAM,aAAa,mBAAmB,MAAM,MAAM,IAAI,EAAE;AACxD,MAAI,WACH,QAAO,KAAK,GAAG,WAAW,OAAO;;AAKpC,KACC,MAAM,0BAA0B,KAAA,KAChC,OAAO,MAAM,0BAA0B,SAEvC,QAAO,KAAK,EACX,SAAS,wDACT,CAAC;AAGH,KAAI,OAAO,SAAS,EACnB,QAAO,EAAE,QAAQ;AAGlB,QAAO,EAAE,OAAO,OAAkD;GAEnE,CAAC;;;AChFH,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAC/B,MAAM,mCAAmC;AAEzC,SAAS,YAAY,QAAoD;CACxE,MAAM,SAAS,OAAO,WAAW,QAAQ,QAAQ,GAAG;CACpD,MAAM,WAAW,OAAO,gBAAgB;CACxC,MAAM,YAAY,OAAO,iBAAiB;AAE1C,QAAO;EACN,YAAY;EACZ,WAAW,SAAS;EACpB,YAAY,SAAS;EACrB;;AAGF,SAAgB,0BACf,SACwB;AACxB,KAAI,QAAQ,SAAS,IACpB,QAAOA,sBAA4B;AAGpC,KAAI,QAAQ,gBACX,QAAOA,sBAA4B;AAGpC,KAAI,QAAQ,WAAW,OAAO,QAAQ,gBAAgB,gBACrD,QAAOA,sBAA4B;AAGpC,QAAOA,sBAA4B;;;;;;;;;AAUpC,IAAa,yBAAb,MAAoC;CACnC;CACA;CACA;CAEA,YAAY,QAAsC;EAIjD,MAAM,SAAS,uBACd,oCACA,OACA;AACD,MAAI,CAAC,OAAO,QACX,SAAQ,KACP,yGAAyG,OAAO,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,GACvJ;AAGF,OAAK,YAAY,OAAO,WAAW,IAAI,QAAQ,QAAQ,GAAG;AAC1D,OAAK,yBACJ,OAAO,yBAAyB;AACjC,OAAK,SAAS,OAAO,SAAS,EAAE,EAAE,IAAI,YAAY;;;CAInD,YAAY,MAAiD;EAC5D,IAAI;AAEJ,OAAK,MAAM,QAAQ,KAAK,OAAO;AAG9B,OAAI,EADH,SAAS,KAAK,cAAc,KAAK,WAAW,GAAG,KAAK,WAAW,GAAG,EAElE;AAGD,OACC,CAAC,eACD,KAAK,WAAW,SAAS,YAAY,WAAW,OAEhD,eAAc;;AAIhB,SAAO;;CAGR,cAAc,YAAuD;AACpE,SAAO,KAAK,MAAM,MAAM,SAAS,KAAK,eAAe,WAAW;;;CAIjE,SAAS,MAAuB;AAC/B,SAAO,KAAK,YAAY,KAAK,KAAK,KAAA;;;CAInC,SAAS,MAA6B,qBAAsC;EAC3E,MAAM,OAAO,KAAK,WAAW,KAAK;AAClC,MAAI,oBAIH,QAAO,GAAG,KAAK,GAHA,IAAI,gBAAgB,GACjC,KAAK,yBAAyB,qBAC/B,CAAC,CACuB,UAAU;AAEpC,SAAO;;CAGR,sBACC,YACA,qBACgB;EAChB,MAAM,OAAO,KAAK,cAAc,WAAW;AAC3C,SAAO,OAAO,KAAK,SAAS,MAAM,oBAAoB,GAAG;;;CAI1D,UAAU,MAAqC;AAC9C,SAAO,KAAK,WAAW,KAAK;;;;;;CAO7B,mBACC,aACA,gBACwB;AACxB,MAAI,mBAAmB,IACtB,QAAO;GAAE,MAAM,oBAAoB;GAAI,OAAO;GAAM;EAGrD,MAAM,OAAO,KAAK,YAAY,YAAY;AAC1C,MAAI,CAAC,KACJ,QAAO;GAAE,MAAM,oBAAoB;GAAI,OAAO;GAAM;AAGrD,SAAO;GACN,MAAM,oBAAoB;GAC1B,QAAQ,wBAAwB;GAChC,UAAU,KAAK,SAAS,MAAM,YAAY;GAC1C"}
|
package/dist/server/index.d.mts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { c as BasicAuthContextClientConfig, t as BasicAuthContextClient } from "../client-DU98lpa_.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/server/helpers.d.ts
|
|
4
|
-
/** Minimal server request context — host-neutral. */
|
|
5
|
-
interface ServerRequestContext {
|
|
6
|
-
/** The request path (e.g. "/api/protected"). */
|
|
7
|
-
path: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Server-side redirect instruction for the host to act on.
|
|
11
|
-
*
|
|
12
|
-
* The host translates this into its framework-specific response:
|
|
13
|
-
* - Next.js: `{ redirect: { destination, statusCode } }`
|
|
14
|
-
* - Express: `res.redirect(statusCode, destination)`
|
|
15
|
-
* - etc.
|
|
16
|
-
*/
|
|
17
|
-
interface ServerRedirectInstruction {
|
|
18
|
-
/** HTTP redirect status code. */
|
|
19
|
-
statusCode: number;
|
|
20
|
-
/** Redirect target URL. */
|
|
21
|
-
destination: string;
|
|
22
|
-
}
|
|
23
|
-
/** Options for {@link createBasicAuthServerHelper}. */
|
|
24
|
-
interface CreateBasicAuthServerHelperOptions {
|
|
25
|
-
/** BasicAuthContextClient config — same shape as the browser client. */
|
|
26
|
-
config: BasicAuthContextClientConfig;
|
|
27
|
-
}
|
|
28
|
-
/** Server-host helper for basic-auth zone-based redirect logic. */
|
|
29
|
-
interface BasicAuthServerHelper {
|
|
30
|
-
/** The underlying BasicAuthContextClient instance. */
|
|
31
|
-
readonly client: BasicAuthContextClient;
|
|
32
|
-
/**
|
|
33
|
-
* Handle a 401 response from the backend in a server context.
|
|
34
|
-
*
|
|
35
|
-
* If the request path falls inside a configured zone, returns a
|
|
36
|
-
* redirect instruction. Otherwise returns `null`.
|
|
37
|
-
*/
|
|
38
|
-
handleUnauthorized(context: ServerRequestContext): ServerRedirectInstruction | null;
|
|
39
|
-
/**
|
|
40
|
-
* Build the login URL for a specific path.
|
|
41
|
-
*
|
|
42
|
-
* Returns the login URL for the zone containing the path,
|
|
43
|
-
* or `null` if the path isn't in any zone.
|
|
44
|
-
*/
|
|
45
|
-
loginUrlForPath(path: string): string | null;
|
|
46
|
-
/**
|
|
47
|
-
* Build the logout URL for a specific path.
|
|
48
|
-
*
|
|
49
|
-
* Returns the logout URL for the zone containing the path,
|
|
50
|
-
* or `null` if the path isn't in any zone.
|
|
51
|
-
*/
|
|
52
|
-
logoutUrlForPath(path: string): string | null;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Create a server-host helper for basic-auth zone management.
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```ts
|
|
59
|
-
* const helper = createBasicAuthServerHelper({
|
|
60
|
-
* config: { baseUrl: "https://auth.example.com", zones: [{ zonePrefix: "/api" }] },
|
|
61
|
-
* });
|
|
62
|
-
*
|
|
63
|
-
* // In a server request handler:
|
|
64
|
-
* const redirect = helper.handleUnauthorized({ path: "/api/data" });
|
|
65
|
-
* if (redirect) {
|
|
66
|
-
* return Response.redirect(redirect.destination, redirect.statusCode);
|
|
67
|
-
* }
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
declare function createBasicAuthServerHelper(options: CreateBasicAuthServerHelperOptions): BasicAuthServerHelper;
|
|
71
|
-
//#endregion
|
|
72
|
-
export { type BasicAuthServerHelper, type CreateBasicAuthServerHelperOptions, type ServerRedirectInstruction, type ServerRequestContext, createBasicAuthServerHelper };
|
|
73
|
-
//# sourceMappingURL=index.d.mts.map
|
package/dist/server/index.mjs
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { t as BasicAuthContextClient } from "../client-D_0eAARH.mjs";
|
|
2
|
-
import { n as AuthGuardResultKind } from "../types-2IoKrgnl.mjs";
|
|
3
|
-
//#region src/server/helpers.ts
|
|
4
|
-
/**
|
|
5
|
-
* Create a server-host helper for basic-auth zone management.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* const helper = createBasicAuthServerHelper({
|
|
10
|
-
* config: { baseUrl: "https://auth.example.com", zones: [{ zonePrefix: "/api" }] },
|
|
11
|
-
* });
|
|
12
|
-
*
|
|
13
|
-
* // In a server request handler:
|
|
14
|
-
* const redirect = helper.handleUnauthorized({ path: "/api/data" });
|
|
15
|
-
* if (redirect) {
|
|
16
|
-
* return Response.redirect(redirect.destination, redirect.statusCode);
|
|
17
|
-
* }
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
function createBasicAuthServerHelper(options) {
|
|
21
|
-
const client = new BasicAuthContextClient(options.config);
|
|
22
|
-
return {
|
|
23
|
-
client,
|
|
24
|
-
handleUnauthorized(context) {
|
|
25
|
-
const result = client.handleUnauthorized(context.path, 401);
|
|
26
|
-
if (result.kind === AuthGuardResultKind.Redirect) return {
|
|
27
|
-
statusCode: result.status,
|
|
28
|
-
destination: result.location
|
|
29
|
-
};
|
|
30
|
-
return null;
|
|
31
|
-
},
|
|
32
|
-
loginUrlForPath(path) {
|
|
33
|
-
const zone = client.zoneForPath(path);
|
|
34
|
-
if (!zone) return null;
|
|
35
|
-
return client.loginUrl(zone, path);
|
|
36
|
-
},
|
|
37
|
-
logoutUrlForPath(path) {
|
|
38
|
-
const zone = client.zoneForPath(path);
|
|
39
|
-
if (!zone) return null;
|
|
40
|
-
return client.logoutUrl(zone);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
//#endregion
|
|
45
|
-
export { createBasicAuthServerHelper };
|
|
46
|
-
|
|
47
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/helpers.ts"],"sourcesContent":["// Server-host helpers for basic-auth-context-client\n//\n// Provides host-neutral server helpers that wrap BasicAuthContextClient\n// for SSR / server-render-host scenarios (Next.js, Remix, Astro,\n// plain Node request handlers, etc.).\n//\n// The host owns HTTP response construction (302 headers, response body).\n// These helpers produce redirect instructions the host can act on.\n\nimport { BasicAuthContextClient } from \"../client\";\nimport type { AuthGuardResult, BasicAuthContextClientConfig } from \"../types\";\nimport { AuthGuardResultKind } from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Server request context\n// ---------------------------------------------------------------------------\n\n/** Minimal server request context — host-neutral. */\nexport interface ServerRequestContext {\n\t/** The request path (e.g. \"/api/protected\"). */\n\tpath: string;\n}\n\n// ---------------------------------------------------------------------------\n// Redirect instruction\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side redirect instruction for the host to act on.\n *\n * The host translates this into its framework-specific response:\n * - Next.js: `{ redirect: { destination, statusCode } }`\n * - Express: `res.redirect(statusCode, destination)`\n * - etc.\n */\nexport interface ServerRedirectInstruction {\n\t/** HTTP redirect status code. */\n\tstatusCode: number;\n\t/** Redirect target URL. */\n\tdestination: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helper options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createBasicAuthServerHelper}. */\nexport interface CreateBasicAuthServerHelperOptions {\n\t/** BasicAuthContextClient config — same shape as the browser client. */\n\tconfig: BasicAuthContextClientConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Helper interface\n// ---------------------------------------------------------------------------\n\n/** Server-host helper for basic-auth zone-based redirect logic. */\nexport interface BasicAuthServerHelper {\n\t/** The underlying BasicAuthContextClient instance. */\n\treadonly client: BasicAuthContextClient;\n\n\t/**\n\t * Handle a 401 response from the backend in a server context.\n\t *\n\t * If the request path falls inside a configured zone, returns a\n\t * redirect instruction. Otherwise returns `null`.\n\t */\n\thandleUnauthorized(\n\t\tcontext: ServerRequestContext,\n\t): ServerRedirectInstruction | null;\n\n\t/**\n\t * Build the login URL for a specific path.\n\t *\n\t * Returns the login URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tloginUrlForPath(path: string): string | null;\n\n\t/**\n\t * Build the logout URL for a specific path.\n\t *\n\t * Returns the logout URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tlogoutUrlForPath(path: string): string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a server-host helper for basic-auth zone management.\n *\n * @example\n * ```ts\n * const helper = createBasicAuthServerHelper({\n * config: { baseUrl: \"https://auth.example.com\", zones: [{ zonePrefix: \"/api\" }] },\n * });\n *\n * // In a server request handler:\n * const redirect = helper.handleUnauthorized({ path: \"/api/data\" });\n * if (redirect) {\n * return Response.redirect(redirect.destination, redirect.statusCode);\n * }\n * ```\n */\nexport function createBasicAuthServerHelper(\n\toptions: CreateBasicAuthServerHelperOptions,\n): BasicAuthServerHelper {\n\tconst client = new BasicAuthContextClient(options.config);\n\n\treturn {\n\t\tclient,\n\n\t\thandleUnauthorized(\n\t\t\tcontext: ServerRequestContext,\n\t\t): ServerRedirectInstruction | null {\n\t\t\tconst result: AuthGuardResult<null> = client.handleUnauthorized(\n\t\t\t\tcontext.path,\n\t\t\t\t401,\n\t\t\t);\n\n\t\t\tif (result.kind === AuthGuardResultKind.Redirect) {\n\t\t\t\treturn {\n\t\t\t\t\tstatusCode: result.status,\n\t\t\t\t\tdestination: result.location,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tloginUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.loginUrl(zone, path);\n\t\t},\n\n\t\tlogoutUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.logoutUrl(zone);\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4GA,SAAgB,4BACf,SACwB;CACxB,MAAM,SAAS,IAAI,uBAAuB,QAAQ,OAAO;AAEzD,QAAO;EACN;EAEA,mBACC,SACmC;GACnC,MAAM,SAAgC,OAAO,mBAC5C,QAAQ,MACR,IACA;AAED,OAAI,OAAO,SAAS,oBAAoB,SACvC,QAAO;IACN,YAAY,OAAO;IACnB,aAAa,OAAO;IACpB;AAGF,UAAO;;EAGR,gBAAgB,MAA6B;GAC5C,MAAM,OAAO,OAAO,YAAY,KAAK;AACrC,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,OAAO,SAAS,MAAM,KAAK;;EAGnC,iBAAiB,MAA6B;GAC7C,MAAM,OAAO,OAAO,YAAY,KAAK;AACrC,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,OAAO,UAAU,KAAK;;EAE9B"}
|
package/dist/types-2IoKrgnl.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
//#region src/types.ts
|
|
2
|
-
const BasicAuthBoundaryKind = {
|
|
3
|
-
Authenticated: "authenticated",
|
|
4
|
-
Challenge: "challenge",
|
|
5
|
-
Unauthorized: "unauthorized",
|
|
6
|
-
LogoutPoison: "logout_poison"
|
|
7
|
-
};
|
|
8
|
-
const AuthGuardResultKind = {
|
|
9
|
-
Ok: "ok",
|
|
10
|
-
Redirect: "redirect"
|
|
11
|
-
};
|
|
12
|
-
const AuthGuardRedirectStatus = {
|
|
13
|
-
Found: 302,
|
|
14
|
-
SeeOther: 303,
|
|
15
|
-
TemporaryRedirect: 307
|
|
16
|
-
};
|
|
17
|
-
//#endregion
|
|
18
|
-
export { AuthGuardResultKind as n, BasicAuthBoundaryKind as r, AuthGuardRedirectStatus as t };
|
|
19
|
-
|
|
20
|
-
//# sourceMappingURL=types-2IoKrgnl.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-2IoKrgnl.mjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["// --- Basic Auth Context Client types ---\n\n/** Configuration for a single Basic Auth zone. */\nexport interface BasicAuthZoneConfig {\n\t/** URL path prefix for this zone (e.g. \"/basic\"). */\n\tzonePrefix: string;\n\t/** Login subpath relative to zone prefix (default: \"/login\"). */\n\tloginSubpath?: string;\n\t/** Logout subpath relative to zone prefix (default: \"/logout\"). */\n\tlogoutSubpath?: string;\n}\n\n/** Configuration for the Basic Auth Context Client. */\nexport interface BasicAuthContextClientConfig {\n\t/** Base URL of the SecurityDept server. */\n\tbaseUrl: string;\n\t/** One or more Basic Auth zones to manage. */\n\tzones: BasicAuthZoneConfig[];\n\t/** Name of the query parameter for post-auth redirect (default: \"post_auth_redirect_uri\"). */\n\tpostAuthRedirectParam?: string;\n}\n\nexport const BasicAuthBoundaryKind = {\n\tAuthenticated: \"authenticated\",\n\tChallenge: \"challenge\",\n\tUnauthorized: \"unauthorized\",\n\tLogoutPoison: \"logout_poison\",\n} as const;\n\nexport type BasicAuthBoundaryKind =\n\t(typeof BasicAuthBoundaryKind)[keyof typeof BasicAuthBoundaryKind];\n\nexport interface BasicAuthBoundaryObservation {\n\tstatus: number;\n\tchallengeHeader?: string | null;\n\trequestPath?: string;\n}\n\nexport const AuthGuardResultKind = {\n\tOk: \"ok\",\n\tRedirect: \"redirect\",\n} as const;\n\nexport type AuthGuardResultKind =\n\t(typeof AuthGuardResultKind)[keyof typeof AuthGuardResultKind];\n\nexport const AuthGuardRedirectStatus = {\n\tFound: 302,\n\tSeeOther: 303,\n\tTemporaryRedirect: 307,\n} as const;\n\nexport type AuthGuardRedirectStatus =\n\t(typeof AuthGuardRedirectStatus)[keyof typeof AuthGuardRedirectStatus];\n\n/** Redirect instruction — framework-neutral result. */\nexport type AuthGuardResult<T> =\n\t| { kind: typeof AuthGuardResultKind.Ok; value: T }\n\t| {\n\t\t\tkind: typeof AuthGuardResultKind.Redirect;\n\t\t\tstatus: AuthGuardRedirectStatus;\n\t\t\tlocation: string;\n\t };\n\n/** Resolved zone with computed paths. */\nexport interface ResolvedBasicAuthZone {\n\tzonePrefix: string;\n\tloginPath: string;\n\tlogoutPath: string;\n}\n"],"mappings":";AAsBA,MAAa,wBAAwB;CACpC,eAAe;CACf,WAAW;CACX,cAAc;CACd,cAAc;CACd;AAWD,MAAa,sBAAsB;CAClC,IAAI;CACJ,UAAU;CACV;AAKD,MAAa,0BAA0B;CACtC,OAAO;CACP,UAAU;CACV,mBAAmB;CACnB"}
|
package/dist/web/index.d.mts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { i as AuthGuardResult, t as BasicAuthContextClient } from "../client-DU98lpa_.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/web/login-redirect.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Options for {@link loginWithRedirect}.
|
|
6
|
-
*/
|
|
7
|
-
interface LoginWithRedirectOptions {
|
|
8
|
-
/**
|
|
9
|
-
* The current request path to resolve the target zone.
|
|
10
|
-
*
|
|
11
|
-
* When omitted, `window.location.pathname` is used.
|
|
12
|
-
*/
|
|
13
|
-
currentPath?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Where to redirect the user after successful authentication.
|
|
16
|
-
*
|
|
17
|
-
* When omitted, `window.location.href` is used as the return URI.
|
|
18
|
-
*/
|
|
19
|
-
postAuthRedirectUri?: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* One-shot browser redirect to the zone login endpoint for the given path.
|
|
23
|
-
*
|
|
24
|
-
* 1. Resolves the zone for the current (or specified) path.
|
|
25
|
-
* 2. Navigates the current window to the zone's login URL with a
|
|
26
|
-
* post-auth redirect parameter.
|
|
27
|
-
*
|
|
28
|
-
* Returns `false` if no zone matches the path (no redirect performed).
|
|
29
|
-
* Returns `true` if the redirect was initiated.
|
|
30
|
-
*
|
|
31
|
-
* This is the recommended browser entry point for initiating basic-auth
|
|
32
|
-
* login. It is intentionally a standalone function (not a method on
|
|
33
|
-
* `BasicAuthContextClient`) to keep browser-specific side-effects out
|
|
34
|
-
* of the host-neutral client contract.
|
|
35
|
-
*/
|
|
36
|
-
declare function loginWithRedirect(client: BasicAuthContextClient, options?: LoginWithRedirectOptions): boolean;
|
|
37
|
-
//#endregion
|
|
38
|
-
//#region src/web/redirect.d.ts
|
|
39
|
-
/**
|
|
40
|
-
* Perform a browser redirect for an `AuthGuardResult` that requires redirection.
|
|
41
|
-
*/
|
|
42
|
-
declare function performRedirect(result: AuthGuardResult<unknown>): void;
|
|
43
|
-
//#endregion
|
|
44
|
-
export { type LoginWithRedirectOptions, loginWithRedirect, performRedirect };
|
|
45
|
-
//# sourceMappingURL=index.d.mts.map
|