nestjs-slightly-better-auth 1.0.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/LICENSE +7 -0
- package/README.md +57 -0
- package/dist/admin.cjs +88 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +14 -0
- package/dist/admin.d.cts.map +1 -0
- package/dist/admin.d.mts +14 -0
- package/dist/admin.d.mts.map +1 -0
- package/dist/admin.mjs +85 -0
- package/dist/admin.mjs.map +1 -0
- package/dist/api-key.cjs +163 -0
- package/dist/api-key.cjs.map +1 -0
- package/dist/api-key.d.cts +42 -0
- package/dist/api-key.d.cts.map +1 -0
- package/dist/api-key.d.mts +42 -0
- package/dist/api-key.d.mts.map +1 -0
- package/dist/api-key.mjs +159 -0
- package/dist/api-key.mjs.map +1 -0
- package/dist/auth-contracts-BBA1C1gj.d.cts +1635 -0
- package/dist/auth-contracts-BBA1C1gj.d.cts.map +1 -0
- package/dist/auth-contracts-tqsfBZ8B.d.mts +1635 -0
- package/dist/auth-contracts-tqsfBZ8B.d.mts.map +1 -0
- package/dist/auth-decorators-CwW5JY4y.cjs +1274 -0
- package/dist/auth-decorators-CwW5JY4y.cjs.map +1 -0
- package/dist/auth-decorators-DfAJbHar.mjs +849 -0
- package/dist/auth-decorators-DfAJbHar.mjs.map +1 -0
- package/dist/auth-errors-CQjfgjaO.mjs +283 -0
- package/dist/auth-errors-CQjfgjaO.mjs.map +1 -0
- package/dist/auth-errors-CsERBNWI.cjs +348 -0
- package/dist/auth-errors-CsERBNWI.cjs.map +1 -0
- package/dist/express.cjs +247 -0
- package/dist/express.cjs.map +1 -0
- package/dist/express.d.cts +37 -0
- package/dist/express.d.cts.map +1 -0
- package/dist/express.d.mts +37 -0
- package/dist/express.d.mts.map +1 -0
- package/dist/express.mjs +245 -0
- package/dist/express.mjs.map +1 -0
- package/dist/fastify.cjs +215 -0
- package/dist/fastify.cjs.map +1 -0
- package/dist/fastify.d.cts +28 -0
- package/dist/fastify.d.cts.map +1 -0
- package/dist/fastify.d.mts +28 -0
- package/dist/fastify.d.mts.map +1 -0
- package/dist/fastify.mjs +213 -0
- package/dist/fastify.mjs.map +1 -0
- package/dist/index.cjs +2634 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2556 -0
- package/dist/index.mjs.map +1 -0
- package/dist/organization.cjs +191 -0
- package/dist/organization.cjs.map +1 -0
- package/dist/organization.d.cts +40 -0
- package/dist/organization.d.cts.map +1 -0
- package/dist/organization.d.mts +40 -0
- package/dist/organization.d.mts.map +1 -0
- package/dist/organization.mjs +177 -0
- package/dist/organization.mjs.map +1 -0
- package/dist/platform.cjs +393 -0
- package/dist/platform.cjs.map +1 -0
- package/dist/platform.d.cts +43 -0
- package/dist/platform.d.cts.map +1 -0
- package/dist/platform.d.mts +43 -0
- package/dist/platform.d.mts.map +1 -0
- package/dist/platform.mjs +385 -0
- package/dist/platform.mjs.map +1 -0
- package/dist/plugin.cjs +312 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +26 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.mts +26 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +310 -0
- package/dist/plugin.mjs.map +1 -0
- package/package.json +186 -0
|
@@ -0,0 +1,1635 @@
|
|
|
1
|
+
import { IncomingHttpHeaders, IncomingMessage } from "node:http";
|
|
2
|
+
import { CallHandler, CanActivate, DynamicModule, ExecutionContext, InjectionToken, Logger, LoggerService, ModuleMetadata, NestInterceptor, OptionalFactoryDependency, Provider, Type } from "@nestjs/common";
|
|
3
|
+
import { AbstractHttpAdapter, DiscoveryService, HttpAdapterHost, ModuleRef, Reflector } from "@nestjs/core";
|
|
4
|
+
import "@nestjs/core/injector/instance-wrapper.js";
|
|
5
|
+
import { HookEndpointContext } from "better-auth";
|
|
6
|
+
import { IntrinsicException } from "@nestjs/common/exceptions/intrinsic.exception.js";
|
|
7
|
+
import { Observable } from "rxjs";
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/auth-errors.d.ts
|
|
10
|
+
type AuthErrorCode = "UNAUTHENTICATED" | "FORBIDDEN" | "RATE_LIMITED";
|
|
11
|
+
/** A transport-neutral denial. Only transports make it throwable. */
|
|
12
|
+
interface AuthFailure {
|
|
13
|
+
readonly status: 401 | 403 | 429;
|
|
14
|
+
readonly code: AuthErrorCode;
|
|
15
|
+
readonly reason?: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly challenge?: string;
|
|
18
|
+
readonly headers?: Headers;
|
|
19
|
+
}
|
|
20
|
+
interface AuthErrorBody {
|
|
21
|
+
statusCode: 401 | 403 | 429;
|
|
22
|
+
error: string;
|
|
23
|
+
code: AuthErrorCode;
|
|
24
|
+
reason?: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
interface AuthGraphqlExtensions {
|
|
28
|
+
code: AuthErrorCode | "INTERNAL_SERVER_ERROR";
|
|
29
|
+
reason?: string;
|
|
30
|
+
statusCode: number;
|
|
31
|
+
}
|
|
32
|
+
interface AuthTransportErrorPayload {
|
|
33
|
+
status: "error";
|
|
34
|
+
statusCode: number;
|
|
35
|
+
code: AuthErrorCode;
|
|
36
|
+
reason?: string;
|
|
37
|
+
message: string;
|
|
38
|
+
}
|
|
39
|
+
interface ErrorMappingOptions {
|
|
40
|
+
map?: (failure: AuthFailure, context: ExecutionContext, transport: string) => unknown;
|
|
41
|
+
exposeRawCause?: boolean;
|
|
42
|
+
}
|
|
43
|
+
declare function isAuthFailure(value: unknown): value is AuthFailure;
|
|
44
|
+
declare function isInfrastructureError(value: unknown): value is BetterAuthInfrastructureError;
|
|
45
|
+
declare function isConfigurationError(value: unknown): value is BetterAuthConfigurationError;
|
|
46
|
+
declare function rejection(input: {
|
|
47
|
+
status: 401 | 403 | 429;
|
|
48
|
+
reason?: string;
|
|
49
|
+
message?: string;
|
|
50
|
+
challenge?: string;
|
|
51
|
+
retryAfterSeconds?: number;
|
|
52
|
+
}): AuthFailure;
|
|
53
|
+
declare const AuthFailures: {
|
|
54
|
+
unauthenticated(message?: string): AuthFailure;
|
|
55
|
+
rejected: typeof rejection;
|
|
56
|
+
forbidden(reason: string, message?: string): AuthFailure;
|
|
57
|
+
fromAPIError(error: unknown): AuthFailure | null;
|
|
58
|
+
};
|
|
59
|
+
declare class BetterAuthConfigurationError extends Error {
|
|
60
|
+
readonly code: string;
|
|
61
|
+
readonly detail: string;
|
|
62
|
+
readonly site?: string;
|
|
63
|
+
readonly hint?: string;
|
|
64
|
+
readonly phase: "boot" | "request";
|
|
65
|
+
readonly extensions: {
|
|
66
|
+
readonly code: "INTERNAL_SERVER_ERROR";
|
|
67
|
+
readonly reason: "AUTH_MISCONFIGURED";
|
|
68
|
+
readonly statusCode: 500;
|
|
69
|
+
};
|
|
70
|
+
readonly issues?: readonly BetterAuthConfigurationError[];
|
|
71
|
+
constructor(code: string, detail: string, hint?: string);
|
|
72
|
+
static atRequest(code: string, detail: string, options?: {
|
|
73
|
+
site?: string;
|
|
74
|
+
hint?: string;
|
|
75
|
+
}): BetterAuthConfigurationError;
|
|
76
|
+
}
|
|
77
|
+
declare class BetterAuthInfrastructureError extends Error {
|
|
78
|
+
readonly cause: Error;
|
|
79
|
+
readonly extensions: {
|
|
80
|
+
readonly code: "INTERNAL_SERVER_ERROR";
|
|
81
|
+
readonly reason: "AUTH_UNAVAILABLE";
|
|
82
|
+
readonly statusCode: 500;
|
|
83
|
+
};
|
|
84
|
+
constructor(cause: unknown, options?: {
|
|
85
|
+
secrets?: readonly string[];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
declare function getRawCause(error: BetterAuthInfrastructureError): unknown;
|
|
89
|
+
declare namespace auth_tokens_d_exports {
|
|
90
|
+
export { ACCEPT_PRINCIPALS_METADATA, ACCESS_METADATA, AUTH_ENHANCER, AUTH_INSTANCE_METADATA, DB_HOOK_METADATA, FORWARD_COOKIES_METADATA, FRESHNESS_METADATA, GUARD_CORE, HOOK_METADATA, INSTANCE_REGISTRY, INVOCATION_PARAMS_METADATA, INVOCATION_VALUES, MOUNT_COORDINATOR, PLATFORMS, POLICY_INVOKER, POLICY_RESOLVER, PRINCIPAL_PARAMS_METADATA, PRINCIPAL_READINGS, PRINCIPAL_RESOLVER, READER_CONTEXT, REQUEST_SCOPE, REQUIREMENTS_METADATA, ROUTE_PLANNER, SCOPE_CORE, SKIP_DEFAULT_REQUIREMENTS_METADATA, SKIP_ORIGIN_CHECK_METADATA, TRANSPORTS, TRANSPORT_REGISTRY, USE_BETTER_AUTH_METADATA, getBetterAuthHandleToken, getBetterAuthInstanceToken, getBetterAuthOptionsToken, getBetterAuthServiceToken, getExtensionToken, getPrincipalSourcesToken };
|
|
91
|
+
}
|
|
92
|
+
declare function getBetterAuthInstanceToken(alias?: string): string;
|
|
93
|
+
declare function getBetterAuthOptionsToken(alias?: string): string;
|
|
94
|
+
declare function getBetterAuthServiceToken(alias?: string): string;
|
|
95
|
+
declare function getBetterAuthHandleToken(alias?: string): string;
|
|
96
|
+
declare const INSTANCE_REGISTRY: unique symbol;
|
|
97
|
+
declare const REQUEST_SCOPE: unique symbol;
|
|
98
|
+
declare const PRINCIPAL_READINGS: unique symbol;
|
|
99
|
+
declare const ROUTE_PLANNER: unique symbol;
|
|
100
|
+
declare const TRANSPORT_REGISTRY: unique symbol;
|
|
101
|
+
declare const POLICY_RESOLVER: unique symbol;
|
|
102
|
+
declare const MOUNT_COORDINATOR: unique symbol;
|
|
103
|
+
declare const GUARD_CORE: unique symbol;
|
|
104
|
+
declare const SCOPE_CORE: unique symbol;
|
|
105
|
+
declare const PRINCIPAL_RESOLVER: unique symbol;
|
|
106
|
+
declare const POLICY_INVOKER: unique symbol;
|
|
107
|
+
declare const PLATFORMS: unique symbol;
|
|
108
|
+
declare const TRANSPORTS: unique symbol;
|
|
109
|
+
declare const ACCESS_METADATA: unique symbol;
|
|
110
|
+
declare const ACCEPT_PRINCIPALS_METADATA: unique symbol;
|
|
111
|
+
declare const REQUIREMENTS_METADATA: unique symbol;
|
|
112
|
+
declare const SKIP_DEFAULT_REQUIREMENTS_METADATA: unique symbol;
|
|
113
|
+
declare const FRESHNESS_METADATA: unique symbol;
|
|
114
|
+
declare const AUTH_INSTANCE_METADATA: unique symbol;
|
|
115
|
+
declare const PRINCIPAL_PARAMS_METADATA: unique symbol;
|
|
116
|
+
declare const INVOCATION_PARAMS_METADATA: unique symbol;
|
|
117
|
+
declare const FORWARD_COOKIES_METADATA: unique symbol;
|
|
118
|
+
declare const SKIP_ORIGIN_CHECK_METADATA: unique symbol;
|
|
119
|
+
declare const USE_BETTER_AUTH_METADATA: unique symbol;
|
|
120
|
+
declare const HOOK_METADATA: unique symbol;
|
|
121
|
+
declare const DB_HOOK_METADATA: unique symbol;
|
|
122
|
+
declare const AUTH_ENHANCER: unique symbol;
|
|
123
|
+
declare function getPrincipalSourcesToken(instance: string): symbol;
|
|
124
|
+
declare function getExtensionToken(point: "platforms" | "transports" | "principals", instance: string, index: number): symbol;
|
|
125
|
+
declare const READER_CONTEXT: unique symbol;
|
|
126
|
+
declare const INVOCATION_VALUES: unique symbol;
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/auth-module.d.ts
|
|
129
|
+
declare class BetterAuthModule {
|
|
130
|
+
static forRoot<A extends AuthLike = RegisteredAuth>(options: BetterAuthModuleOptions<A> & BetterAuthAppOptions & {
|
|
131
|
+
name?: "default";
|
|
132
|
+
} & DefaultInstanceCheck<A>): DynamicModule;
|
|
133
|
+
static forRoot<A extends AuthLike>(options: BetterAuthModuleOptions<A> & {
|
|
134
|
+
name: string;
|
|
135
|
+
} & NoAppOptions): DynamicModule;
|
|
136
|
+
static forRootAsync<A extends AuthLike = RegisteredAuth>(options: BetterAuthModuleAsyncOptions<A> & BetterAuthAppOptions & {
|
|
137
|
+
name?: "default";
|
|
138
|
+
}): DynamicModule;
|
|
139
|
+
static forRootAsync<A extends AuthLike>(options: BetterAuthModuleAsyncOptions<A> & {
|
|
140
|
+
name: string;
|
|
141
|
+
} & NoAppOptions): DynamicModule;
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/bridge-protocol.d.ts
|
|
145
|
+
declare const EXTENSION_DEFINITION: unique symbol;
|
|
146
|
+
interface BridgeHandle {
|
|
147
|
+
readonly protocol: 4;
|
|
148
|
+
readonly clientIpHeader: string | null;
|
|
149
|
+
/** 'unbound' until the first bind(); 'bound' while an application's hooks are registered; 'closed' after its shutdown, hooks still registered. */
|
|
150
|
+
readonly state: "unbound" | "bound" | "closed";
|
|
151
|
+
/** Endpoint dispatches and database-hook dispatches (writes) seen before the first binding, reset by every bind() (B26). */
|
|
152
|
+
readonly unboundDispatches: number;
|
|
153
|
+
/**
|
|
154
|
+
* Registers the application's hooks and returns close(), which the kernel calls in onApplicationShutdown: a closed binding keeps
|
|
155
|
+
* dispatching until another application binds (§10.6). A second bind() from the same owner for the same instance is a no-op that
|
|
156
|
+
* returns the existing registration (NestMicroservice.init() runs lifecycle hooks twice); from the same owner for another instance it
|
|
157
|
+
* throws PLUGIN_SHARED_BETWEEN_INSTANCES (B03). Throws INSTANCE_ALREADY_BOUND if another bootstrapped application is bound; takes over
|
|
158
|
+
* a never-bootstrapped one (reported, B06) and a closed one (silently).
|
|
159
|
+
*/
|
|
160
|
+
bind(binding: BridgeBinding): {
|
|
161
|
+
close(): void;
|
|
162
|
+
tookOverFrom?: string;
|
|
163
|
+
};
|
|
164
|
+
/** true when an endpoint returned `value` (an object) in a non-router dispatch observed while bound; false for before-hook short-circuits (LEAD-V37). */
|
|
165
|
+
producedByEndpoint(value: unknown): boolean;
|
|
166
|
+
}
|
|
167
|
+
interface BridgeBinding {
|
|
168
|
+
/** The application: compared by identity for the same-owner no-op; `description` goes into the B06 message. */
|
|
169
|
+
readonly owner: {
|
|
170
|
+
readonly description: string;
|
|
171
|
+
};
|
|
172
|
+
/** The instance this plugin object serves; one plugin object serves one instance (B03). */
|
|
173
|
+
readonly instance: string;
|
|
174
|
+
/** 'initialized' at bind; the kernel sets 'bootstrapped' in onApplicationBootstrap; close() sets 'closed'. */
|
|
175
|
+
state: "initialized" | "bootstrapped" | "closed";
|
|
176
|
+
/** View of the kernel's RequestScope; the plugin never owns an AsyncLocalStorage. [C] */
|
|
177
|
+
current(): ScopeView | undefined;
|
|
178
|
+
/** Credential header names declared by the instance's principal sources (credential matching, §7.5). */
|
|
179
|
+
readonly credentialHeaders: readonly string[];
|
|
180
|
+
readonly before: readonly CompiledHook[];
|
|
181
|
+
readonly after: readonly CompiledHook[];
|
|
182
|
+
readonly database: Readonly<{ [E in DatabaseHookTarget]: {
|
|
183
|
+
readonly before: readonly DatabaseHookMethod<E, "before">[];
|
|
184
|
+
readonly after: readonly DatabaseHookMethod<E, "after">[];
|
|
185
|
+
}; }>;
|
|
186
|
+
/** Debug log when the bridge drops a direct call's cookies because its credential is foreign. */
|
|
187
|
+
onDropped(path: string): void;
|
|
188
|
+
}
|
|
189
|
+
interface ScopeView {
|
|
190
|
+
/** Cookie-capable forwarding handlers have already passed the enforcing form check on every method/operation (§7.10). */
|
|
191
|
+
readonly cookies: CookieSink | null;
|
|
192
|
+
readonly forward: "none" | "same-credential" | "any";
|
|
193
|
+
readonly inbound: (() => Headers) | undefined;
|
|
194
|
+
readonly internal: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Present in EVERY handler scope whose transport exposes a browser leg, unless the plan's origin check is 'off' — enforcing or not,
|
|
197
|
+
* so safe methods and GraphQL queries are covered too (§7.5). Returns when this leg has a passing origin verdict for `instance`, or
|
|
198
|
+
* the single kernel disable predicate (§7.10 item 4) holds; a guard run without a passing verdict is insufficient. Otherwise throws the kernel's
|
|
199
|
+
* BetterAuthConfigurationError PUBLIC_HANDLER_USED_CALLER_SESSION.
|
|
200
|
+
*/
|
|
201
|
+
readonly checkCallerSession?: (path: string, instance: string) => void;
|
|
202
|
+
/** The browser leg's OWN headers (TransportCall.browser.headers()), before any transport credential mapping replaced them; undefined
|
|
203
|
+
* when the transport exposes no browser leg. Entry 2 recognizes an ambiently carried session cookie with it. */
|
|
204
|
+
readonly browserHeaders: (() => Headers) | undefined;
|
|
205
|
+
}
|
|
206
|
+
interface CompiledHook {
|
|
207
|
+
/**
|
|
208
|
+
* Evaluate the user predicate once and let it throw. The dispatcher logs and
|
|
209
|
+
* converts before-hook matcher failures to APIError 500; after-hook matcher
|
|
210
|
+
* failures cross the next fixed SDK matcher boundary unchanged, so the SDK
|
|
211
|
+
* cannot recover them as handler APIErrors. Never swallow a failure as a
|
|
212
|
+
* non-match or evaluate the predicate again in run().
|
|
213
|
+
*/
|
|
214
|
+
matches(ctx: HookEndpointContext, scope: ScopeView | undefined): boolean;
|
|
215
|
+
run(ctx: HookEndpointContext): Promise<unknown>;
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/request-scope.d.ts
|
|
219
|
+
interface ScopeState {
|
|
220
|
+
readonly view: ScopeView;
|
|
221
|
+
readonly call?: TransportCall;
|
|
222
|
+
readonly plan?: RoutePlan;
|
|
223
|
+
readonly reading?: () => PrincipalReading;
|
|
224
|
+
}
|
|
225
|
+
interface RequestState {
|
|
226
|
+
readonly principal: Map<string, Promise<PrincipalResult>>;
|
|
227
|
+
readonly policyIo: Map<string, Promise<unknown>>;
|
|
228
|
+
readonly decisions: Map<string, Promise<AuthorizationDecision>>;
|
|
229
|
+
readonly values: Map<symbol, unknown>;
|
|
230
|
+
readonly authorizationCalls: Set<string>;
|
|
231
|
+
readonly origins: Map<string, Promise<AuthFailure | null>>;
|
|
232
|
+
readonly surfaced: Set<string>;
|
|
233
|
+
readonly connections: Map<string, {
|
|
234
|
+
value: Extract<PrincipalResult, {
|
|
235
|
+
outcome: "authenticated" | "absent";
|
|
236
|
+
}>;
|
|
237
|
+
expiresAt: number;
|
|
238
|
+
}>;
|
|
239
|
+
}
|
|
240
|
+
declare class RequestScope {
|
|
241
|
+
#private;
|
|
242
|
+
run<T>(state: ScopeState, fn: () => T): T;
|
|
243
|
+
exit<T>(fn: () => T): T;
|
|
244
|
+
current(): ScopeState | undefined;
|
|
245
|
+
stateFor(key: object): RequestState;
|
|
246
|
+
memoPrincipal(call: Pick<TransportCall, "key" | "connection" | "principalTtlMs">, input: {
|
|
247
|
+
instance: string;
|
|
248
|
+
freshness: "default" | "authoritative";
|
|
249
|
+
sourceSet: string;
|
|
250
|
+
}, compute: () => Promise<PrincipalResult>): Promise<PrincipalResult>;
|
|
251
|
+
memoPolicyIo<T>(request: object, instance: string, concreteKey: string, compute: () => Promise<T>, limit?: number | false): Promise<T>;
|
|
252
|
+
memoDecision(invocation: object, instance: string, concreteKey: string, compute: () => Promise<AuthorizationDecision>): Promise<AuthorizationDecision>;
|
|
253
|
+
valueKey(instance: string, key: symbol): symbol;
|
|
254
|
+
surfaced(key: object, error: object, context: {
|
|
255
|
+
instance: string;
|
|
256
|
+
site: string;
|
|
257
|
+
lineage?: InvocationLineage;
|
|
258
|
+
}): boolean;
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/origin-check.d.ts
|
|
262
|
+
/** Bounded, per-application diagnostics; the next denial rolls the window without timers. */
|
|
263
|
+
declare class OriginDiagnostics {
|
|
264
|
+
#private;
|
|
265
|
+
private readonly logger;
|
|
266
|
+
constructor(logger: {
|
|
267
|
+
warn(message: string): void;
|
|
268
|
+
debug(message: string): void;
|
|
269
|
+
});
|
|
270
|
+
record(instance: string, failure: AuthFailure, headers: Headers, trustedCount: number): void;
|
|
271
|
+
advisoryFailure(instance: string, headers: Headers, trustedCount: number): void;
|
|
272
|
+
}
|
|
273
|
+
interface OriginCheckInit {
|
|
274
|
+
readonly instance: string;
|
|
275
|
+
readonly context: AuthContextView;
|
|
276
|
+
readonly options?: OriginCheckOptions;
|
|
277
|
+
readonly diagnostics?: OriginDiagnostics;
|
|
278
|
+
readonly credentialHeaders?: readonly string[];
|
|
279
|
+
readonly exposeRawCause?: boolean;
|
|
280
|
+
}
|
|
281
|
+
declare class OriginCheck {
|
|
282
|
+
#private;
|
|
283
|
+
private readonly scope;
|
|
284
|
+
private readonly init;
|
|
285
|
+
constructor(scope: RequestScope, init: OriginCheckInit);
|
|
286
|
+
check(browser: BrowserExposure, mode: "cookie" | "form"): Promise<AuthFailure | null>;
|
|
287
|
+
advisory(browser: BrowserExposure): Promise<void>;
|
|
288
|
+
assertCallerSession(browser: BrowserExposure, path: string, instance: string): void;
|
|
289
|
+
private calculationFailures;
|
|
290
|
+
private verdict;
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/instance-registry.d.ts
|
|
294
|
+
interface InstanceEntry {
|
|
295
|
+
readonly name: string;
|
|
296
|
+
readonly instance: AuthLike;
|
|
297
|
+
readonly options: BetterAuthRuntimeOptions<AuthLike>;
|
|
298
|
+
readonly staticOptions: BetterAuthStaticOptions;
|
|
299
|
+
readonly sources: readonly PrincipalSource[];
|
|
300
|
+
readonly handle: AuthHandle;
|
|
301
|
+
readonly context: AuthContextView;
|
|
302
|
+
readonly origin: OriginCheck;
|
|
303
|
+
readonly bridge: BridgeHandle;
|
|
304
|
+
readonly credentialHeaders: readonly string[];
|
|
305
|
+
}
|
|
306
|
+
interface InstanceLookup {
|
|
307
|
+
get(name: string): InstanceEntry;
|
|
308
|
+
list(): readonly InstanceEntry[];
|
|
309
|
+
}
|
|
310
|
+
interface InstanceRegistration {
|
|
311
|
+
readonly name: string;
|
|
312
|
+
readonly instance: AuthLike;
|
|
313
|
+
readonly options: BetterAuthRuntimeOptions<AuthLike>;
|
|
314
|
+
readonly staticOptions: BetterAuthStaticOptions;
|
|
315
|
+
readonly appOptions: BetterAuthAppOptions;
|
|
316
|
+
readonly sources: readonly PrincipalSource[];
|
|
317
|
+
readonly handle: AuthHandle;
|
|
318
|
+
}
|
|
319
|
+
declare class InstanceRegistry implements InstanceLookup {
|
|
320
|
+
#private;
|
|
321
|
+
private readonly scope;
|
|
322
|
+
readonly logger: Logger;
|
|
323
|
+
readonly diagnostics: OriginDiagnostics;
|
|
324
|
+
state: "new" | "initialized" | "bootstrapped";
|
|
325
|
+
adapter: AbstractHttpAdapter | null;
|
|
326
|
+
owner: {
|
|
327
|
+
description: string;
|
|
328
|
+
};
|
|
329
|
+
constructor(scope: RequestScope);
|
|
330
|
+
register(options: BetterAuthRuntimeOptions<AuthLike>, staticOptions: BetterAuthStaticOptions, sources: readonly PrincipalSource[], appOptions: BetterAuthAppOptions): InstanceRegistration;
|
|
331
|
+
registrations(): readonly InstanceRegistration[];
|
|
332
|
+
get(name: string): InstanceEntry;
|
|
333
|
+
list(): readonly InstanceEntry[];
|
|
334
|
+
begin(adapter: AbstractHttpAdapter | null): boolean;
|
|
335
|
+
initialize(issues: BetterAuthConfigurationError[]): Promise<void>;
|
|
336
|
+
reset(): void;
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/mount-coordinator.d.ts
|
|
340
|
+
declare class MountCoordinator {
|
|
341
|
+
#private;
|
|
342
|
+
readonly host: HttpAdapterHost;
|
|
343
|
+
private readonly registry;
|
|
344
|
+
readonly logger: Logger;
|
|
345
|
+
constructor(host: HttpAdapterHost, registry: InstanceRegistry, scope: RequestScope);
|
|
346
|
+
registerPlatforms(platforms: readonly HttpPlatform[]): void;
|
|
347
|
+
get platform(): HttpPlatform | undefined;
|
|
348
|
+
get adapter(): AbstractHttpAdapter | null;
|
|
349
|
+
private tryPrepare;
|
|
350
|
+
prepareAtInit(issues: BetterAuthConfigurationError[]): void;
|
|
351
|
+
resolve(issues: BetterAuthConfigurationError[]): void;
|
|
352
|
+
route(pathname: string): AuthRouteBinding | undefined;
|
|
353
|
+
binding(name: string): AuthRouteBinding | undefined;
|
|
354
|
+
bindings(): readonly AuthRouteBinding[];
|
|
355
|
+
normalizeApplicationRoute(path: string): string;
|
|
356
|
+
registerApplicationRoutes(routes: readonly ApplicationRouteDescriptor[], issues: BetterAuthConfigurationError[]): void;
|
|
357
|
+
mount(): Promise<void>;
|
|
358
|
+
reset(): void;
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/principal-readings.d.ts
|
|
362
|
+
interface ReadingInput {
|
|
363
|
+
readonly args: readonly unknown[];
|
|
364
|
+
readonly call?: TransportCall;
|
|
365
|
+
readonly lineage?: InvocationLineage;
|
|
366
|
+
readonly plan?: Pick<RoutePlan, "access" | "instance" | "site">;
|
|
367
|
+
readonly beforeGuard?: boolean;
|
|
368
|
+
readonly site?: string;
|
|
369
|
+
}
|
|
370
|
+
/** The intrinsic twin suppresses Nest's repeated log, while retaining the safe wire shape. */
|
|
371
|
+
declare class RepeatedReaderError extends IntrinsicException {
|
|
372
|
+
constructor(error: BetterAuthConfigurationError);
|
|
373
|
+
}
|
|
374
|
+
declare class PrincipalReadings {
|
|
375
|
+
private readonly scope;
|
|
376
|
+
constructor(scope: RequestScope);
|
|
377
|
+
record(call: Pick<TransportCall, "invocation" | "lineage">, reading: PrincipalReading): void;
|
|
378
|
+
recordLineage(lineage: InvocationLineage, reading: PrincipalReading): void;
|
|
379
|
+
enclosing(lineage: InvocationLineage, args: readonly unknown[]): PrincipalReading | undefined;
|
|
380
|
+
read(input: ReadingInput): PrincipalReading;
|
|
381
|
+
current(): PrincipalReading;
|
|
382
|
+
project<T>(reading: PrincipalReading, spec: {
|
|
383
|
+
kind?: string;
|
|
384
|
+
reason?: string;
|
|
385
|
+
site: string;
|
|
386
|
+
project: (principal: AuthPrincipal) => T;
|
|
387
|
+
}, input?: ReadingInput): T | null;
|
|
388
|
+
deliver(error: BetterAuthConfigurationError, input?: ReadingInput, instance?: string): BetterAuthConfigurationError | RepeatedReaderError;
|
|
389
|
+
stamp(args: readonly unknown[], instance: string, result: PrincipalResult): void;
|
|
390
|
+
}
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/policy-resolver.d.ts
|
|
393
|
+
declare class PolicyResolver {
|
|
394
|
+
private readonly moduleRef;
|
|
395
|
+
private readonly cache;
|
|
396
|
+
constructor(moduleRef: ModuleRef);
|
|
397
|
+
resolve<P>(reference: PolicyRef<P>): AuthorizationPolicy<P, any>;
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/transport-registry.d.ts
|
|
401
|
+
declare class TransportRegistry {
|
|
402
|
+
private transports;
|
|
403
|
+
private readonly owners;
|
|
404
|
+
private kit;
|
|
405
|
+
register(transports: readonly AuthTransport[]): void;
|
|
406
|
+
setKit(kit: TransportKit): void;
|
|
407
|
+
list(): readonly AuthTransport[];
|
|
408
|
+
defaultAccessFor(target: Function, method: string): "inherit" | undefined;
|
|
409
|
+
find(context: ExecutionContext): AuthTransport | undefined;
|
|
410
|
+
select(context: ExecutionContext): AuthTransport;
|
|
411
|
+
idOf(call: TransportCall): string;
|
|
412
|
+
describe(context: ExecutionContext, transport?: AuthTransport): TransportCall;
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/route-planner.d.ts
|
|
416
|
+
declare class RoutePlanner {
|
|
417
|
+
private readonly instances;
|
|
418
|
+
private readonly policies;
|
|
419
|
+
private readonly transports;
|
|
420
|
+
private readonly cache;
|
|
421
|
+
constructor(instances: InstanceLookup, policies: PolicyResolver, transports: TransportRegistry);
|
|
422
|
+
private prepare;
|
|
423
|
+
requirementsOf(target: Type, method: string): readonly RequirementExpr[];
|
|
424
|
+
forContext(context: ExecutionContext): RoutePlan;
|
|
425
|
+
plan(target: Type, method: string): RoutePlan;
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/auth-service.d.ts
|
|
429
|
+
declare class BetterAuthService<A extends AuthLike = RegisteredAuth> {
|
|
430
|
+
#private;
|
|
431
|
+
private readonly registry;
|
|
432
|
+
private readonly scope;
|
|
433
|
+
private readonly readings;
|
|
434
|
+
private readonly planner;
|
|
435
|
+
private readonly transports;
|
|
436
|
+
private readonly coordinator;
|
|
437
|
+
readonly name: string;
|
|
438
|
+
readonly instance: A;
|
|
439
|
+
readonly api: A["api"];
|
|
440
|
+
constructor(options: BetterAuthRuntimeOptions<A>, registry: InstanceRegistry, scope: RequestScope, readings: PrincipalReadings, planner: RoutePlanner, transports: TransportRegistry, coordinator: MountCoordinator);
|
|
441
|
+
context(): Promise<Awaited<A["$context"]>>;
|
|
442
|
+
mount(): {
|
|
443
|
+
readonly basePath: string;
|
|
444
|
+
readonly bodyLimit: number;
|
|
445
|
+
readonly platform: string;
|
|
446
|
+
} | undefined;
|
|
447
|
+
getPrincipal(): Promise<AuthPrincipal | null>;
|
|
448
|
+
getSession(): Promise<SessionOf<A> | null>;
|
|
449
|
+
principalFor(context: ExecutionContext): PrincipalReading;
|
|
450
|
+
headersFrom(platformRequest: unknown): Headers;
|
|
451
|
+
forwardForeignCookies<T>(fn: () => Promise<T>): Promise<T>;
|
|
452
|
+
withoutCookies<T>(fn: () => Promise<T>): Promise<T>;
|
|
453
|
+
runOutsideScope<T>(fn: () => Promise<T>): Promise<T>;
|
|
454
|
+
}
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/authorization-evaluator.d.ts
|
|
457
|
+
declare const allow: () => AuthorizationDecision;
|
|
458
|
+
declare const deny: (input: {
|
|
459
|
+
reason: string;
|
|
460
|
+
status?: 401 | 403;
|
|
461
|
+
message?: string;
|
|
462
|
+
challenge?: string;
|
|
463
|
+
}) => AuthorizationDecision;
|
|
464
|
+
declare function definePolicy<P, K extends PrincipalKind = PrincipalKind>(policy: AuthorizationPolicy<P, PrincipalOfKind<K>>): (params: P, options?: RequirementOptions) => Requirement<P>;
|
|
465
|
+
declare class AuthorizationEvaluator {
|
|
466
|
+
private readonly policies;
|
|
467
|
+
private readonly invoker;
|
|
468
|
+
private readonly resolver;
|
|
469
|
+
private readonly scope;
|
|
470
|
+
private readonly keys;
|
|
471
|
+
constructor(policies: PolicyResolver, invoker: PolicyInvoker, resolver: PrincipalResolver, scope: RequestScope);
|
|
472
|
+
evaluate(plan: RoutePlan, principal: AuthPrincipal, call: TransportCall, entry: InstanceEntry, execution: ExecutionContext, transport: string): Promise<AuthorizationDecision>;
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/auth-guard.d.ts
|
|
476
|
+
declare class BetterAuthGuard implements CanActivate {
|
|
477
|
+
private readonly core;
|
|
478
|
+
static readonly [AUTH_ENHANCER] = "guard";
|
|
479
|
+
readonly [AUTH_ENHANCER] = "guard";
|
|
480
|
+
constructor(core: Pick<GuardCore, "canActivate">);
|
|
481
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
482
|
+
}
|
|
483
|
+
declare class GuardCore implements CanActivate {
|
|
484
|
+
private readonly instances;
|
|
485
|
+
private readonly planner;
|
|
486
|
+
private readonly transports;
|
|
487
|
+
private readonly resolver;
|
|
488
|
+
private readonly evaluator;
|
|
489
|
+
private readonly scope;
|
|
490
|
+
private readonly readings;
|
|
491
|
+
constructor(instances: InstanceLookup, planner: RoutePlanner, transports: TransportRegistry, resolver: PrincipalResolver, evaluator: AuthorizationEvaluator, scope: RequestScope, readings: PrincipalReadings);
|
|
492
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/auth-scope-interceptor.d.ts
|
|
496
|
+
declare class BetterAuthScopeInterceptor implements NestInterceptor {
|
|
497
|
+
private readonly core;
|
|
498
|
+
static readonly [AUTH_ENHANCER] = "scope";
|
|
499
|
+
readonly [AUTH_ENHANCER] = "scope";
|
|
500
|
+
constructor(core: Pick<ScopeCore, "intercept">);
|
|
501
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
502
|
+
}
|
|
503
|
+
declare class ScopeCore implements NestInterceptor {
|
|
504
|
+
private readonly instances;
|
|
505
|
+
private readonly planner;
|
|
506
|
+
private readonly transports;
|
|
507
|
+
private readonly scope;
|
|
508
|
+
private readonly readings;
|
|
509
|
+
constructor(instances: InstanceLookup, planner: RoutePlanner, transports: TransportRegistry, scope: RequestScope, readings: PrincipalReadings);
|
|
510
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
511
|
+
}
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/auth-module-definition.d.ts
|
|
514
|
+
declare function defineExtension<T>(definition: Omit<ExtensionDefinition<T>, typeof EXTENSION_DEFINITION>): ExtensionDefinition<T>;
|
|
515
|
+
declare function defineHttpPlatform<P extends HttpPlatform>(platform: P): P;
|
|
516
|
+
declare function defineTransport<T extends AuthTransport>(transport: T): T;
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region src/auth-decorators.d.ts
|
|
519
|
+
declare const Public: () => import("@nestjs/common").CustomDecorator<typeof ACCESS_METADATA>;
|
|
520
|
+
declare const OptionalAuth: () => import("@nestjs/common").CustomDecorator<typeof ACCESS_METADATA>;
|
|
521
|
+
declare const RequireAuth: (options?: {
|
|
522
|
+
authoritative?: boolean;
|
|
523
|
+
}) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
524
|
+
declare const UseAuthInstance: (name: string) => import("@nestjs/common").CustomDecorator<typeof AUTH_INSTANCE_METADATA>;
|
|
525
|
+
declare const AcceptPrincipals: (...kinds: readonly [PrincipalKind, ...PrincipalKind[]]) => import("@nestjs/common").CustomDecorator<typeof ACCEPT_PRINCIPALS_METADATA>;
|
|
526
|
+
declare const SkipDefaultRequirements: () => import("@nestjs/common").CustomDecorator<typeof SKIP_DEFAULT_REQUIREMENTS_METADATA>;
|
|
527
|
+
declare const ForwardAuthCookies: (enabled?: boolean) => import("@nestjs/common").CustomDecorator<typeof FORWARD_COOKIES_METADATA>;
|
|
528
|
+
declare const SkipOriginCheck: () => import("@nestjs/common").CustomDecorator<typeof SKIP_ORIGIN_CHECK_METADATA>;
|
|
529
|
+
declare const UseBetterAuth: () => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
530
|
+
declare function Require(...requirements: readonly RequirementExpr[]): ClassDecorator & MethodDecorator;
|
|
531
|
+
declare const anyOf: (...requirements: readonly RequirementExpr[]) => RequirementExpr;
|
|
532
|
+
declare const allOf: (...requirements: readonly RequirementExpr[]) => RequirementExpr;
|
|
533
|
+
declare const requirement: <P>(policy: PolicyRef<P>, params: P, options?: RequirementOptions) => Requirement<P>;
|
|
534
|
+
declare const CurrentPrincipal: () => ParameterDecorator;
|
|
535
|
+
declare function definePrincipalParam<K extends PrincipalKind, R>(spec: {
|
|
536
|
+
readonly kind: K;
|
|
537
|
+
readonly reason: string;
|
|
538
|
+
readonly project: (principal: PrincipalOfKind<K>) => R;
|
|
539
|
+
}): () => ParameterDecorator;
|
|
540
|
+
declare function defineInvocationParam(slot: symbol, spec: {
|
|
541
|
+
readonly missing: string;
|
|
542
|
+
}): () => ParameterDecorator;
|
|
543
|
+
declare const BeforeAuth: <const P extends EndpointPath | (string & {})>(match?: P | readonly P[] | HookPredicate, options?: HookOptions) => HookMethodDecorator<P>;
|
|
544
|
+
declare const AfterAuth: <const P extends EndpointPath | (string & {})>(match?: P | readonly P[] | HookPredicate, options?: HookOptions) => HookMethodDecorator<P>;
|
|
545
|
+
declare const BeforeDatabase: <E extends DatabaseHookTarget>(target: E, options?: DbHookOptions) => DbHookMethodDecorator<E, "before">;
|
|
546
|
+
declare const AfterDatabase: <E extends DatabaseHookTarget>(target: E, options?: DbHookOptions) => DbHookMethodDecorator<E, "after">;
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/session-principal.d.ts
|
|
549
|
+
declare const SESSION_PRINCIPAL_KIND: "session";
|
|
550
|
+
declare const CurrentSession: () => ParameterDecorator;
|
|
551
|
+
declare const CurrentUser: () => ParameterDecorator;
|
|
552
|
+
declare function sessionPrincipal<S = AuthSession>(options?: SessionPrincipalOptions<S>): PrincipalSource<SessionPrincipal<S>>;
|
|
553
|
+
declare const freshSession: (options?: {
|
|
554
|
+
maxAgeSeconds?: number;
|
|
555
|
+
}) => Requirement<{
|
|
556
|
+
maxAgeSeconds?: number;
|
|
557
|
+
}>;
|
|
558
|
+
declare const RequireFreshSession: (options?: {
|
|
559
|
+
maxAgeSeconds?: number;
|
|
560
|
+
}) => ClassDecorator & MethodDecorator;
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/principal-resolver.d.ts
|
|
563
|
+
declare const definePrincipalSource: <P extends AuthPrincipalBase>(source: PrincipalSource<P>) => PrincipalSource<P>;
|
|
564
|
+
declare const authenticated: <P extends AuthPrincipalBase>(principal: P) => PrincipalResult<P>;
|
|
565
|
+
declare const absent: () => PrincipalResult<never>;
|
|
566
|
+
declare const rejected: (failure: AuthFailure) => PrincipalResult<never>;
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region src/auth-exchange.d.ts
|
|
569
|
+
/** Credentialed CORS for the auth mount, using the initialized SDK trusted origins. */
|
|
570
|
+
declare function betterAuthCorsOrigin(auth: AuthLike, options?: CorsOriginOptions): (origin: string | undefined, cb: (err: Error | null, allow?: boolean) => void) => void;
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/http-transport.d.ts
|
|
573
|
+
declare function httpTransport(): ExtensionRef<AuthTransport>;
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/index.d.ts
|
|
576
|
+
/** Augment this public interface to register an optional principal kind. */
|
|
577
|
+
interface PrincipalKinds {
|
|
578
|
+
session: SessionPrincipal;
|
|
579
|
+
}
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/auth-types.d.ts
|
|
582
|
+
/** Structural minimum; accepts any betterAuth() result incl. plugins and customSession (EXP-C2, EXP-A:types/). */
|
|
583
|
+
interface AuthLike {
|
|
584
|
+
handler(request: Request): Promise<Response>;
|
|
585
|
+
api: {
|
|
586
|
+
getSession(ctx: {
|
|
587
|
+
headers: Headers;
|
|
588
|
+
query?: {
|
|
589
|
+
disableCookieCache?: boolean;
|
|
590
|
+
disableRefresh?: boolean;
|
|
591
|
+
};
|
|
592
|
+
returnHeaders?: boolean;
|
|
593
|
+
}): Promise<unknown>;
|
|
594
|
+
};
|
|
595
|
+
$context: Promise<unknown>;
|
|
596
|
+
}
|
|
597
|
+
/** What getSession({ returnHeaders: true }) resolves to. `headers` is undefined when a before-hook short-circuited the call (LEAD-V37). */
|
|
598
|
+
interface GetSessionWithHeaders {
|
|
599
|
+
readonly headers?: Headers | null;
|
|
600
|
+
readonly response: unknown;
|
|
601
|
+
}
|
|
602
|
+
/** Augment this interface with the application auth instance and named instances. */
|
|
603
|
+
interface Register {}
|
|
604
|
+
type IsRegistered = Register extends {
|
|
605
|
+
auth: AuthLike;
|
|
606
|
+
} ? true : false;
|
|
607
|
+
type RegisteredAuth = Register extends {
|
|
608
|
+
auth: infer A extends AuthLike;
|
|
609
|
+
} ? A : AuthLike;
|
|
610
|
+
type RegisteredInstances = Register extends {
|
|
611
|
+
instances: infer I extends Record<string, AuthLike>;
|
|
612
|
+
} ? I : Record<never, never>;
|
|
613
|
+
type AuthOf<N extends string = "default"> = N extends "default" ? RegisteredAuth : N extends keyof RegisteredInstances ? RegisteredInstances[N] : AuthLike;
|
|
614
|
+
/** Unregistered fallback = better-auth's default core shape (Session/User are exported by better-auth, EXP-C11). */
|
|
615
|
+
type DefaultSession = {
|
|
616
|
+
session: import("better-auth").Session;
|
|
617
|
+
user: import("better-auth").User;
|
|
618
|
+
};
|
|
619
|
+
type SessionOf<A> = A extends {
|
|
620
|
+
$Infer: {
|
|
621
|
+
Session: infer S;
|
|
622
|
+
};
|
|
623
|
+
} ? NonNullable<S> : DefaultSession;
|
|
624
|
+
type UserOf<A> = SessionOf<A> extends {
|
|
625
|
+
user: infer U;
|
|
626
|
+
} ? U : never;
|
|
627
|
+
type AuthSession<N extends string = "default"> = SessionOf<AuthOf<N>>;
|
|
628
|
+
type AuthUser<N extends string = "default"> = UserOf<AuthOf<N>>;
|
|
629
|
+
type HasUserId<S> = S extends {
|
|
630
|
+
user: {
|
|
631
|
+
id: string;
|
|
632
|
+
};
|
|
633
|
+
} ? true : false;
|
|
634
|
+
type BodyOf<F> = F extends ((ctx: infer C) => unknown) ? [NonNullable<C>] extends [{
|
|
635
|
+
body?: infer B;
|
|
636
|
+
}] ? NonNullable<B> : never : never;
|
|
637
|
+
type PermissionsOf<A, K extends string> = A extends {
|
|
638
|
+
api: { [k in K]: infer F; };
|
|
639
|
+
} ? BodyOf<F> extends {
|
|
640
|
+
permissions?: infer P;
|
|
641
|
+
} ? NonNullable<P> : never : never;
|
|
642
|
+
type AdminPermissions<N extends string = "default"> = IsRegistered extends true ? PermissionsOf<AuthOf<N>, "userHasPermission"> : Record<string, readonly string[]>;
|
|
643
|
+
type OrgPermissions<N extends string = "default"> = IsRegistered extends true ? PermissionsOf<AuthOf<N>, "hasPermission"> : Record<string, readonly string[]>;
|
|
644
|
+
type ApiOf<A> = A extends {
|
|
645
|
+
api: infer Api;
|
|
646
|
+
} ? Api : never;
|
|
647
|
+
type EndpointPath<A = RegisteredAuth> = { [K in keyof ApiOf<A>]: ApiOf<A>[K] extends {
|
|
648
|
+
path: infer P extends string;
|
|
649
|
+
} ? P : never; }[keyof ApiOf<A>];
|
|
650
|
+
type EndpointAt<P extends string, A = RegisteredAuth> = { [K in keyof ApiOf<A>]: ApiOf<A>[K] extends {
|
|
651
|
+
path: P;
|
|
652
|
+
} ? ApiOf<A>[K] : never; }[keyof ApiOf<A>];
|
|
653
|
+
type BodyAt<P extends string> = [EndpointAt<P>] extends [never] ? unknown : BodyOf<EndpointAt<P>>;
|
|
654
|
+
/**
|
|
655
|
+
* The RAW body a hook sees: the endpoint's schema has not run yet (before-hooks) or ran on the same raw input (after-hooks).
|
|
656
|
+
* Known keys autocomplete; every value is `unknown` until the hook checks it.
|
|
657
|
+
*/
|
|
658
|
+
type UnvalidatedBody<B> = [B] extends [never] ? unknown : B extends object ? { readonly [K in keyof B]?: unknown; } & Readonly<Record<string, unknown>> : unknown;
|
|
659
|
+
type AuthHookContext<P extends string = string> = Omit<import("better-auth").HookEndpointContext, "path" | "body"> & {
|
|
660
|
+
path: P;
|
|
661
|
+
body: string extends P ? unknown : UnvalidatedBody<BodyAt<P>>;
|
|
662
|
+
};
|
|
663
|
+
type AuthAfterHookContext<P extends string = string> = AuthHookContext<P>;
|
|
664
|
+
type DbHooks = NonNullable<import("better-auth").BetterAuthOptions["databaseHooks"]>;
|
|
665
|
+
type DbHookFn<E extends DatabaseHookTarget, Ph extends "before" | "after"> = E extends `${infer M extends keyof DbHooks & string}.${infer O extends "create" | "update" | "delete"}` ? NonNullable<NonNullable<NonNullable<DbHooks[M]>[O]>[Ph]> : never;
|
|
666
|
+
/** Best effort: the registered instance's additional user/session fields, Partial for updates; nothing when not derivable. */
|
|
667
|
+
type RegisteredRow<M> = M extends "user" ? UserOf<RegisteredAuth> : M extends "session" ? SessionOf<RegisteredAuth> extends {
|
|
668
|
+
session: infer S;
|
|
669
|
+
} ? S : never : never;
|
|
670
|
+
type ExtraFields<E extends DatabaseHookTarget> = E extends `${infer M}.${infer O}` ? [RegisteredRow<M>] extends [never] ? Record<never, never> : O extends "update" ? Partial<RegisteredRow<M>> : RegisteredRow<M> : Record<never, never>;
|
|
671
|
+
/** The payload better-auth passes: e.g. update.before → Partial<User> & Record<string, unknown>. */
|
|
672
|
+
type DatabaseHookData<E extends DatabaseHookTarget, Ph extends "before" | "after" = "before"> = Parameters<DbHookFn<E, Ph>>[0] & ExtraFields<E>;
|
|
673
|
+
/** What better-auth accepts back: create/update.before → boolean | void | { data }; delete.before → boolean | void; after → void. */
|
|
674
|
+
type DatabaseHookResult<E extends DatabaseHookTarget, Ph extends "before" | "after"> = Awaited<ReturnType<DbHookFn<E, Ph>>>;
|
|
675
|
+
type DatabaseHookMethod<E extends DatabaseHookTarget, Ph extends "before" | "after"> = (data: DatabaseHookData<E, Ph>, ctx: import("better-auth").GenericEndpointContext | null | undefined) => DatabaseHookResult<E, Ph> | Promise<DatabaseHookResult<E, Ph>>;
|
|
676
|
+
/** Guard-established invariant helper (reference #163). Prefer @ActiveOrganizationId() (§8.3). */
|
|
677
|
+
type WithActiveOrganization<S> = S & {
|
|
678
|
+
session: {
|
|
679
|
+
activeOrganizationId: string;
|
|
680
|
+
};
|
|
681
|
+
};
|
|
682
|
+
type PrincipalKind = keyof PrincipalKinds & string;
|
|
683
|
+
type AuthPrincipal = PrincipalKinds[PrincipalKind];
|
|
684
|
+
type PrincipalOfKind<K extends PrincipalKind> = PrincipalKinds[K];
|
|
685
|
+
interface SessionPrincipal<S = AuthSession> extends AuthPrincipalBase {
|
|
686
|
+
readonly kind: "session";
|
|
687
|
+
readonly session: S;
|
|
688
|
+
}
|
|
689
|
+
//#endregion
|
|
690
|
+
//#region src/auth-contracts.d.ts
|
|
691
|
+
interface BetterAuthAppOptions {
|
|
692
|
+
/** HTTP platforms. Exactly one must support the running HTTP adapter when the app has one. Default []. */
|
|
693
|
+
platforms?: readonly ExtensionRef<HttpPlatform>[];
|
|
694
|
+
/** Transports, consulted in order BEFORE the built-in httpTransport(). Default []. */
|
|
695
|
+
transports?: readonly ExtensionRef<AuthTransport>[];
|
|
696
|
+
}
|
|
697
|
+
type NoAppOptions = {
|
|
698
|
+
readonly platforms?: never;
|
|
699
|
+
readonly transports?: never;
|
|
700
|
+
};
|
|
701
|
+
/** Instance-shape options: they decide which providers exist, so they are fixed at definition time. [A][B][C] */
|
|
702
|
+
interface BetterAuthStaticOptions {
|
|
703
|
+
/** Instance name. Default 'default'. Named instances get their own tokens and mount (§5.5). */
|
|
704
|
+
name?: string;
|
|
705
|
+
/** Register the module as global. Default true. */
|
|
706
|
+
isGlobal?: boolean;
|
|
707
|
+
/**
|
|
708
|
+
* Register BetterAuthGuard as APP_GUARD (via useExisting). Default true for the default instance, false for named instances.
|
|
709
|
+
* Since v6 this no longer controls APP_INTERCEPTOR: see globalScope.
|
|
710
|
+
*/
|
|
711
|
+
globalGuard?: boolean;
|
|
712
|
+
/**
|
|
713
|
+
* Register BetterAuthScopeInterceptor as APP_INTERCEPTOR (via useExisting). Default true for the DEFAULT instance, whatever
|
|
714
|
+
* globalGuard says; named instances never register it (one scope opener per app). The interceptor denies nothing: it opens the
|
|
715
|
+
* scope that carries the cookie sink, refresh suppression and the caller-session check (§7.5, ADR-70). Setting it false is an
|
|
716
|
+
* explicit opt-out, printed in the boot summary and warned about (W_NO_GLOBAL_SCOPE, §5.4 B31). [§17 Q38]
|
|
717
|
+
*/
|
|
718
|
+
globalScope?: boolean;
|
|
719
|
+
/** Principal sources of this instance, tried in order BEFORE the built-in session source, filtered per route by the kinds it accepts (§7.6). Default []. */
|
|
720
|
+
principals?: readonly ExtensionRef<PrincipalSource>[];
|
|
721
|
+
}
|
|
722
|
+
/** Runtime options: read by providers after DI resolution, so they may come from useFactory. */
|
|
723
|
+
type BetterAuthRuntimeOptions<A extends AuthLike = RegisteredAuth> = {
|
|
724
|
+
/** The object returned by betterAuth(). Never wrapped, cloned or mutated. */
|
|
725
|
+
auth: A;
|
|
726
|
+
/** Access for handlers without an access decorator, unless a transport supplies the default (field resolvers inherit, §7.6). Default 'authenticated'. [C] */
|
|
727
|
+
defaultAccess?: "authenticated" | "public";
|
|
728
|
+
/**
|
|
729
|
+
* Requirements every 'required' plan of this instance carries, ahead of its class and method requirements: a company-wide
|
|
730
|
+
* rule such as orgMember(). Compiled into the plan, so boot validation, coverage, the guard and the decorators see one plan.
|
|
731
|
+
* Replaces v2's protected BetterAuthGuard.planFor seam (§7.6). A handler or controller opts out visibly with
|
|
732
|
+
* @SkipDefaultRequirements() (counted in the boot summary), and a plan whose AND-ed requirements admit no common principal kind
|
|
733
|
+
* fails boot (B15 UNSATISFIABLE_PRINCIPAL_KINDS). Default [].
|
|
734
|
+
*/
|
|
735
|
+
defaultRequirements?: readonly RequirementExpr[];
|
|
736
|
+
/** HTTP mount options for this instance. */
|
|
737
|
+
http?: HttpMountOptions;
|
|
738
|
+
/** Set-Cookie forwarding for application-level direct auth.api calls (§7.5). */
|
|
739
|
+
cookies?: CookieForwardingOptions;
|
|
740
|
+
/** better-auth's origin rule on cookie-carrying app operations, and its form-CSRF rule on handlers that forward auth cookies (§7.10). */
|
|
741
|
+
originCheck?: OriginCheckOptions;
|
|
742
|
+
/** Per-instance override of transport error objects for this instance's plans, and error logging (§13.5). */
|
|
743
|
+
errors?: ErrorMappingOptions;
|
|
744
|
+
/** Bounds on the authorization work one logical request can cause (§8.1). */
|
|
745
|
+
limits?: AuthorizationLimits;
|
|
746
|
+
/** Log the one-line boot summary. Default true. */
|
|
747
|
+
logSummary?: boolean;
|
|
748
|
+
} & SessionOption<A>;
|
|
749
|
+
interface AuthorizationLimits {
|
|
750
|
+
/**
|
|
751
|
+
* Distinct better-auth calls that policies may make through AuthorizationContext.memo for one logical request (memo hits do
|
|
752
|
+
* not count). Past it, a requirement denies 429 TOO_MANY_AUTHORIZATION_CHECKS instead of calling better-auth, so N aliased
|
|
753
|
+
* GraphQL fields with N different organizations cannot exhaust the connection pool. false disables the bound. Default 100.
|
|
754
|
+
*/
|
|
755
|
+
maxAuthorizationCallsPerRequest?: number | false;
|
|
756
|
+
}
|
|
757
|
+
interface CookieForwardingOptions {
|
|
758
|
+
/**
|
|
759
|
+
* Forward Set-Cookie produced by direct auth.api.* calls that application code makes inside a handler, when the call
|
|
760
|
+
* carries the caller's own credential or none. Default false, which is better-auth's server-call semantics
|
|
761
|
+
* (a server call never touches the browser unless you ask). Per handler: @ForwardAuthCookies(). The library's own calls
|
|
762
|
+
* (principal sources and policies) always forward. Handlers with forwarding on enforce better-auth's form-CSRF origin rule on EVERY
|
|
763
|
+
* method and operation kind, including safe cookie-free GET/query calls (§7.10), so turning this on instance-wide applies it everywhere. v3's per-call-site opt-in is gone: it forwarded
|
|
764
|
+
* without the form check (§7.5).
|
|
765
|
+
*/
|
|
766
|
+
forwardDirectCalls?: boolean;
|
|
767
|
+
}
|
|
768
|
+
interface OriginCheckOptions {
|
|
769
|
+
/**
|
|
770
|
+
* 'cookie' (default): an unsafe operation whose browser leg carries a cookie must come from a trusted origin, whatever credential
|
|
771
|
+
* authenticated it, exactly as on better-auth's routes (§7.10). 'off': disabled (B19 warns in production).
|
|
772
|
+
*/
|
|
773
|
+
mode?: "cookie" | "off";
|
|
774
|
+
/**
|
|
775
|
+
* A cookie-authenticated unsafe operation without Origin or Referer.
|
|
776
|
+
* 'reject' (default): 403 MISSING_OR_NULL_ORIGIN, exactly as better-auth's own routes answer (LEAD-V16).
|
|
777
|
+
* 'allow-non-browser': allowed when Origin, Referer and Sec-Fetch-Site are all absent (no browser sends such a request cross-site).
|
|
778
|
+
* Native clients that send the session cookie themselves (better-auth's Expo client on your own routes, SSR servers) send none of
|
|
779
|
+
* them; under 'reject' they need an Origin header. See §17 Q15.
|
|
780
|
+
*/
|
|
781
|
+
missingOrigin?: "reject" | "allow-non-browser";
|
|
782
|
+
}
|
|
783
|
+
/** `session` is REQUIRED (with userId) when the session shape lacks user.id, i.e. customSession (§11.5). [C] */
|
|
784
|
+
type SessionOption<A extends AuthLike> = HasUserId<SessionOf<A>> extends true ? {
|
|
785
|
+
session?: SessionPrincipalOptions<SessionOf<A>> | false;
|
|
786
|
+
} : {
|
|
787
|
+
session: (SessionPrincipalOptions<SessionOf<A>> & {
|
|
788
|
+
userId: (s: SessionOf<A>) => string | null;
|
|
789
|
+
}) | false;
|
|
790
|
+
};
|
|
791
|
+
interface SessionPrincipalOptions<S> {
|
|
792
|
+
/** Map a (possibly customSession-shaped) session to its user id. Default s => s.user?.id ?? null. */
|
|
793
|
+
userId?: (session: S) => string | null;
|
|
794
|
+
/** Default freshness of identity reads; per route: @RequireAuth({ authoritative: true }). Default 'default'. */
|
|
795
|
+
freshness?: "default" | "authoritative";
|
|
796
|
+
/**
|
|
797
|
+
* Does any apiKey() configuration of this instance set enableSessionForAPIKeys? The api-key plugin object does not expose
|
|
798
|
+
* its configuration, so no unit can read it (§8.3.1). Unset (default): when the api-key plugin is present, the session
|
|
799
|
+
* unit's boot advice W_API_KEY_FULL_SESSION / W_API_KEY_SESSION_MULTIPLIER is worded conditionally. false: you assert
|
|
800
|
+
* that none does, and the advice is dropped. true: the advice is worded as a fact.
|
|
801
|
+
*/
|
|
802
|
+
apiKeySessions?: boolean;
|
|
803
|
+
}
|
|
804
|
+
interface HttpMountOptions {
|
|
805
|
+
/** Mount better-auth routes on the platform. Default true. false = guard-only service. */
|
|
806
|
+
mount?: boolean;
|
|
807
|
+
/** Maximum auth-route request body. Default 1_048_576 (1 MiB). 413 above. */
|
|
808
|
+
bodyLimit?: number | `${number}${"b" | "kb" | "mb"}`;
|
|
809
|
+
/** Wrap every auth-route exchange (ORM request context, tracing). Outermost first. [A][C] */
|
|
810
|
+
around?: readonly AuthHandlerInterceptor[];
|
|
811
|
+
/** Allow a mount path of '/'. Default false (a root mount captures every unmatched route). [A] */
|
|
812
|
+
allowRootMount?: boolean;
|
|
813
|
+
/**
|
|
814
|
+
* Allow an unset baseURL in production (production = NODE_ENV is not development, dev or test, §5.4). better-auth then derives
|
|
815
|
+
* the base URL, token links and a trusted origin from each request's Host header. Default false: boot fails with UNSAFE_BASE_URL
|
|
816
|
+
* (B23).
|
|
817
|
+
*/
|
|
818
|
+
allowRequestDerivedBaseURL?: boolean;
|
|
819
|
+
/** Log each auth path better-auth answers 404 for once, with a "did you mean" (capped at 100 paths). Default: true outside production. */
|
|
820
|
+
diagnostics?: boolean;
|
|
821
|
+
}
|
|
822
|
+
type AuthHandlerInterceptor = (call: {
|
|
823
|
+
readonly request: Request;
|
|
824
|
+
readonly platformRequest: unknown;
|
|
825
|
+
readonly instance: string;
|
|
826
|
+
}, next: (request?: Request) => Promise<Response>) => Promise<Response>;
|
|
827
|
+
type BetterAuthModuleOptions<A extends AuthLike> = BetterAuthStaticOptions & BetterAuthRuntimeOptions<A>;
|
|
828
|
+
interface BetterAuthModuleAsyncOptions<A extends AuthLike> extends BetterAuthStaticOptions {
|
|
829
|
+
imports?: ModuleMetadata["imports"];
|
|
830
|
+
inject?: readonly (InjectionToken | OptionalFactoryDependency)[];
|
|
831
|
+
useFactory: (...args: any[]) => BetterAuthFactoryResult<A> | Promise<BetterAuthFactoryResult<A>>;
|
|
832
|
+
}
|
|
833
|
+
/** A static or app-level key returned from useFactory is a compile error with a readable message (EXP-C9). [C] */
|
|
834
|
+
type BetterAuthFactoryResult<A extends AuthLike> = BetterAuthRuntimeOptions<A> & { [K in keyof (BetterAuthStaticOptions & BetterAuthAppOptions)]?: StaticOptionMustBePassedToForRootAsync<K>; };
|
|
835
|
+
interface StaticOptionMustBePassedToForRootAsync<K extends string> {
|
|
836
|
+
readonly __error: `'${K}' is a static option: pass it to forRootAsync() next to useFactory, not in its result`;
|
|
837
|
+
}
|
|
838
|
+
/** Rejects a different instance than the registered one for the default instance (EXP-C2). [C] */
|
|
839
|
+
type DefaultInstanceCheck<A> = IsRegistered extends true ? A extends RegisteredAuth ? unknown : {
|
|
840
|
+
auth: RegisteredAuth;
|
|
841
|
+
} : unknown;
|
|
842
|
+
/** A class (DI-constructed), a ready instance, or a definition that brings its own providers. */
|
|
843
|
+
type ExtensionRef<T> = Type<T> | T | ExtensionDefinition<T>;
|
|
844
|
+
interface ExtensionDefinition<T> {
|
|
845
|
+
readonly [EXTENSION_DEFINITION]: true;
|
|
846
|
+
readonly use: {
|
|
847
|
+
readonly useClass: Type<T>;
|
|
848
|
+
} | {
|
|
849
|
+
readonly useFactory: (...deps: any[]) => T | Promise<T>;
|
|
850
|
+
readonly inject?: readonly InjectionToken[];
|
|
851
|
+
} | {
|
|
852
|
+
readonly useExisting: InjectionToken<T>;
|
|
853
|
+
};
|
|
854
|
+
readonly providers?: readonly Provider[];
|
|
855
|
+
readonly imports?: ModuleMetadata["imports"];
|
|
856
|
+
/** Tokens (from `providers`) the forRoot module re-exports, globally when isGlobal: helper services for users. */
|
|
857
|
+
readonly exports?: readonly InjectionToken[];
|
|
858
|
+
}
|
|
859
|
+
interface RoutePlan {
|
|
860
|
+
readonly instance: string;
|
|
861
|
+
/**
|
|
862
|
+
* 'inherit': a handler a transport declares as reached only through an already authorized operation (GraphQL field resolvers
|
|
863
|
+
* without method-level access, acceptance or requirement metadata; class-level metadata never applies to them, §7.6 step 3). The
|
|
864
|
+
* guard performs no principal I/O: it checks the enclosing reading and also enforces form mode when forwarding is declared; readers take the nearest
|
|
865
|
+
* enclosing invocation's reading (§7.8).
|
|
866
|
+
*/
|
|
867
|
+
readonly access: "public" | "optional" | "required" | "inherit";
|
|
868
|
+
/** A transport's defaultAccessFor answered 'inherit' for the handler (a nested handler, §7.6 step 3), whatever its effective access. */
|
|
869
|
+
readonly nested: boolean;
|
|
870
|
+
/**
|
|
871
|
+
* defaultRequirements first (unless @SkipDefaultRequirements() applies), then class requirements (base first; not for handlers a
|
|
872
|
+
* transport nests), then method requirements; empty unless access is 'required'.
|
|
873
|
+
*/
|
|
874
|
+
readonly requirements: readonly RequirementExpr[];
|
|
875
|
+
/** @SkipDefaultRequirements() removed the instance's defaultRequirements from this plan (counted in the boot summary, B22). */
|
|
876
|
+
readonly skipsDefaultRequirements: boolean;
|
|
877
|
+
/**
|
|
878
|
+
* The handler declares something in B16's sense (§5.4), computed with the same scoping as the plan: method-level metadata, class-level
|
|
879
|
+
* metadata unless a transport nests the handler, and direct-call forwarding (@ForwardAuthCookies() or cookies.forwardDirectCalls).
|
|
880
|
+
* The other instance-wide defaults (defaultAccess, the default kinds, defaultRequirements) never count.
|
|
881
|
+
*/
|
|
882
|
+
readonly declares: boolean;
|
|
883
|
+
/** 'authoritative' when the route asks for it, session.freshness says so, or a requirement demands fresh identity. */
|
|
884
|
+
readonly freshness: "default" | "authoritative";
|
|
885
|
+
/** Principal kinds admitted on the route (§7.6): @AcceptPrincipals or the instance's default kinds, plus kinds requirements name. */
|
|
886
|
+
readonly accepts: ReadonlySet<string>;
|
|
887
|
+
/** Kind constraints contributed by param decorators (e.g. @CurrentSession() → { kind: 'session', reason: 'SESSION_REQUIRED' }); checked at boot (B15). */
|
|
888
|
+
readonly principalParams: readonly {
|
|
889
|
+
readonly kind: string;
|
|
890
|
+
readonly reason: string;
|
|
891
|
+
}[];
|
|
892
|
+
/**
|
|
893
|
+
* The origin check (§7.10). 'cookie': better-auth's rule when the browser leg carries a cookie. 'form': better-auth's
|
|
894
|
+
* form-CSRF rule, enforcing on every method/operation even without an inbound cookie (handlers declaring cookie forwarding).
|
|
895
|
+
* 'off': @SkipOriginCheck() or originCheck.mode 'off'.
|
|
896
|
+
*/
|
|
897
|
+
readonly originCheck: "cookie" | "form" | "off";
|
|
898
|
+
/** Content key of the sources this plan consults (ordered source indexes, interned per instance); part of the principal memo key (§7.2). */
|
|
899
|
+
readonly sourceSet: string;
|
|
900
|
+
/** Set-Cookie of application direct calls is forwarded (§7.5). */
|
|
901
|
+
readonly forwardDirectCalls: boolean;
|
|
902
|
+
readonly site: string;
|
|
903
|
+
}
|
|
904
|
+
type PrincipalReading = (PrincipalResult | {
|
|
905
|
+
readonly outcome: "no-identity";
|
|
906
|
+
}) & {
|
|
907
|
+
readonly instance: string;
|
|
908
|
+
};
|
|
909
|
+
/**
|
|
910
|
+
* The resolver port the guard uses. Default: ChainPrincipalResolver. Readers never call it: they read the results the guard
|
|
911
|
+
* recorded (§7.8), so v3's peek() is gone. [B]
|
|
912
|
+
*/
|
|
913
|
+
interface PrincipalResolver {
|
|
914
|
+
resolve(call: TransportCall, request: ResolutionRequest): Promise<PrincipalResult>;
|
|
915
|
+
}
|
|
916
|
+
interface ResolutionRequest {
|
|
917
|
+
readonly auth: AuthHandle;
|
|
918
|
+
readonly freshness: "default" | "authoritative";
|
|
919
|
+
/** Kinds the route accepts; sources producing none of them are not consulted (§7.6). */
|
|
920
|
+
readonly accepts: ReadonlySet<string>;
|
|
921
|
+
/** The plan's content key of those sources (RoutePlan.sourceSet). */
|
|
922
|
+
readonly sourceSet: string;
|
|
923
|
+
/**
|
|
924
|
+
* The re-classification read of a generic 401 a policy call met (§8.1): resolve again through the one source that produced the
|
|
925
|
+
* principal (its id), which must declare sessionBacked, bypassing the principal memo. The evaluator shares one such read per logical
|
|
926
|
+
* request (§7.2).
|
|
927
|
+
*/
|
|
928
|
+
readonly reclassify?: {
|
|
929
|
+
readonly sourceId: string;
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
/** The port the evaluator calls a policy through. Default: policy.evaluate(params, context). Tests substitute it (overrideDecisions, §14.8). */
|
|
933
|
+
interface PolicyInvoker {
|
|
934
|
+
invoke<P>(policy: AuthorizationPolicy<P, any>, params: P, context: AuthorizationContext): Promise<AuthorizationDecision>;
|
|
935
|
+
}
|
|
936
|
+
interface RequirementOptions {
|
|
937
|
+
readonly label?: string;
|
|
938
|
+
/** Overrides policy.requires.principals for this requirement. */
|
|
939
|
+
readonly principals?: readonly PrincipalKind[];
|
|
940
|
+
/** Overrides policy.requires.freshIdentity for this requirement. */
|
|
941
|
+
readonly freshIdentity?: boolean;
|
|
942
|
+
}
|
|
943
|
+
interface HookOptions {
|
|
944
|
+
/** 'http' = requests routed by auth.handler; 'server' = direct auth.api calls. Default 'all'. */
|
|
945
|
+
calls?: "all" | "http" | "server";
|
|
946
|
+
/** Skip calls this library makes (guard getSession, policy checks). Default false (better-auth semantics). */
|
|
947
|
+
skipInternal?: boolean;
|
|
948
|
+
/** Ascending order among Nest hooks; ties keep discovery order. Default 0. */
|
|
949
|
+
order?: number;
|
|
950
|
+
/** Named instance the hook binds to. Default 'default'. */
|
|
951
|
+
instance?: string;
|
|
952
|
+
}
|
|
953
|
+
interface DbHookOptions {
|
|
954
|
+
order?: number;
|
|
955
|
+
instance?: string;
|
|
956
|
+
}
|
|
957
|
+
type HookPredicate = (ctx: AuthHookContext) => boolean;
|
|
958
|
+
type HookMethodDecorator<P extends string> = <T>(target: object, key: string | symbol, descriptor: TypedPropertyDescriptor<(ctx: AuthHookContext<P>) => T>) => void;
|
|
959
|
+
type DbHookMethodDecorator<E extends DatabaseHookTarget, Ph extends "before" | "after"> = (target: object, key: string | symbol, descriptor: TypedPropertyDescriptor<DatabaseHookMethod<E, Ph>>) => void;
|
|
960
|
+
type DatabaseHookTarget = `${"user" | "session" | "account" | "verification"}.${"create" | "update" | "delete"}`;
|
|
961
|
+
interface CorsOriginOptions {
|
|
962
|
+
/** Also honor wildcard (`https://*.vercel.app`) and custom-scheme trusted origins for credentialed CORS. Default false: exact origins only. */
|
|
963
|
+
allowPatterns?: boolean;
|
|
964
|
+
}
|
|
965
|
+
interface ExpressPlatformOptions {
|
|
966
|
+
/** Client IP for better-auth's rate limiter. Default req.ip (honors Express `trust proxy`). */
|
|
967
|
+
clientIp?: (req: IncomingMessage & {
|
|
968
|
+
ip?: string;
|
|
969
|
+
}) => string | null;
|
|
970
|
+
}
|
|
971
|
+
interface FastifyPlatformOptions {
|
|
972
|
+
/** Default request.ip (honors Fastify `trustProxy`). */
|
|
973
|
+
clientIp?: (request: {
|
|
974
|
+
ip?: string;
|
|
975
|
+
raw: IncomingMessage;
|
|
976
|
+
}) => string | null;
|
|
977
|
+
}
|
|
978
|
+
interface BootAdvice {
|
|
979
|
+
readonly level: "warn" | "info";
|
|
980
|
+
/** Stable code, e.g. 'W_API_KEY_SESSION_MULTIPLIER'. */
|
|
981
|
+
readonly code: string;
|
|
982
|
+
readonly message: string;
|
|
983
|
+
readonly hint?: string;
|
|
984
|
+
}
|
|
985
|
+
interface BootAdviceContext {
|
|
986
|
+
readonly instance: string;
|
|
987
|
+
readonly auth: AuthHandle;
|
|
988
|
+
/** Awaited $context (post-init options, plugins, sessionConfig, authCookies). */
|
|
989
|
+
readonly context: AuthContextView;
|
|
990
|
+
/** true unless NODE_ENV is 'development', 'dev' or 'test' (an unset NODE_ENV counts as production, §5.4). */
|
|
991
|
+
readonly production: boolean;
|
|
992
|
+
readonly hasHttpAdapter: boolean;
|
|
993
|
+
/** The instance's effective origin-check options (the session unit's W_ORIGIN_CHECK_NATIVE_CLIENTS reads missingOrigin). */
|
|
994
|
+
readonly originCheck: {
|
|
995
|
+
readonly mode: "cookie" | "off";
|
|
996
|
+
readonly missingOrigin: "reject" | "allow-non-browser";
|
|
997
|
+
};
|
|
998
|
+
/** Every compiled handler of this instance (controllers, resolvers, gateways, message handlers) with what transports claimed for it. */
|
|
999
|
+
readonly handlers: readonly AdvisedHandler[];
|
|
1000
|
+
readonly transports: readonly {
|
|
1001
|
+
readonly id: string;
|
|
1002
|
+
readonly connectionTtlMs?: number;
|
|
1003
|
+
}[];
|
|
1004
|
+
readonly sources: readonly Pick<PrincipalSource, "id" | "kinds" | "acceptance" | "effects">[];
|
|
1005
|
+
/** Policies referenced by any plan (resolved instances, §4.4.3), with their declared requirements. */
|
|
1006
|
+
readonly policies: readonly Pick<AuthorizationPolicy, "id" | "requires">[];
|
|
1007
|
+
}
|
|
1008
|
+
interface AdvisedHandler {
|
|
1009
|
+
readonly plan: RoutePlan;
|
|
1010
|
+
/** Ids of the transports that claimed the handler; empty when none did. */
|
|
1011
|
+
readonly transports: readonly string[];
|
|
1012
|
+
/** Named inputs the claiming transports reported (ClaimOptions.inputs): HTTP route params, GraphQL @Args names. Empty when unknown. */
|
|
1013
|
+
readonly inputs: readonly string[];
|
|
1014
|
+
}
|
|
1015
|
+
interface HttpPlatform {
|
|
1016
|
+
/** Diagnostic id ('express', 'fastify', 'hono'…). Core prints it and never compares it. */
|
|
1017
|
+
readonly id: string;
|
|
1018
|
+
/**
|
|
1019
|
+
* Capabilities. http2: the conformance kit exercises HTTP/2. prepareAtInit: prepare() may also run from the core module's
|
|
1020
|
+
* onModuleInit, because nothing it installs has to precede Nest's init(); core then prepares a second application's adapter on the
|
|
1021
|
+
* same container instead of failing with APP_ADAPTER_CHANGED (§5.7).
|
|
1022
|
+
*/
|
|
1023
|
+
readonly capabilities?: {
|
|
1024
|
+
readonly http2?: boolean;
|
|
1025
|
+
readonly prepareAtInit?: boolean;
|
|
1026
|
+
};
|
|
1027
|
+
/** Does this platform drive the running Nest HTTP adapter? Adapter-owned predicate. Never called with a null adapter. */
|
|
1028
|
+
supports(adapter: AbstractHttpAdapter): boolean;
|
|
1029
|
+
/**
|
|
1030
|
+
* Phase 1, optional, synchronous. Called once, as soon as BOTH the running adapter (HttpAdapterHost.init$) and the app's
|
|
1031
|
+
* platform list are known, whichever arrives last (§5.7, LEAD-EXP-3):
|
|
1032
|
+
* NestFactory → inside NestFactory.create(), before it resolves: before main.ts code and before init() registers parsers;
|
|
1033
|
+
* @nestjs/testing → inside createNestApplication(), before it returns.
|
|
1034
|
+
* Use it only for work that must precede body parsing (Express raw capture) or per-request bookkeeping
|
|
1035
|
+
* (Fastify request→reply map). Mount paths are not resolved yet: call ctx.route(pathname) per request.
|
|
1036
|
+
*/
|
|
1037
|
+
prepare?(ctx: PlatformPrepareContext): void;
|
|
1038
|
+
/**
|
|
1039
|
+
* Phase 2, required. Called once per auth instance from onModuleInit, after parsers, main.ts middleware/CORS,
|
|
1040
|
+
* MiddlewareConsumer middleware and controller routes are registered, and before Nest's 404/error handlers.
|
|
1041
|
+
* Route every method for binding.basePath and binding.basePath/* to binding.handle(). May be async. Failures of
|
|
1042
|
+
* handle() must reach the host's error pipeline at REQUEST time (H8), even if routes are registered eagerly.
|
|
1043
|
+
*/
|
|
1044
|
+
mount(ctx: PlatformMountContext): void | Promise<void>;
|
|
1045
|
+
/** Per-request access for transports running on this platform (HTTP controllers, GraphQL over HTTP). */
|
|
1046
|
+
readonly requests: HttpRequestAccessor;
|
|
1047
|
+
/** How the platform resolves the client IP behind proxies (boot summary, W_PROXY_* warnings; §6.8). */
|
|
1048
|
+
proxyTrust?(adapter: AbstractHttpAdapter): ProxyTrust;
|
|
1049
|
+
/** Optional boot-time checks (throw BetterAuthConfigurationError to fail fast). */
|
|
1050
|
+
validate?(adapter: AbstractHttpAdapter): void;
|
|
1051
|
+
/**
|
|
1052
|
+
* Native controller routes and resolved auth bindings. Called after Nest has registered routes and auth mounts are resolved,
|
|
1053
|
+
* before the application starts accepting requests. Platforms own route-grammar matching for controller precedence.
|
|
1054
|
+
*/
|
|
1055
|
+
applicationRoutes?(routes: readonly ApplicationRouteDescriptor[], bindings: readonly AuthRouteBinding[]): void;
|
|
1056
|
+
/** Optional boot advice (§4). */
|
|
1057
|
+
advise?(ctx: BootAdviceContext): readonly BootAdvice[] | Promise<readonly BootAdvice[]>;
|
|
1058
|
+
}
|
|
1059
|
+
interface ApplicationRouteDescriptor {
|
|
1060
|
+
/** Nest RequestMethod name (GET, POST, ALL, ...). */
|
|
1061
|
+
readonly method: string;
|
|
1062
|
+
/** Paths resolved by Nest's RoutePathFactory, including module/global prefixes and URI versions. */
|
|
1063
|
+
readonly paths: readonly string[];
|
|
1064
|
+
/** Conditions evaluated inside the selected controller route after Express body parsing. */
|
|
1065
|
+
readonly conditions: readonly ("host" | "version")[];
|
|
1066
|
+
/** Controller and method name for boot diagnostics. */
|
|
1067
|
+
readonly source: string;
|
|
1068
|
+
}
|
|
1069
|
+
interface ProxyTrust {
|
|
1070
|
+
/**
|
|
1071
|
+
* 'none': the socket address is the client IP (behind a proxy, every client shares the proxy's IP);
|
|
1072
|
+
* 'all': every forwarded hop is trusted (clients can choose their IP); 'partial': hop count, subnet list or function;
|
|
1073
|
+
* 'unknown': the platform cannot tell.
|
|
1074
|
+
*/
|
|
1075
|
+
readonly mode: "none" | "all" | "partial" | "unknown";
|
|
1076
|
+
readonly detail: string;
|
|
1077
|
+
}
|
|
1078
|
+
interface PlatformPrepareContext {
|
|
1079
|
+
readonly adapter: AbstractHttpAdapter;
|
|
1080
|
+
readonly logger: LoggerService;
|
|
1081
|
+
/** The binding whose base path matches this raw (undecoded, query-less) pathname; undefined before mount or if none. */
|
|
1082
|
+
route(pathname: string): AuthRouteBinding | undefined;
|
|
1083
|
+
}
|
|
1084
|
+
interface PlatformMountContext {
|
|
1085
|
+
readonly adapter: AbstractHttpAdapter;
|
|
1086
|
+
readonly logger: LoggerService;
|
|
1087
|
+
readonly binding: AuthRouteBinding;
|
|
1088
|
+
}
|
|
1089
|
+
interface AuthRouteBinding {
|
|
1090
|
+
readonly instance: string;
|
|
1091
|
+
/** Normalized mount path: no trailing slash; never '/' unless allowRootMount. */
|
|
1092
|
+
readonly basePath: string;
|
|
1093
|
+
readonly bodyLimit: number;
|
|
1094
|
+
/** Static baseURL origin (e.g. 'https://api.example.com'), or undefined for unset/dynamic baseURL. */
|
|
1095
|
+
readonly staticOrigin: string | undefined;
|
|
1096
|
+
/** pathname === basePath || pathname.startsWith(basePath + '/'), on the raw pathname. */
|
|
1097
|
+
matches(pathname: string): boolean;
|
|
1098
|
+
/**
|
|
1099
|
+
* Core exchange (§6.3). Resolves a Response for every better-auth outcome; rejects with BetterAuthInfrastructureError,
|
|
1100
|
+
* or with the original APIError when the instance sets onAPIError.throw (§6.10).
|
|
1101
|
+
*/
|
|
1102
|
+
handle(inbound: InboundAuthRequest): Promise<Response>;
|
|
1103
|
+
/** Canonical 413: better-auth's error shape { code: 'PAYLOAD_TOO_LARGE', message }. */
|
|
1104
|
+
payloadTooLarge(): Response;
|
|
1105
|
+
}
|
|
1106
|
+
interface InboundAuthRequest {
|
|
1107
|
+
readonly method: string;
|
|
1108
|
+
/** Absolute URL: platform trust-proxy-aware origin + the ORIGINAL path and query (never a rewritten req.url). */
|
|
1109
|
+
readonly url: string;
|
|
1110
|
+
/** Original headers as Web Headers (toWebHeaders drops pseudo-headers); never rewritten by the platform. */
|
|
1111
|
+
readonly headers: Headers;
|
|
1112
|
+
/** Exact bytes (<= bodyLimit), or a stream core buffers up to bodyLimit, or null when there is no body. */
|
|
1113
|
+
readonly body: Uint8Array | ReadableStream<Uint8Array> | null;
|
|
1114
|
+
/** Client IP from the platform's own trust-proxy resolution; never read from a raw header by core. */
|
|
1115
|
+
readonly clientIp: string | null;
|
|
1116
|
+
/** Opaque; passed to AuthHandlerInterceptor. */
|
|
1117
|
+
readonly platformRequest: unknown;
|
|
1118
|
+
/** Aborted on client disconnect. */
|
|
1119
|
+
readonly signal?: AbortSignal;
|
|
1120
|
+
}
|
|
1121
|
+
interface HttpRequestAccessor {
|
|
1122
|
+
/**
|
|
1123
|
+
* Is `value` this platform's request object, as frameworks layered on HTTP hand it on (Express: an IncomingMessage carrying `res`;
|
|
1124
|
+
* Fastify: a request whose `raw` is an IncomingMessage)? A WebSocket upgrade request, or an object a user's GraphQL context built,
|
|
1125
|
+
* is not. GraphQL transports take the HTTP branch only on a positive answer (§9.2).
|
|
1126
|
+
*/
|
|
1127
|
+
isRequest(value: unknown): boolean;
|
|
1128
|
+
/**
|
|
1129
|
+
* REQUIRED since v6: is the response to this request still unsent (Express: !req.res.writableEnded; Fastify: the reply of request.raw
|
|
1130
|
+
* is not sent; a Web-native platform: !c.finalized)? GraphQL transports refuse a context whose request already finished, which a context
|
|
1131
|
+
* object or a caching context function shared across requests carries (§9.2). It was optional in v5, so a third-party platform silently
|
|
1132
|
+
* lost the runtime half of that defense with no boot or runtime signal — including this document's own Hono sketch, which omitted it.
|
|
1133
|
+
* It is a security defense, not a convenience, and two lines on every platform.
|
|
1134
|
+
*/
|
|
1135
|
+
isLive(req: unknown): boolean;
|
|
1136
|
+
/** Stable identity of one request for guards, pipes, interceptors and GraphQL (Fastify: request.raw). */
|
|
1137
|
+
key(req: unknown): object;
|
|
1138
|
+
/** Request headers as Web Headers (pseudo-headers dropped). */
|
|
1139
|
+
headers(req: unknown): Headers;
|
|
1140
|
+
/** Method and absolute URL (trust-proxy-aware origin, original path). DPoP-bound tokens and the origin check need both. */
|
|
1141
|
+
request(req: unknown): {
|
|
1142
|
+
readonly method: string;
|
|
1143
|
+
readonly url: string;
|
|
1144
|
+
};
|
|
1145
|
+
/** Trust-proxy-aware client IP, or null. */
|
|
1146
|
+
clientIp(req: unknown): string | null;
|
|
1147
|
+
/** Route param. */
|
|
1148
|
+
param(req: unknown, name: string): string | undefined;
|
|
1149
|
+
/** Sink writing Set-Cookie onto the native response; null if unreachable. */
|
|
1150
|
+
cookieSink(req: unknown, res: unknown): CookieSink | null;
|
|
1151
|
+
/** Locate the native response for a request when a framework dropped it (Apollo on Fastify, LEAD-V8). */
|
|
1152
|
+
responseFor?(req: unknown): unknown | undefined;
|
|
1153
|
+
}
|
|
1154
|
+
interface CookieSink {
|
|
1155
|
+
/** Append each value as its own Set-Cookie header. Returns false (and writes nothing) if headers were already sent. */
|
|
1156
|
+
append(setCookies: readonly string[]): boolean;
|
|
1157
|
+
}
|
|
1158
|
+
interface AuthTransport {
|
|
1159
|
+
/** Diagnostics only. */
|
|
1160
|
+
readonly id: string;
|
|
1161
|
+
/**
|
|
1162
|
+
* Adapter-owned, side-effect free predicate (e.g. ctx.getType() === 'graphql' plus shape probes). It must not depend on the
|
|
1163
|
+
* presence of credentials: a context without credentials is still this transport's (T1).
|
|
1164
|
+
*/
|
|
1165
|
+
handles(context: ExecutionContext): boolean;
|
|
1166
|
+
/**
|
|
1167
|
+
* Build a synchronous, side-effect-free envelope whenever handles() accepted the context; never throw merely because credentials,
|
|
1168
|
+
* an upgrade request or a live platform request cannot be extracted. key/invocation/lineage are structural. Deferred request-dependent
|
|
1169
|
+
* getters (headers, browser, cookies, request, clientIp) throw the original configuration error only when consumed.
|
|
1170
|
+
*/
|
|
1171
|
+
describe(context: ExecutionContext, kit: TransportKit): TransportCall;
|
|
1172
|
+
/** Turn a denial into what this transport's runtime delivers correctly (thrown by the guard). */
|
|
1173
|
+
toException(failure: AuthFailure, context: ExecutionContext): unknown;
|
|
1174
|
+
/**
|
|
1175
|
+
* Optional: what to throw for a request-time infrastructure or configuration error. Default: the error itself (HTTP, WS
|
|
1176
|
+
* and RPC runtimes already answer a generic 500 / "Internal server error"). GraphQL returns a generic error with
|
|
1177
|
+
* extensions, because drivers expose error messages to clients. `repeated` is true when an earlier invocation of the same logical
|
|
1178
|
+
* request already surfaced this error: return something the runtime delivers identically but does not log (an IntrinsicException),
|
|
1179
|
+
* so one outage costs one ERROR line per request, not one per aliased field (§13.4).
|
|
1180
|
+
*/
|
|
1181
|
+
toInternalException?(error: BetterAuthInfrastructureError | BetterAuthConfigurationError, context: ExecutionContext, info: {
|
|
1182
|
+
readonly repeated: boolean;
|
|
1183
|
+
}): unknown;
|
|
1184
|
+
/**
|
|
1185
|
+
* Optional boot step: claim every handler this transport serves, with how BetterAuthGuard reaches it (ctx.claim). Core applies
|
|
1186
|
+
* one coverage rule to the claims (§5.4 B16). A transport may also throw BetterAuthConfigurationError for its own prerequisites.
|
|
1187
|
+
*/
|
|
1188
|
+
validate?(ctx: TransportValidationContext): void | Promise<void>;
|
|
1189
|
+
/**
|
|
1190
|
+
* Optional: 'inherit' marks a NESTED handler, one that runs only inside an operation this transport already authorized (GraphQL field
|
|
1191
|
+
* resolvers). For a nested handler the planner ignores class-level access, acceptance and requirement metadata; method-level metadata
|
|
1192
|
+
* gives it a real plan, and without any it inherits (§7.6 step 3). An entry point of an operation (a GraphQL root field, a federation
|
|
1193
|
+
* reference resolver) is never nested. The planner asks every registered transport; the first answer wins.
|
|
1194
|
+
*
|
|
1195
|
+
*/
|
|
1196
|
+
defaultAccessFor?(target: Function, method: string): "inherit" | undefined;
|
|
1197
|
+
/**
|
|
1198
|
+
* Optional, synchronous, side-effect free: the lineage of this invocation (TransportCall.lineage) without building the whole call.
|
|
1199
|
+
* The guard asks for it on public plans, which select no transport otherwise, to record "no identity" for the invocations nested in
|
|
1200
|
+
* a public operation, and on inherit plans, to check that an enclosing invocation recorded a reading (§7.1, §7.8). Transports without
|
|
1201
|
+
* nesting omit it.
|
|
1202
|
+
*/
|
|
1203
|
+
lineage?(context: ExecutionContext): InvocationLineage | undefined;
|
|
1204
|
+
/** better-auth prerequisites. hostlessCalls: this transport's calls carry no Host header (RPC), so a dynamic baseURL needs a fallback (B20). */
|
|
1205
|
+
readonly requires?: {
|
|
1206
|
+
readonly hostlessCalls?: boolean;
|
|
1207
|
+
};
|
|
1208
|
+
/** Optional boot advice (§4). */
|
|
1209
|
+
advise?(ctx: BootAdviceContext): readonly BootAdvice[] | Promise<readonly BootAdvice[]>;
|
|
1210
|
+
}
|
|
1211
|
+
interface TransportCall {
|
|
1212
|
+
/**
|
|
1213
|
+
* Identity of ONE logical request: the HTTP request (GraphQL queries and mutations over HTTP share it), a GraphQL operation carried by a WebSocket,
|
|
1214
|
+
* a WS message, an RPC message. Scope of the principal memo and of policies' I/O memo.
|
|
1215
|
+
*/
|
|
1216
|
+
readonly key: object;
|
|
1217
|
+
/**
|
|
1218
|
+
* Identity of ONE handler invocation, chosen by the transport: the finest object that is distinct for each invocation of the
|
|
1219
|
+
* handler, including invocations the transport reaches in parallel from one entry point, for which an operation-level object
|
|
1220
|
+
* would be shared. Built-in choices: HTTP = the request; GraphQL = the field's `info` object (distinct per root field, alias
|
|
1221
|
+
* and batched operation), except where one `info` serves several parallel invocations, where it is the per-invocation input
|
|
1222
|
+
* the transport passes instead (the GraphQL units use the representation, gql.getRoot(), for federation reference resolvers,
|
|
1223
|
+
* §9.2, T7); WS = the per-message args array; RPC = the message context. Scope of authorization decisions and per-invocation
|
|
1224
|
+
* values. Never a connection.
|
|
1225
|
+
*/
|
|
1226
|
+
readonly invocation: object;
|
|
1227
|
+
/** Connection identity for principal reuse (WS socket, subscription connection); used only when principalTtlMs > 0. */
|
|
1228
|
+
readonly connection?: object;
|
|
1229
|
+
/** Reuse an authenticated/absent principal on `connection` for this long, for plans whose freshness is not 'authoritative' (§7.2). Default 0. */
|
|
1230
|
+
readonly principalTtlMs?: number;
|
|
1231
|
+
/**
|
|
1232
|
+
* Where this invocation sits inside an enclosing operation (GraphQL fields), so that readers of a nested invocation take the reading of
|
|
1233
|
+
* the nearest enclosing invocation whose guard decided (§7.8). Replaces v3's carrier, whose map by memo key let a field read a
|
|
1234
|
+
* sibling root field's principal. Absent for transports whose invocations do not nest (HTTP, WS, RPC).
|
|
1235
|
+
*/
|
|
1236
|
+
readonly lineage?: InvocationLineage;
|
|
1237
|
+
/**
|
|
1238
|
+
* Credentials as Web Headers, plus the leg's `host`, `x-forwarded-host` and `x-forwarded-proto` when the transport has a request or an
|
|
1239
|
+
* upgrade request (better-auth resolves a dynamic base URL from them, even where a user mapping replaced the credentials). Core then
|
|
1240
|
+
* strips any inbound client-IP header and sets it from clientIp. Throws a request-time configuration error when extraction is unavailable; public scope creation never calls it.
|
|
1241
|
+
*/
|
|
1242
|
+
headers(): Headers;
|
|
1243
|
+
/** Trust-proxy-aware client IP when the transport knows it (HTTP); null otherwise. */
|
|
1244
|
+
readonly clientIp: string | null;
|
|
1245
|
+
/** null = this transport cannot deliver Set-Cookie: core suppresses session refresh (§7.4). */
|
|
1246
|
+
readonly cookies: CookieSink | null;
|
|
1247
|
+
/** Method and URL when the transport has them (DPoP-bound tokens need both). */
|
|
1248
|
+
readonly request?: {
|
|
1249
|
+
readonly method: string;
|
|
1250
|
+
readonly url: string;
|
|
1251
|
+
};
|
|
1252
|
+
/** Named input for policies: route param, GraphQL arg, WS/RPC payload field. */
|
|
1253
|
+
param(name: string): unknown;
|
|
1254
|
+
/** The browser leg of this operation, for the origin check (§7.10). Absent when no browser can reach it (RPC). */
|
|
1255
|
+
readonly browser?: BrowserExposure;
|
|
1256
|
+
}
|
|
1257
|
+
interface BrowserExposure {
|
|
1258
|
+
/**
|
|
1259
|
+
* Cookie-mode enforcement classification only (form mode always enforces, regardless of this value). The operation is unsafe: HTTP methods other than GET/HEAD/OPTIONS, GraphQL mutations over HTTP, and EVERY
|
|
1260
|
+
* operation carried by a WebSocket (messages; GraphQL subscriptions, queries and mutations over graphql-ws), because a cross-site
|
|
1261
|
+
* socket can read replies.
|
|
1262
|
+
*/
|
|
1263
|
+
readonly enforce: boolean;
|
|
1264
|
+
/**
|
|
1265
|
+
* The headers the browser itself sent on this leg: the request, or the WebSocket upgrade / handshake request. Never values a
|
|
1266
|
+
* transport copied from handshake.auth, connectionParams, _connectionInit, payloads or query strings.
|
|
1267
|
+
*/
|
|
1268
|
+
headers(): Headers;
|
|
1269
|
+
/** Absolute URL of the leg (scheme://host/path; WebSocket transports use upgradeRequestUrl). */
|
|
1270
|
+
readonly url: string;
|
|
1271
|
+
/** Identity of the leg (the HTTP request, the socket, the upgrade request): the origin verdict is computed once per leg. */
|
|
1272
|
+
readonly key: object;
|
|
1273
|
+
}
|
|
1274
|
+
/** Where an invocation sits inside an operation, for readers of nested invocations (§7.8). */
|
|
1275
|
+
interface InvocationLineage {
|
|
1276
|
+
/** One of ctx.getArgs(), shared by every invocation of the operation (the GraphQL context). Readings are recorded on it under a Symbol.for slot. */
|
|
1277
|
+
readonly carrier: object;
|
|
1278
|
+
/**
|
|
1279
|
+
* This invocation's position, a string the transport derives from the invocation's own args. GraphQL: an id of `info.operation`
|
|
1280
|
+
* (batched operations can share a context) plus the serialized response path of `info.path`, list indexes included, e.g.
|
|
1281
|
+
* `'1:reports.0.owner'`. Aliases give distinct paths. Federation reference resolvers, whose representations share the `_entities`
|
|
1282
|
+
* field's `info`, take `'<op>:_entities#<__typename>'`: invocations of one handler plan in one request, whose readings are identical
|
|
1283
|
+
* (§9.2).
|
|
1284
|
+
*/
|
|
1285
|
+
readonly position: string;
|
|
1286
|
+
/**
|
|
1287
|
+
* Positions of the invocations that enclose the invocation whose args are given, nearest first (GraphQL: walking `info.path.prev`).
|
|
1288
|
+
* It is a function of the args alone, so the guard can record it on the carrier and param factories, which have no DI access, can
|
|
1289
|
+
* walk the lineage too. Every enclosing invocation completes its guard run before a nested one starts: GraphQL resolves a field
|
|
1290
|
+
* before it executes the field's selections.
|
|
1291
|
+
*/
|
|
1292
|
+
readonly enclosing: (args: readonly unknown[]) => Iterable<string>;
|
|
1293
|
+
/**
|
|
1294
|
+
* Optional transport-owned validation before exposing an authenticated reading from this lineage. Structural lineage() never
|
|
1295
|
+
* runs it. GraphQL units provide the current invocation's request-liveness check; readers without DI obtain the callback from
|
|
1296
|
+
* the recorded carrier metadata, supplying their own args (never a closure over a sibling/finished request).
|
|
1297
|
+
*/
|
|
1298
|
+
readonly assertReadable?: (args: readonly unknown[]) => void;
|
|
1299
|
+
}
|
|
1300
|
+
interface TransportKit {
|
|
1301
|
+
/** Accessor of the running app's HTTP platform (for transports layered on HTTP, e.g. GraphQL); null without one. */
|
|
1302
|
+
readonly http: HttpRequestAccessor | null;
|
|
1303
|
+
}
|
|
1304
|
+
interface TransportValidationContext {
|
|
1305
|
+
readonly discovery: DiscoveryService;
|
|
1306
|
+
readonly reflector: Reflector;
|
|
1307
|
+
readonly moduleRef: ModuleRef;
|
|
1308
|
+
readonly hasHttpAdapter: boolean;
|
|
1309
|
+
/** Compiled plan of a handler (class + method). */
|
|
1310
|
+
readonly planOf: (target: Function, method: string) => RoutePlan;
|
|
1311
|
+
/**
|
|
1312
|
+
* Declare a handler this transport serves and how BetterAuthGuard reaches it. `method` undefined claims a class whose handlers the
|
|
1313
|
+
* transport could not enumerate (a class-level decorator is then required). Core decides coverage (§5.4 B16).
|
|
1314
|
+
*/
|
|
1315
|
+
claim(target: Function, method: string | undefined, reach: GuardReach, options: ClaimOptions): void;
|
|
1316
|
+
readonly logger: LoggerService;
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* 'global': the app's global guards reach the handler: covered when they include BetterAuthGuard, however registered or overridden.
|
|
1320
|
+
* BetterAuthScopeInterceptor is not part of global coverage; absence is B31's warning, including globalScope: false. (§5.4 B16)
|
|
1321
|
+
*
|
|
1322
|
+
* 'explicit': @UseBetterAuth(), or BOTH @UseGuards(BetterAuthGuard) and @UseInterceptors(BetterAuthScopeInterceptor), must apply locally.
|
|
1323
|
+
* 'none': no guard ever runs for it (GraphQL field resolvers without fieldResolverEnhancers: ['guards']).
|
|
1324
|
+
*/
|
|
1325
|
+
type GuardReach = "global" | "explicit" | "none";
|
|
1326
|
+
interface ClaimOptions {
|
|
1327
|
+
/** Stable code of the coverage report, e.g. 'GATEWAY_UNGUARDED'. */
|
|
1328
|
+
readonly code: string;
|
|
1329
|
+
/** Copy-paste fix, e.g. 'Add @UseBetterAuth() (or @Public() to opt out); global guards do not reach gateways on NestJS 11.' */
|
|
1330
|
+
readonly hint: string;
|
|
1331
|
+
/** Severity when the handler is not covered. Default 'error'. */
|
|
1332
|
+
readonly coverage?: "error" | "warn" | "off";
|
|
1333
|
+
/** true: every handler needs coverage, even one without library metadata (gateways, hybrid message handlers). false: only handlers that declare something (§5.4 B16). Default false. */
|
|
1334
|
+
readonly everyHandler?: boolean;
|
|
1335
|
+
/**
|
|
1336
|
+
* Names of the handler's inputs that ctx.param(name) can read, as far as the transport knows them statically: HTTP route params,
|
|
1337
|
+
* GraphQL @Args names. Core passes them to boot advice (AdvisedHandler.inputs) and never interprets them. Default [].
|
|
1338
|
+
*/
|
|
1339
|
+
readonly inputs?: readonly string[];
|
|
1340
|
+
}
|
|
1341
|
+
interface AuthPrincipalBase {
|
|
1342
|
+
readonly kind: string;
|
|
1343
|
+
/** PrincipalSource.id that produced it. */
|
|
1344
|
+
readonly source: string;
|
|
1345
|
+
/** The better-auth user this principal acts for; null for machine or organization principals. */
|
|
1346
|
+
readonly userId: string | null;
|
|
1347
|
+
/**
|
|
1348
|
+
* Present when the principal acts with a NARROWER grant than its owner's rights (API key, OAuth token). Built-in policies
|
|
1349
|
+
* AND it with the owner's rights (Z5); a policy that does not name the principal's kind never receives it.
|
|
1350
|
+
*/
|
|
1351
|
+
readonly delegation?: PrincipalDelegation;
|
|
1352
|
+
}
|
|
1353
|
+
interface PrincipalDelegation {
|
|
1354
|
+
/** Does the credential's own grant include these permissions? (api-key: role(key.permissions).authorize) */
|
|
1355
|
+
allows(permissions: Readonly<Record<string, readonly string[]>>): boolean;
|
|
1356
|
+
/** Diagnostics, e.g. 'api-key permissions', 'oauth scopes'. */
|
|
1357
|
+
readonly description: string;
|
|
1358
|
+
}
|
|
1359
|
+
interface PrincipalSource<P extends AuthPrincipalBase = AuthPrincipal> {
|
|
1360
|
+
readonly id: string;
|
|
1361
|
+
/** The principal kinds this source produces. Data: core compares them with the route's accepted kinds (§7.6). */
|
|
1362
|
+
readonly kinds: readonly P["kind"][];
|
|
1363
|
+
/**
|
|
1364
|
+
* 'default': accepted on every authenticated route of the instance. 'explicit': only where a route (@AcceptPrincipals) or a
|
|
1365
|
+
* requirement's policy names one of its kinds. Default 'explicit', so a new source never widens existing routes.
|
|
1366
|
+
*/
|
|
1367
|
+
readonly acceptance?: "default" | "explicit";
|
|
1368
|
+
/**
|
|
1369
|
+
* Request headers this source reads credentials from (e.g. ['x-api-key']). The cookie bridge compares them (§7.5), and their
|
|
1370
|
+
* values are redacted from error causes (§13.4). They play no part in the origin check (§7.10).
|
|
1371
|
+
*/
|
|
1372
|
+
readonly credentialHeaders?: readonly string[];
|
|
1373
|
+
/** Side effects of one resolution (drive boot advice, e.g. quota consumption per WS message). */
|
|
1374
|
+
readonly effects?: {
|
|
1375
|
+
readonly consumesQuota?: boolean;
|
|
1376
|
+
readonly writes?: boolean;
|
|
1377
|
+
};
|
|
1378
|
+
/**
|
|
1379
|
+
* The principal IS better-auth's session read of the request's headers (auth.api.getSession). Only then does a generic "no session"
|
|
1380
|
+
* 401 that a policy's later better-auth call meets contradict the principal, and only then does the evaluator re-read through this
|
|
1381
|
+
* source to tell a lost session from a swallowed storage failure (§8.1). The built-in session source sets it; a source that
|
|
1382
|
+
* authenticates by other means (API keys, OAuth tokens) must not. Default false.
|
|
1383
|
+
*/
|
|
1384
|
+
readonly sessionBacked?: boolean;
|
|
1385
|
+
/**
|
|
1386
|
+
* The principals this source produces carry `delegation` (API keys, OAuth tokens). Data for B15: a requirement whose policy names no kinds
|
|
1387
|
+
* judges only non-delegated principals (§8.1), so at boot it admits exactly the kinds of the instance's sources that do not delegate.
|
|
1388
|
+
* S-delegation checks the declaration against the principals the source produces. Default false.
|
|
1389
|
+
*/
|
|
1390
|
+
readonly delegates?: boolean;
|
|
1391
|
+
/**
|
|
1392
|
+
* better-auth plugins this source needs, checked at boot with $context.hasPlugin (B13); hostlessCalls: some of its auth.api calls
|
|
1393
|
+
* carry no Host header whatever the transport, so a dynamic baseURL needs a fallback (B20).
|
|
1394
|
+
*/
|
|
1395
|
+
readonly requires?: {
|
|
1396
|
+
readonly plugins?: readonly string[];
|
|
1397
|
+
readonly hostlessCalls?: boolean;
|
|
1398
|
+
};
|
|
1399
|
+
/** Cheap synchronous sniffing (e.g. a header is present). false = skip without I/O. Default: true. */
|
|
1400
|
+
appliesTo?(request: PrincipalRequest): boolean;
|
|
1401
|
+
/**
|
|
1402
|
+
* Denials are values: return authenticated(p), absent() (no credential this source understands) or
|
|
1403
|
+
* rejected(failure) (a credential was presented and is invalid). A throw means infrastructure (5xx).
|
|
1404
|
+
*/
|
|
1405
|
+
resolve(request: PrincipalRequest): Promise<PrincipalResult<P>>;
|
|
1406
|
+
/** Optional boot advice (§4). */
|
|
1407
|
+
advise?(ctx: BootAdviceContext): readonly BootAdvice[] | Promise<readonly BootAdvice[]>;
|
|
1408
|
+
}
|
|
1409
|
+
type PrincipalResult<P extends AuthPrincipalBase = AuthPrincipal> = {
|
|
1410
|
+
readonly outcome: "authenticated";
|
|
1411
|
+
readonly principal: P;
|
|
1412
|
+
} | {
|
|
1413
|
+
readonly outcome: "absent";
|
|
1414
|
+
} | {
|
|
1415
|
+
readonly outcome: "rejected";
|
|
1416
|
+
readonly failure: AuthFailure;
|
|
1417
|
+
};
|
|
1418
|
+
interface PrincipalRequest {
|
|
1419
|
+
readonly headers: Headers;
|
|
1420
|
+
readonly cookies: CookieSink | null;
|
|
1421
|
+
readonly transport: string;
|
|
1422
|
+
readonly request?: {
|
|
1423
|
+
readonly method: string;
|
|
1424
|
+
readonly url: string;
|
|
1425
|
+
};
|
|
1426
|
+
readonly freshness: "default" | "authoritative";
|
|
1427
|
+
readonly auth: AuthHandle;
|
|
1428
|
+
/** Memo per logical request (e.g. one verifyApiKey per request: it consumes quota). */
|
|
1429
|
+
memo<T>(key: string | object | symbol, compute: () => Promise<T>): Promise<T>;
|
|
1430
|
+
}
|
|
1431
|
+
/** What extensions receive instead of the raw instance. [A][C] */
|
|
1432
|
+
interface AuthHandle<A extends AuthLike = AuthLike> {
|
|
1433
|
+
readonly name: string;
|
|
1434
|
+
readonly instance: A;
|
|
1435
|
+
readonly api: A["api"];
|
|
1436
|
+
context(): Promise<AuthContextView>;
|
|
1437
|
+
hasPlugin(id: string): Promise<boolean>;
|
|
1438
|
+
/** Run auth.api calls inside a scope: cookie capability, forwarding mode, internal flag (§7.4, §7.5, §10.4). */
|
|
1439
|
+
run<T>(init: ScopeInit, fn: () => Promise<T>): Promise<T>;
|
|
1440
|
+
/** Origin/form rule for a browser leg (§7.10): null on pass/nonapplication, else denial; memoized per leg. browser.enforce gates cookie mode only; false never bypasses form mode. */
|
|
1441
|
+
checkOrigin(browser: BrowserExposure, mode: "cookie" | "form"): Promise<AuthFailure | null>;
|
|
1442
|
+
/**
|
|
1443
|
+
* Did a better-auth endpoint itself produce `value` in a dispatch the plugin observed while bound, as opposed to a before-hook
|
|
1444
|
+
* short-circuit (LEAD-V37, LEAD-EXP-8)? Authoritative identity reads accept only such sessions (§7.3).
|
|
1445
|
+
*/
|
|
1446
|
+
producedByEndpoint(value: unknown): boolean;
|
|
1447
|
+
/** Is `origin` trusted by this instance (post-init set; function-valued trustedOrigins get `request`)? (§6.9) */
|
|
1448
|
+
isTrustedOrigin(origin: string, request?: Request): Promise<boolean>;
|
|
1449
|
+
}
|
|
1450
|
+
interface ScopeInit {
|
|
1451
|
+
/** null = cannot write cookies → the plugin suppresses session refresh for every call in fn. */
|
|
1452
|
+
readonly cookies: CookieSink | null;
|
|
1453
|
+
/**
|
|
1454
|
+
* Set-Cookie forwarding through the plugin's bridge for calls in fn. 'same-credential' (default): only calls that carry the
|
|
1455
|
+
* scope's credential (see `inbound`) or none; 'any': every call; 'none': no forwarding.
|
|
1456
|
+
*/
|
|
1457
|
+
readonly forward?: "none" | "same-credential" | "any";
|
|
1458
|
+
/** The inbound request's headers, the credential 'same-credential' compares against. */
|
|
1459
|
+
readonly inbound?: () => Headers;
|
|
1460
|
+
/** Mark calls as library-internal (HookOptions.skipInternal). Default false. */
|
|
1461
|
+
readonly internal?: boolean;
|
|
1462
|
+
}
|
|
1463
|
+
/** The subset of better-auth's AuthContext the library reads (structural: any version in range fits). [C] */
|
|
1464
|
+
interface AuthContextView {
|
|
1465
|
+
readonly baseURL: string;
|
|
1466
|
+
readonly trustedOrigins: readonly string[];
|
|
1467
|
+
readonly skipCSRFCheck: boolean;
|
|
1468
|
+
readonly skipOriginCheck: boolean | readonly string[];
|
|
1469
|
+
/** The resolved signing secret; B29 compares it with better-auth's public default (LEAD-V48). */
|
|
1470
|
+
readonly secret: string;
|
|
1471
|
+
/** better-auth's resolved rate limiter; B30 reports it off in production (LEAD-V52). */
|
|
1472
|
+
readonly rateLimit: {
|
|
1473
|
+
readonly enabled: boolean;
|
|
1474
|
+
};
|
|
1475
|
+
readonly options: {
|
|
1476
|
+
readonly baseURL?: unknown;
|
|
1477
|
+
readonly basePath?: string;
|
|
1478
|
+
readonly database?: unknown;
|
|
1479
|
+
readonly secondaryStorage?: unknown;
|
|
1480
|
+
/** `enabled` is undefined unless the user set it (B30 tells an explicit false from better-auth's NODE_ENV default). */
|
|
1481
|
+
readonly rateLimit?: {
|
|
1482
|
+
readonly enabled?: boolean;
|
|
1483
|
+
};
|
|
1484
|
+
readonly trustedOrigins?: readonly string[] | ((request?: Request) => unknown);
|
|
1485
|
+
readonly plugins?: readonly {
|
|
1486
|
+
readonly id: string;
|
|
1487
|
+
readonly hooks?: {
|
|
1488
|
+
readonly after?: readonly unknown[];
|
|
1489
|
+
};
|
|
1490
|
+
}[];
|
|
1491
|
+
readonly onAPIError?: {
|
|
1492
|
+
readonly throw?: boolean;
|
|
1493
|
+
};
|
|
1494
|
+
readonly advanced?: {
|
|
1495
|
+
readonly ipAddress?: {
|
|
1496
|
+
readonly ipAddressHeaders?: readonly string[];
|
|
1497
|
+
readonly trustedProxies?: readonly string[];
|
|
1498
|
+
};
|
|
1499
|
+
readonly disableOriginCheck?: boolean;
|
|
1500
|
+
readonly disableCSRFCheck?: boolean;
|
|
1501
|
+
readonly cookiePrefix?: string;
|
|
1502
|
+
};
|
|
1503
|
+
};
|
|
1504
|
+
/** The admin policy's owner-row read for delegated principals (the ban rule), the call userHasPermission itself makes (LEAD-V45). */
|
|
1505
|
+
readonly internalAdapter: {
|
|
1506
|
+
findUserById(userId: string): Promise<{
|
|
1507
|
+
readonly id: string;
|
|
1508
|
+
readonly role?: string | null;
|
|
1509
|
+
readonly banned?: boolean | null;
|
|
1510
|
+
readonly banExpires?: Date | string | null;
|
|
1511
|
+
} | null>;
|
|
1512
|
+
};
|
|
1513
|
+
readonly authCookies: Readonly<Record<"sessionToken" | "sessionData" | "dontRememberToken" | "accountData", {
|
|
1514
|
+
readonly name: string;
|
|
1515
|
+
readonly attributes: {
|
|
1516
|
+
readonly domain?: string;
|
|
1517
|
+
readonly path?: string;
|
|
1518
|
+
};
|
|
1519
|
+
}>>;
|
|
1520
|
+
readonly sessionConfig: {
|
|
1521
|
+
readonly freshAge: number;
|
|
1522
|
+
readonly updateAge: number;
|
|
1523
|
+
readonly expiresIn: number;
|
|
1524
|
+
};
|
|
1525
|
+
readonly adapter: {
|
|
1526
|
+
findOne(input: {
|
|
1527
|
+
model: string;
|
|
1528
|
+
where: readonly {
|
|
1529
|
+
field: string;
|
|
1530
|
+
value: unknown;
|
|
1531
|
+
}[];
|
|
1532
|
+
}): Promise<unknown>;
|
|
1533
|
+
updateMany(input: {
|
|
1534
|
+
model: string;
|
|
1535
|
+
where: readonly {
|
|
1536
|
+
field: string;
|
|
1537
|
+
value: unknown;
|
|
1538
|
+
}[];
|
|
1539
|
+
update: Record<string, unknown>;
|
|
1540
|
+
}): Promise<number>;
|
|
1541
|
+
};
|
|
1542
|
+
hasPlugin(id: string): boolean;
|
|
1543
|
+
getPlugin(id: string): unknown;
|
|
1544
|
+
isTrustedOrigin(url: string, settings?: {
|
|
1545
|
+
allowRelativePaths: boolean;
|
|
1546
|
+
}): boolean;
|
|
1547
|
+
}
|
|
1548
|
+
interface AuthorizationPolicy<Params = unknown, P extends AuthPrincipalBase = AuthPrincipal> {
|
|
1549
|
+
/** Diagnostic id ('better-auth:admin/permission'); never used for dispatch. */
|
|
1550
|
+
readonly id: string;
|
|
1551
|
+
readonly requires?: {
|
|
1552
|
+
/** better-auth plugin ids validated at boot through $context.hasPlugin (B13). */
|
|
1553
|
+
readonly plugins?: readonly string[];
|
|
1554
|
+
/**
|
|
1555
|
+
* Principal kinds this policy can judge; others are denied 403 PRINCIPAL_NOT_SUPPORTED (data-driven, no core branch).
|
|
1556
|
+
* Naming a kind also ADMITS it on routes that use the policy (§7.6). Omitted: any kind WITHOUT `delegation`; delegated
|
|
1557
|
+
* principals are denied 403 PRINCIPAL_NOT_SUPPORTED, so a policy must opt in to judging them (Z5). At boot such a policy admits
|
|
1558
|
+
* exactly the kinds of the instance's sources that do not declare `delegates` (B15).
|
|
1559
|
+
*/
|
|
1560
|
+
readonly principals?: readonly P["kind"][];
|
|
1561
|
+
/** The decision needs an identity read that bypasses the cookie cache (admin-grade decisions): raises the route's freshness (§8.4). */
|
|
1562
|
+
readonly freshIdentity?: boolean;
|
|
1563
|
+
/** The policy calls better-auth with the request's credential headers, so credential hooks re-run per call (api-key quota). Boot advice only. */
|
|
1564
|
+
readonly presentsCredentials?: boolean;
|
|
1565
|
+
/** Some of the policy's auth.api calls carry no Host header (e.g. userHasPermission without headers), so a dynamic baseURL needs a fallback (B20). */
|
|
1566
|
+
readonly hostlessCalls?: boolean;
|
|
1567
|
+
};
|
|
1568
|
+
/** Denials are values. Throw only for infrastructure faults (they become 5xx, never 403). */
|
|
1569
|
+
evaluate(params: Params, context: AuthorizationContext<P>): AuthorizationDecision | Promise<AuthorizationDecision>;
|
|
1570
|
+
/** Optional boot validation of params (e.g. empty permission maps). Throw BetterAuthConfigurationError. */
|
|
1571
|
+
validate?(params: Params, boot: PolicyBootContext): void | Promise<void>;
|
|
1572
|
+
/** Optional boot advice (§4). */
|
|
1573
|
+
advise?(ctx: BootAdviceContext): readonly BootAdvice[] | Promise<readonly BootAdvice[]>;
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* A policy value, or a provider class/token resolved through ModuleRef (for DI-backed policies). Whatever the ref, the planner, the
|
|
1577
|
+
* evaluator and B14 see the same resolved instance (U19), so `requires` declared as an instance field reaches the plan.
|
|
1578
|
+
*/
|
|
1579
|
+
type PolicyRef<Params> = AuthorizationPolicy<Params, any> | Type<AuthorizationPolicy<Params, any>> | InjectionToken;
|
|
1580
|
+
interface Requirement<Params = unknown> {
|
|
1581
|
+
readonly policy: PolicyRef<Params>;
|
|
1582
|
+
readonly params: Params;
|
|
1583
|
+
readonly label?: string;
|
|
1584
|
+
/** Overrides policy.requires.principals for this requirement (e.g. permission(p, { principals })). */
|
|
1585
|
+
readonly principals?: readonly string[];
|
|
1586
|
+
/** Overrides policy.requires.freshIdentity for this requirement. */
|
|
1587
|
+
readonly freshIdentity?: boolean;
|
|
1588
|
+
}
|
|
1589
|
+
type RequirementExpr = Requirement | {
|
|
1590
|
+
readonly anyOf: readonly RequirementExpr[];
|
|
1591
|
+
} | {
|
|
1592
|
+
readonly allOf: readonly RequirementExpr[];
|
|
1593
|
+
};
|
|
1594
|
+
type AuthorizationDecision = {
|
|
1595
|
+
readonly effect: "allow";
|
|
1596
|
+
} | {
|
|
1597
|
+
readonly effect: "deny";
|
|
1598
|
+
readonly status?: 401 | 403;
|
|
1599
|
+
readonly reason: string;
|
|
1600
|
+
readonly message?: string;
|
|
1601
|
+
readonly challenge?: string;
|
|
1602
|
+
};
|
|
1603
|
+
interface AuthorizationContext<P extends AuthPrincipalBase = AuthPrincipal> {
|
|
1604
|
+
readonly principal: P;
|
|
1605
|
+
readonly instance: string;
|
|
1606
|
+
readonly transport: string;
|
|
1607
|
+
readonly headers: Headers;
|
|
1608
|
+
readonly cookies: CookieSink | null;
|
|
1609
|
+
readonly request?: {
|
|
1610
|
+
readonly method: string;
|
|
1611
|
+
readonly url: string;
|
|
1612
|
+
};
|
|
1613
|
+
param(name: string): unknown;
|
|
1614
|
+
readonly handler: {
|
|
1615
|
+
readonly class: Type;
|
|
1616
|
+
readonly method: string;
|
|
1617
|
+
};
|
|
1618
|
+
readonly auth: AuthHandle;
|
|
1619
|
+
/**
|
|
1620
|
+
* Memo per LOGICAL REQUEST (HTTP request, WS message, …), never per connection: dedupe better-auth I/O by concrete inputs,
|
|
1621
|
+
* serialized as a tuple, e.g. `JSON.stringify(['org:hasPermission', orgId, stablePermissions])`, so no input can collide with another key's sentinel. Decisions themselves are never shared across invocations.
|
|
1622
|
+
*/
|
|
1623
|
+
memo<T>(key: string | object | symbol, compute: () => Promise<T>): Promise<T>;
|
|
1624
|
+
/** Publish a per-invocation value for defineInvocationParam decorators (e.g. the resolved organization id). */
|
|
1625
|
+
provide(slot: symbol, value: unknown): void;
|
|
1626
|
+
readonly execution: ExecutionContext;
|
|
1627
|
+
}
|
|
1628
|
+
interface PolicyBootContext {
|
|
1629
|
+
readonly auth: AuthHandle;
|
|
1630
|
+
readonly context: AuthContextView;
|
|
1631
|
+
readonly site: string;
|
|
1632
|
+
}
|
|
1633
|
+
//#endregion
|
|
1634
|
+
export { PrincipalResult as $, AfterAuth as $t, ExpressPlatformOptions as A, POLICY_INVOKER as An, PrincipalOfKind as At, HttpRequestAccessor as B, AuthFailures as Bn, httpTransport as Bt, CookieForwardingOptions as C, allow as Cn, DatabaseHookMethod as Ct, DbHookMethodDecorator as D, EXTENSION_DEFINITION as Dn, IsRegistered as Dt, DatabaseHookTarget as E, BetterAuthService as En, GetSessionWithHeaders as Et, HookMethodDecorator as F, getBetterAuthOptionsToken as Fn, SessionPrincipal as Ft, PlatformPrepareContext as G, ErrorMappingOptions as Gn, rejected as Gt, InvocationLineage as H, AuthTransportErrorPayload as Hn, absent as Ht, HookOptions as I, getBetterAuthServiceToken as In, UnvalidatedBody as It, PolicyRef as J, isConfigurationError as Jn, RequireFreshSession as Jt, PolicyBootContext as K, getRawCause as Kn, CurrentSession as Kt, HookPredicate as L, AuthErrorBody as Ln, UserOf as Lt, ExtensionRef as M, SCOPE_CORE as Mn, RegisteredAuth as Mt, FastifyPlatformOptions as N, getBetterAuthHandleToken as Nn, RegisteredInstances as Nt, DbHookOptions as O, BetterAuthModule as On, OrgPermissions as Ot, GuardReach as P, getBetterAuthInstanceToken as Pn, SessionOf as Pt, PrincipalResolver as Q, AcceptPrincipals as Qt, HttpMountOptions as R, AuthErrorCode as Rn, WithActiveOrganization as Rt, ClaimOptions as S, BetterAuthGuard as Sn, DatabaseHookData as St, CorsOriginOptions as T, deny as Tn, EndpointPath as Tt, OriginCheckOptions as U, BetterAuthConfigurationError as Un, authenticated as Ut, InboundAuthRequest as V, AuthGraphqlExtensions as Vn, betterAuthCorsOrigin as Vt, PlatformMountContext as W, BetterAuthInfrastructureError as Wn, definePrincipalSource as Wt, PrincipalReading as X, freshSession as Xt, PrincipalDelegation as Y, isInfrastructureError as Yn, SESSION_PRINCIPAL_KIND as Yt, PrincipalRequest as Z, sessionPrincipal as Zt, BetterAuthRuntimeOptions as _, requirement as _n, AuthLike as _t, AuthHandlerInterceptor as a, OptionalAuth as an, ResolutionRequest as at, BootAdviceContext as b, defineTransport as bn, AuthSession as bt, AuthTransport as c, RequireAuth as cn, SessionOption as ct, AuthorizationLimits as d, UseAuthInstance as dn, TransportCall as dt, AfterDatabase as en, PrincipalSource as et, AuthorizationPolicy as f, UseBetterAuth as fn, TransportKit as ft, BetterAuthModuleOptions as g, definePrincipalParam as gn, AuthHookContext as gt, BetterAuthModuleAsyncOptions as h, defineInvocationParam as hn, AuthAfterHookContext as ht, AuthHandle as i, ForwardAuthCookies as in, RequirementOptions as it, ExtensionDefinition as j, PRINCIPAL_RESOLVER as jn, Register as jt, DefaultInstanceCheck as k, GUARD_CORE as kn, PrincipalKind as kt, AuthorizationContext as l, SkipDefaultRequirements as ln, SessionPrincipalOptions as lt, BetterAuthFactoryResult as m, anyOf as mn, AdminPermissions as mt, ApplicationRouteDescriptor as n, BeforeDatabase as nn, Requirement as nt, AuthPrincipalBase as o, Public as on, RoutePlan as ot, BetterAuthAppOptions as p, allOf as pn, TransportValidationContext as pt, PolicyInvoker as q, isAuthFailure as qn, CurrentUser as qt, AuthContextView as r, CurrentPrincipal as rn, RequirementExpr as rt, AuthRouteBinding as s, Require as sn, ScopeInit as st, AdvisedHandler as t, BeforeAuth as tn, ProxyTrust as tt, AuthorizationDecision as u, SkipOriginCheck as un, StaticOptionMustBePassedToForRootAsync as ut, BetterAuthStaticOptions as v, defineExtension as vn, AuthOf as vt, CookieSink as w, definePolicy as wn, DatabaseHookResult as wt, BrowserExposure as x, BetterAuthScopeInterceptor as xn, AuthUser as xt, BootAdvice as y, defineHttpPlatform as yn, AuthPrincipal as yt, HttpPlatform as z, AuthFailure as zn, PrincipalKinds as zt };
|
|
1635
|
+
//# sourceMappingURL=auth-contracts-BBA1C1gj.d.cts.map
|