@velajs/better-auth 1.0.0 → 1.22.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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Continue the coordinated framework release on the 1.x line with the current checked provider, endpoint, identity, CRUD, live, and Studio APIs. Breaking API changes are accepted during this development phase; maintained applications use the current contracts.
8
+ - Publish from the pnpm packages workspace with TypeScript 7 and GitHub OIDC. Obsolete standalone examples have been removed; runnable applications live in apps/.
9
+ - Updated workspace dependencies
10
+ - @velajs/authz@1.22.0
11
+ - @velajs/vela@1.22.0
12
+
13
+ ## 2.0.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Publish from the unified packages workspace with corrected repository paths, shared native tooling, TypeScript 7 checks, and npm OIDC releases. Runnable examples now live in apps/.
18
+ - Updated dependencies
19
+ - @velajs/vela@2.0.1
20
+ - @velajs/authz@2.0.1
21
+
22
+ ## 2.0.0
23
+
24
+ Native environment configuration and one immutable verified identity shared with provider-independent authorization.
25
+
26
+ Requires the coordinated Vela 2.0 package set. See the workspace migration guide.
27
+
3
28
  ## 1.0.0
4
29
 
5
30
  ### Major Changes
package/README.md CHANGED
@@ -74,22 +74,19 @@ imports: [
74
74
 
75
75
  ### Pattern B — DI'd plugin construction
76
76
 
77
- `forRootAsync` lets vela services participate in your better-auth config. Required for Cloudflare D1 / Hyperdrive bindings, since env bindings only resolve at request time.
77
+ `forRootAsync` requires an explicit `inject` tuple (use `inject: []` when there are no dependencies), so factory parameter types always have matching runtime tokens. It lets Vela services participate in your Better Auth configuration. On Workers, declare `WORKER_ENV = new InjectionToken<WorkerEnv>('app.Env')` and pass that token to `createCloudflareWorker(AppModule, { envToken: WORKER_ENV })`; the native event environment is available before DI factories run.
78
78
 
79
79
  ```ts
80
80
  imports: [
81
- D1Module.forRoot({ binding: 'DB' }),
82
81
  BetterAuthModule.forRootAsync({
83
- inject: [D1Service, EmailService],
84
- useFactory: (d1: D1Service, email: EmailService) => ({
85
- auth: betterAuth({
86
- database: drizzleAdapter(drizzle(d1.binding), { provider: 'sqlite' }),
82
+ inject: [WORKER_ENV, EmailService],
83
+ useFactory: (env, email) => betterAuth({
84
+ database: drizzleAdapter(drizzle(env.DB), { provider: 'sqlite' }),
87
85
  plugins: [
88
86
  magicLink({ sendMagicLink: (data) => email.send(data) }), // DI'd EmailService
89
87
  apiKey(),
90
88
  twoFactor(),
91
89
  ],
92
- }),
93
90
  }),
94
91
  isGlobal: true,
95
92
  }),
@@ -102,7 +99,7 @@ Each feature ships a self-contained vela module that exports a plugin token. App
102
99
 
103
100
  ```ts
104
101
  // magic-link-auth.module.ts
105
- import { Module, InjectionToken } from '@velajs/vela';
102
+ import { Module, InjectionToken, defineProvider } from '@velajs/vela';
106
103
  import { magicLink } from 'better-auth/plugins';
107
104
  import { EmailService } from './email.service';
108
105
 
@@ -113,12 +110,11 @@ export const MAGIC_LINK_PLUGIN = new InjectionToken<ReturnType<typeof magicLink>
113
110
  @Module({
114
111
  providers: [
115
112
  EmailService,
116
- {
117
- provide: MAGIC_LINK_PLUGIN,
113
+ defineProvider(MAGIC_LINK_PLUGIN, {
118
114
  inject: [EmailService],
119
115
  useFactory: (email: EmailService) =>
120
116
  magicLink({ sendMagicLink: (d) => email.send({ to: d.email, link: d.url }) }),
121
- },
117
+ }),
122
118
  ],
123
119
  exports: [MAGIC_LINK_PLUGIN],
124
120
  })
@@ -134,9 +130,7 @@ export class MagicLinkAuthModule {}
134
130
  BetterAuthModule.forRootAsync({
135
131
  imports: [MagicLinkAuthModule, OAuthAuthModule],
136
132
  inject: [MAGIC_LINK_PLUGIN, OAUTH_PLUGIN],
137
- useFactory: (magicLink, oauth) => ({
138
- auth: betterAuth({ database, plugins: [magicLink, oauth] }),
139
- }),
133
+ useFactory: (magicLink, oauth) => betterAuth({ database, plugins: [magicLink, oauth] }),
140
134
  isGlobal: true,
141
135
  }),
142
136
  ],
@@ -154,7 +148,7 @@ import { BetterAuthService } from '@velajs/better-auth';
154
148
 
155
149
  @Injectable()
156
150
  class AdminUserService {
157
- constructor(@Inject(BetterAuthService) private readonly auth: BetterAuthService) {}
151
+ constructor(@Inject(BetterAuthService) private readonly auth: BetterAuthService<typeof auth>) {}
158
152
 
159
153
  listSessions(userId: string) {
160
154
  return this.auth.api.listUserSessions({ userId });
@@ -165,7 +159,7 @@ class AdminUserService {
165
159
  }
166
160
  ```
167
161
 
168
- Under `forRootAsync`, the underlying `betterAuth({...})` instance is constructed **lazily on first `.auth` / `.api` / `.handler` access**. That's what makes Cloudflare bindings (D1, KV, R2) work: the user factory only runs after request-time middleware has populated env. No proxies, no lifecycle hooks just a service with a cached field.
162
+ Under `forRootAsync`, the underlying `betterAuth({...})` instance is constructed lazily on first `.auth` / `.api` / `.handler` access. The Workers adapter supplies the typed environment before DI and owns a separate application for each environment, so a cached auth instance never crosses environments.
169
163
 
170
164
  ## Decorators
171
165
 
@@ -175,7 +169,6 @@ Under `forRootAsync`, the underlying `betterAuth({...})` instance is constructed
175
169
  | `@CurrentSession()` | Better-auth `Session` after guards run |
176
170
  | `@Public(true)` | Class or method — bypass AuthGuard entirely |
177
171
  | `@OptionalAuth(true)` | Class or method — populate user if present, never throw 401 |
178
- | `@Roles(['admin'])` | Method — read by `RolesGuard`. Compares against `user.role`. |
179
172
 
180
173
  Optional identities are ordinary values: an anonymous caller receives the actual `undefined`, so normal truthiness checks are safe.
181
174
 
@@ -187,8 +180,8 @@ handle(@CurrentUser() user: User | undefined) {
187
180
 
188
181
  ## Guards
189
182
 
190
- - **`AuthGuard`** — singleton. Reads `Authorization` header / cookies via `auth.api.getSession`, populates `REQUEST_CONTEXT`, and publishes Vela's trusted principal/tenant identity for downstream security components. The Better Auth organization plugin's verified `activeOrganizationId` becomes the tenant partition when present. The guard honors only explicit `@Public()` / `@OptionalAuth()` metadata. The generated Better Auth catch-all controller is explicitly public; sharing its URL prefix never makes an application controller public.
191
- - **`RolesGuard`** singleton. Reads `@Roles([...])` metadata, compares against `user.role`. Use with `@UseGuards(AuthGuard, RolesGuard)` order matters.
183
+ - **`AuthGuard`** — singleton. Reads `Authorization` header / cookies via `auth.api.getSession`, validates the full base user/session models, and publishes Vela's trusted principal, tenant, roles, and session expiry for downstream security components. The Better Auth organization plugin's verified `activeOrganizationId` becomes the tenant partition when present. The guard honors only explicit `@Public()` / `@OptionalAuth()` metadata. The generated Better Auth catch-all controller is explicitly public; sharing its URL prefix never makes an application controller public.
184
+ Permission and role guards live in `@velajs/authz/vela`: import `PermissionGuard`, `RequirePermission`, `RolesGuard`, and `Roles` there. They read the same trusted identity for Better Auth and Cloudflare Access. Run authentication before authorization.
192
185
 
193
186
  Global registration is the default: installing the module binds `AuthGuard` to `APP_GUARD`. Routes are deny-by-default; mark public ones with `@Public(true)`. The generated Better Auth controller is already marked public.
194
187
 
@@ -196,6 +189,18 @@ When using `ThrottlerModule`, import Better Auth first. Vela then rate-limits by
196
189
  the verified issuer, subject, principal type, and active organization before it
197
190
  falls back to a platform-attested client address.
198
191
 
192
+ `BETTER_AUTH_OPTIONS` now contains only runtime configuration; read the auth instance from `BetterAuthService`. The obsolete `defaultPolicy` option and default-path controller constant were removed. Use `createBetterAuthCatchallController()` for a manually mounted catch-all.
193
+
194
+ ## Trusted identity and typing
195
+
196
+ `@CurrentUser()` and `@CurrentSession()` expose only validated Better Auth data tied to the exact current trusted identity. Public routes, missing/rejected sessions, logout, expiry, and another provider replacing the identity invalidate those values. Hono user variables and removed compatibility symbols cannot grant roles or permissions.
197
+
198
+ Core `getTrustedRequestIdentity(request)` and authz `@CurrentIdentity()` return the verified issuer/subject/type, optional tenant, explicit roles, and credential expiry. WebSocket guards consume only the normalized server connection attachment and do not consult HTTP cookies or arbitrary socket role metadata.
199
+
200
+ The integration accepts the minimal `BetterAuthInstance` contract instead of `Auth<any>`. `new BetterAuthService(() => auth)` infers the concrete instance and retains plugin API/result types. For an injected service, annotate `BetterAuthService<typeof auth>` with the same configured instance type. The unparameterized service intentionally exposes only the operations the framework itself requires.
201
+
202
+ Migration: identity symbols (`AUTH_USER_KEY`, `AUTH_SESSION_KEY`, issuer/type keys) were removed. Use validated parameter decorators or core's trusted identity reader. Authorization imports moved to `@velajs/authz/vela`; the module no longer registers duplicate provider-specific permission guards.
203
+
199
204
  ## Edge-safe DB adapters
200
205
 
201
206
  `@velajs/better-auth` itself is `node:`-clean. Edge-safety of your runtime depends on your better-auth DB adapter:
@@ -1,17 +1,17 @@
1
1
  import { Inject, Injectable, InjectionToken } from "@velajs/vela";
2
- //#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorateMetadata.js
2
+ //#region \0@oxc-project+runtime@0.150.0/helpers/esm/decorateMetadata.js
3
3
  function __decorateMetadata(k, v) {
4
4
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
5
5
  }
6
6
  //#endregion
7
- //#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorateParam.js
7
+ //#region \0@oxc-project+runtime@0.150.0/helpers/esm/decorateParam.js
8
8
  function __decorateParam(paramIndex, decorator) {
9
9
  return function(target, key) {
10
10
  decorator(target, key, paramIndex);
11
11
  };
12
12
  }
13
13
  //#endregion
14
- //#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorate.js
14
+ //#region \0@oxc-project+runtime@0.150.0/helpers/esm/decorate.js
15
15
  function __decorate(decorators, target, key, desc) {
16
16
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
17
17
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -60,4 +60,4 @@ BetterAuthService = __decorate([
60
60
  //#endregion
61
61
  export { __decorateMetadata as a, __decorateParam as i, BetterAuthService as n, __decorate as r, BETTER_AUTH_BUILDER as t };
62
62
 
63
- //# sourceMappingURL=better-auth.service-BMkyFX-w.js.map
63
+ //# sourceMappingURL=better-auth.service-DurQ4JQf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"better-auth.service-DurQ4JQf.js","names":[],"sources":["../src/better-auth.service.ts"],"sourcesContent":["import { Inject, Injectable, InjectionToken } from '@velajs/vela';\nimport type { BetterAuthInstance } from './better-auth.types';\n\n/**\n * Internal token holding the auth-construction closure with its inject deps\n * closed over. Resolves cheaply at module load (just captures references);\n * the inner call happens lazily on first auth use (see `BetterAuthService`).\n *\n * Not exported from the public surface — only the service consumes it.\n */\nexport const BETTER_AUTH_BUILDER = new InjectionToken<() => BetterAuthInstance>(\n 'vela.better-auth.Builder',\n);\n\n/**\n * The single injectable consumers reach for to interact with better-auth.\n * Wraps the underlying `betterAuth({...})` instance with lazy construction:\n *\n * - `forRoot({ auth })` — the builder returns the eagerly-provided instance,\n * so the first `.auth` / `.api` / `.handler` access is effectively a\n * read-and-cache.\n * - `forRootAsync({ inject, useFactory })` — the builder wraps the user's\n * factory + inject deps. First access triggers `useFactory(...deps)`. This\n * is what makes Cloudflare D1/KV bindings work: at module load the factory\n * doesn't run; on first request (when AuthGuard or the catch-all calls\n * `service.api` / `service.handler`), the native environment is registered and the\n * factory can read them safely.\n *\n * Used directly by AuthGuard and the catch-all controller. Consumers in\n * application code inject the same way: `@Inject(BetterAuthService)`.\n */\n@Injectable()\nexport class BetterAuthService<TAuth extends BetterAuthInstance = BetterAuthInstance> {\n private cached: TAuth | undefined;\n\n constructor(@Inject(BETTER_AUTH_BUILDER) private readonly build: () => TAuth) {}\n\n /**\n * The underlying better-auth instance. Constructed once on first access.\n * Safe to call from any request-time code path (guards, controllers,\n * services invoked from handlers).\n */\n get auth(): TAuth {\n if (!this.cached) this.cached = this.build();\n return this.cached;\n }\n\n /** Convenience accessor — equivalent to `service.auth.api`. */\n get api(): TAuth['api'] {\n return this.auth.api;\n }\n\n /** Convenience accessor — equivalent to `service.auth.handler`. */\n get handler(): TAuth['handler'] {\n return this.auth.handler;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,MAAa,sBAAsB,IAAI,eACrC,0BACF;AAoBO,IAAM,oBAAN,MAAM,kBAAyE;CAG1B;CAF1D;CAEA,YAAY,OAAkE;EAApB,KAAA,QAAA;CAAqB;;;;;;CAO/E,IAAI,OAAc;EAChB,IAAI,CAAC,KAAK,QAAQ,KAAK,SAAS,KAAK,MAAM;EAC3C,OAAO,KAAK;CACd;;CAGA,IAAI,MAAoB;EACtB,OAAO,KAAK,KAAK;CACnB;;CAGA,IAAI,UAA4B;EAC9B,OAAO,KAAK,KAAK;CACnB;AACF;;CAzBC,WAAW;CAIG,gBAAA,GAAA,OAAO,mBAAmB,CAAA"}
@@ -0,0 +1,68 @@
1
+ import { InjectionToken } from "@velajs/vela";
2
+ import { Auth, Session, User } from "better-auth";
3
+ //#region src/better-auth.types.d.ts
4
+ /** The runtime surface required by the integration; concrete instances retain their generics. */
5
+ interface BetterAuthInstance {
6
+ readonly api: {
7
+ getSession(input: {
8
+ headers: Headers;
9
+ }): Promise<unknown>;
10
+ };
11
+ readonly handler: (request: Request) => Promise<Response>;
12
+ /** Used only by the real-session testing helper. */
13
+ readonly $context?: Promise<Pick<Awaited<Auth['$context']>, 'internalAdapter' | 'authCookies' | 'secret'>>;
14
+ }
15
+ interface BetterAuthModuleOptions<TAuth extends BetterAuthInstance = BetterAuthInstance> extends BetterAuthRuntimeOptions {
16
+ auth: TAuth;
17
+ }
18
+ /** Provider configuration, separate from the lazily constructed auth instance. */
19
+ interface BetterAuthRuntimeOptions {
20
+ /** Stable namespace paired with user ids in authorization identities. */
21
+ issuer?: string;
22
+ basePath?: string;
23
+ /**
24
+ * Register AuthGuard application-wide. Defaults to `true`; opt out only when
25
+ * the application installs an equivalent global authentication guard itself.
26
+ */
27
+ isGlobal?: boolean;
28
+ mountHandler?: boolean;
29
+ }
30
+ type User$1 = User;
31
+ type Session$1 = Session;
32
+ //#endregion
33
+ //#region src/better-auth.service.d.ts
34
+ /**
35
+ * The single injectable consumers reach for to interact with better-auth.
36
+ * Wraps the underlying `betterAuth({...})` instance with lazy construction:
37
+ *
38
+ * - `forRoot({ auth })` — the builder returns the eagerly-provided instance,
39
+ * so the first `.auth` / `.api` / `.handler` access is effectively a
40
+ * read-and-cache.
41
+ * - `forRootAsync({ inject, useFactory })` — the builder wraps the user's
42
+ * factory + inject deps. First access triggers `useFactory(...deps)`. This
43
+ * is what makes Cloudflare D1/KV bindings work: at module load the factory
44
+ * doesn't run; on first request (when AuthGuard or the catch-all calls
45
+ * `service.api` / `service.handler`), the native environment is registered and the
46
+ * factory can read them safely.
47
+ *
48
+ * Used directly by AuthGuard and the catch-all controller. Consumers in
49
+ * application code inject the same way: `@Inject(BetterAuthService)`.
50
+ */
51
+ declare class BetterAuthService<TAuth extends BetterAuthInstance = BetterAuthInstance> {
52
+ private readonly build;
53
+ private cached;
54
+ constructor(build: () => TAuth);
55
+ /**
56
+ * The underlying better-auth instance. Constructed once on first access.
57
+ * Safe to call from any request-time code path (guards, controllers,
58
+ * services invoked from handlers).
59
+ */
60
+ get auth(): TAuth;
61
+ /** Convenience accessor — equivalent to `service.auth.api`. */
62
+ get api(): TAuth['api'];
63
+ /** Convenience accessor — equivalent to `service.auth.handler`. */
64
+ get handler(): TAuth['handler'];
65
+ }
66
+ //#endregion
67
+ export { Session$1 as a, BetterAuthRuntimeOptions as i, BetterAuthInstance as n, User$1 as o, BetterAuthModuleOptions as r, BetterAuthService as t };
68
+ //# sourceMappingURL=better-auth.service-NOlWXxd4.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,25 +1,6 @@
1
+ import { a as Session, i as BetterAuthRuntimeOptions, n as BetterAuthInstance, o as User, r as BetterAuthModuleOptions, t as BetterAuthService } from "./better-auth.service-NOlWXxd4.js";
1
2
  import { CanActivate, DynamicModule, ExecutionContext, InferTokens, InjectionToken, Token, Type } from "@velajs/vela";
2
- import { Auth, Session as Session$1, User as User$1 } from "better-auth";
3
3
  import { Identity, PermissionResolver } from "@velajs/authz";
4
- //#region src/better-auth.types.d.ts
5
- type BetterAuthInstance = Auth<any>;
6
- interface BetterAuthModuleOptions {
7
- auth: BetterAuthInstance;
8
- /** Stable namespace paired with user ids in authorization identities. */
9
- issuer?: string;
10
- basePath?: string;
11
- /**
12
- * Register AuthGuard application-wide. Defaults to `true`; opt out only when
13
- * the application installs an equivalent global authentication guard itself.
14
- */
15
- isGlobal?: boolean;
16
- /** @deprecated Authentication is deny-by-default. Only `'deny'` is accepted. */
17
- defaultPolicy?: 'deny';
18
- mountHandler?: boolean;
19
- }
20
- type User = User$1;
21
- type Session = Session$1;
22
- //#endregion
23
4
  //#region src/better-auth.module.d.ts
24
5
  /**
25
6
  * Options for {@link BetterAuthModule.forRootAsync}.
@@ -30,25 +11,23 @@ type Session = Session$1;
30
11
  *
31
12
  * ```ts
32
13
  * BetterAuthModule.forRootAsync({
33
- * inject: [D1Service, ConfigService], // captured as readonly tuple
34
- * useFactory: (d1, config) => // d1: D1Service, config: ConfigService
35
- * betterAuth({ database: drizzleAdapter(drizzle(d1.database), ...) }),
14
+ * inject: [WORKER_ENV, ConfigService], // captured as readonly tuple
15
+ * useFactory: (env, config) => // inferred from the tokens
16
+ * betterAuth({ database: drizzleAdapter(drizzle(env.DB), ...) }),
36
17
  * });
37
18
  * ```
38
19
  */
39
- interface ForRootAsyncOptions<Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]> {
40
- inject?: Inject;
20
+ interface ForRootAsyncOptions<Inject extends readonly Token[] = readonly Token[]> {
21
+ inject: Inject;
41
22
  imports?: DynamicModule['imports'];
42
23
  useFactory: (...deps: InferTokens<Inject>) => BetterAuthInstance;
43
24
  isGlobal?: boolean;
44
25
  mountHandler?: boolean;
45
26
  basePath?: string;
46
27
  issuer?: string;
47
- /** @deprecated Authentication is deny-by-default. Only `'deny'` is accepted. */
48
- defaultPolicy?: 'deny';
49
28
  key?: string;
50
29
  }
51
- declare class BetterAuthModule {
30
+ export declare class BetterAuthModule {
52
31
  /**
53
32
  * Synchronous registration. The auth instance is constructed by the consumer
54
33
  * at module-load time and passed in directly. Use this when the inputs to
@@ -64,47 +43,11 @@ declare class BetterAuthModule {
64
43
  * first time anything reads `BetterAuthService.auth` (or `.api` / `.handler`).
65
44
  * In normal request handling that's `AuthGuard.canActivate` or the catch-all
66
45
  * controller's `.handle`. At module load the factory does NOT run — it's only
67
- * captured behind {@link lazyProvider}'s memoized thunk. This is what makes
68
- * Cloudflare bindings (D1, KV, R2) work: the binding isn't ready at boot, but
69
- * it IS by the time a request flows through and the guard / catch-all reads
70
- * the service. Inject deps resolve at module load (cheap BindingRef wrappers);
71
- * their *values* are read at first auth use, inside your factory body.
72
- */
73
- static forRootAsync<const Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]>(options: ForRootAsyncOptions<Inject>): DynamicModule;
74
- }
75
- //#endregion
76
- //#region src/better-auth.service.d.ts
77
- /**
78
- * The single injectable consumers reach for to interact with better-auth.
79
- * Wraps the underlying `betterAuth({...})` instance with lazy construction:
80
- *
81
- * - `forRoot({ auth })` — the builder returns the eagerly-provided instance,
82
- * so the first `.auth` / `.api` / `.handler` access is effectively a
83
- * read-and-cache.
84
- * - `forRootAsync({ inject, useFactory })` — the builder wraps the user's
85
- * factory + inject deps. First access triggers `useFactory(...deps)`. This
86
- * is what makes Cloudflare D1/KV bindings work: at module load the factory
87
- * doesn't run; on first request (when AuthGuard or the catch-all calls
88
- * `service.api` / `service.handler`), the bindings are populated and the
89
- * factory can read them safely.
90
- *
91
- * Used directly by AuthGuard and the catch-all controller. Consumers in
92
- * application code inject the same way: `@Inject(BetterAuthService)`.
93
- */
94
- declare class BetterAuthService {
95
- private readonly build;
96
- private cached;
97
- constructor(build: () => BetterAuthInstance);
98
- /**
99
- * The underlying better-auth instance. Constructed once on first access.
100
- * Safe to call from any request-time code path (guards, controllers,
101
- * services invoked from handlers).
46
+ * captured in the checked builder provider. The Workers adapter supplies its
47
+ * native environment before DI; the lazily constructed auth instance belongs
48
+ * to that environment's application and never captures another app's bindings.
102
49
  */
103
- get auth(): BetterAuthInstance;
104
- /** Convenience accessor — equivalent to `service.auth.api`. */
105
- get api(): BetterAuthInstance['api'];
106
- /** Convenience accessor — equivalent to `service.auth.handler`. */
107
- get handler(): BetterAuthInstance['handler'];
50
+ static forRootAsync<const Inject extends readonly Token[] = readonly Token[]>(options: ForRootAsyncOptions<Inject>): DynamicModule;
108
51
  }
109
52
  //#endregion
110
53
  //#region src/better-auth.controller.d.ts
@@ -121,106 +64,33 @@ declare class BetterAuthService {
121
64
  *
122
65
  * Both default to `/api/auth`, so the no-prefix / no-config case just works.
123
66
  */
124
- declare function createBetterAuthCatchallController(basePath?: string): Type;
125
- /**
126
- * Default-path (`/api/auth`) catch-all controller. Retained for back-compat;
127
- * `BetterAuthModule` now mounts {@link createBetterAuthCatchallController} with
128
- * the configured `basePath`. Prefer the factory for a custom base path.
129
- */
130
- declare const BetterAuthCatchallController: Type;
67
+ export declare function createBetterAuthCatchallController(basePath?: string): Type;
131
68
  //#endregion
132
69
  //#region src/better-auth.tokens.d.ts
133
- declare const BETTER_AUTH_OPTIONS: InjectionToken<BetterAuthModuleOptions>;
134
- declare const AUTH_USER_KEY: unique symbol;
135
- declare const AUTH_SESSION_KEY: unique symbol;
136
- declare const AUTH_ISSUER_KEY: unique symbol;
137
- declare const AUTH_PRINCIPAL_TYPE_KEY: unique symbol;
70
+ export declare const BETTER_AUTH_OPTIONS: InjectionToken<BetterAuthRuntimeOptions>;
138
71
  //#endregion
139
72
  //#region src/guards/auth.guard.d.ts
140
- declare class AuthGuard implements CanActivate {
73
+ export declare class AuthGuard implements CanActivate {
141
74
  private readonly auth;
142
75
  private readonly opts;
143
76
  private readonly reflector;
144
- constructor(auth: BetterAuthService, opts: BetterAuthModuleOptions);
145
- canActivate(context: ExecutionContext): Promise<boolean>;
146
- }
147
- //#endregion
148
- //#region src/guards/roles.guard.d.ts
149
- declare class RolesGuard implements CanActivate {
150
- private readonly reflector;
151
- canActivate(context: ExecutionContext): boolean;
152
- }
153
- //#endregion
154
- //#region src/guards/permission.guard.d.ts
155
- /**
156
- * Enforces the `@RequirePermission(...)` metadata against the `@velajs/authz`
157
- * engine. For each required permission it calls `authz.can(identity, perm)`,
158
- * requiring **all** of them (AND semantics — contrast {@link RolesGuard}, which
159
- * is OR over roles). The caller's `Identity` is derived from the better-auth
160
- * user that {@link AuthGuard} placed in canonical request-local auth state, so this guard must
161
- * run *after* `AuthGuard` (e.g. `@UseGuards(AuthGuard, PermissionGuard)`).
162
- *
163
- * `AUTHZ` is resolved at request time from the per-request container. Exactly
164
- * one reachable engine is required; zero or multiple registrations deny rather
165
- * than selecting one by import order.
166
- *
167
- * Fail-closed on every abnormal path — no branch grants access on missing
168
- * wiring or a missing caller:
169
- * - no required permissions → allow (nothing to enforce);
170
- * - `AUTHZ` unresolvable (`AuthzModule` not registered) → deny (`ForbiddenException`);
171
- * - no authenticated user in request-local auth state → deny;
172
- * - any single required permission not granted → deny.
173
- *
174
- * The guard is stateless (no injected dependencies), so it is safe to register
175
- * as a plain provided guard.
176
- */
177
- declare class PermissionGuard implements CanActivate {
178
- private readonly reflector;
77
+ constructor(auth: BetterAuthService, opts: BetterAuthRuntimeOptions);
179
78
  canActivate(context: ExecutionContext): Promise<boolean>;
180
79
  }
181
80
  //#endregion
182
81
  //#region src/decorators/current-user.decorator.d.ts
183
- declare const CurrentUser: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
82
+ export declare const CurrentUser: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
184
83
  //#endregion
185
84
  //#region src/decorators/current-session.decorator.d.ts
186
- declare const CurrentSession: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
85
+ export declare const CurrentSession: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
187
86
  //#endregion
188
87
  //#region src/decorators/public.decorator.d.ts
189
- declare const Public: import("@velajs/vela").ReflectableDecorator<boolean>;
190
- declare const PUBLIC_KEY: string;
88
+ export declare const Public: import("@velajs/vela").ReflectableDecorator<boolean>;
89
+ export declare const PUBLIC_KEY: string;
191
90
  //#endregion
192
91
  //#region src/decorators/optional-auth.decorator.d.ts
193
- declare const OptionalAuth: import("@velajs/vela").ReflectableDecorator<boolean>;
194
- declare const OPTIONAL_AUTH_KEY: string;
195
- //#endregion
196
- //#region src/decorators/roles.decorator.d.ts
197
- declare const Roles: import("@velajs/vela").ReflectableDecorator<string[]>;
198
- declare const ROLES_KEY: string;
199
- //#endregion
200
- //#region src/decorators/require-permission.decorator.d.ts
201
- /**
202
- * Declares the `@velajs/authz` permission(s) required to reach a controller or
203
- * route handler. Read via `Reflector` in an authorization guard, then checked
204
- * against the caller's `Identity` with `authz.can(...)`.
205
- *
206
- * ```ts
207
- * @RequirePermission(['posts:write'])
208
- * @Post()
209
- * create() { ... }
210
- * ```
211
- *
212
- * The metadata is a plain `string[]` of permission strings in the granted-side
213
- * format `@velajs/authz` matches (`resource:action`, or wildcards like
214
- * `posts:*`). Handler-level metadata overrides class-level (standard
215
- * `Reflector.getAllAndOverride` precedence).
216
- *
217
- * Semantics are **require-ALL** (AND): every listed permission must be granted
218
- * for access — the `PermissionGuard` denies if any one is missing. This
219
- * contrasts with `@Roles`, which is **OR** (any one of the listed roles
220
- * suffices).
221
- */
222
- declare const RequirePermission: import("@velajs/vela").ReflectableDecorator<string[]>;
223
- declare const REQUIRE_PERMISSION_KEY: string;
92
+ export declare const OptionalAuth: import("@velajs/vela").ReflectableDecorator<boolean>;
93
+ export declare const OPTIONAL_AUTH_KEY: string;
224
94
  //#endregion
225
95
  //#region src/authz-bridge.d.ts
226
96
  /**
@@ -228,13 +98,14 @@ declare const REQUIRE_PERMISSION_KEY: string;
228
98
  * admin plugin. `role` may be a single role, a comma-separated list, or an
229
99
  * array — {@link identityFromUser} normalizes all three.
230
100
  */
231
- type AuthUser = User & {
101
+ type AuthUser = Pick<User, 'id'> & {
232
102
  role?: string | string[] | null;
233
103
  };
234
104
  /** Stable issuer namespace used for better-auth session principals. */
235
- declare const BETTER_AUTH_ISSUER = "better-auth";
105
+ export declare const BETTER_AUTH_ISSUER = "better-auth";
236
106
  /**
237
- * Adapts a better-auth user into a stable `@velajs/authz` {@link Identity}.
107
+ * Pure authorization projection; this does not authenticate or publish trusted state.
108
+ * Adapts an already verified better-auth user into a stable `@velajs/authz` {@link Identity}.
238
109
  * The issuer scopes `user.id` as both `subject` and the compatibility `userId`;
239
110
  * the admin-plugin `role` field supplies local roles.
240
111
  *
@@ -242,7 +113,7 @@ declare const BETTER_AUTH_ISSUER = "better-auth";
242
113
  * request) maps to the zero-privilege identity `{ roles: [] }`, so downstream
243
114
  * `can()` checks grant nothing.
244
115
  */
245
- declare const identityFromUser: (user: AuthUser | null | undefined, issuer?: string, principalType?: 'user' | 'service') => Identity;
116
+ export declare const identityFromUser: (user: AuthUser | null | undefined, issuer?: string, principalType?: 'user' | 'service') => Identity;
246
117
  /**
247
118
  * The minimal slice of a better-auth access-control role consumed here. Both
248
119
  * `createAccessControl(...).newRole(...)` and the standalone `role(...)` return
@@ -258,7 +129,7 @@ interface BetterAuthAcRole {
258
129
  * permission strings — the granted-side format `@velajs/authz` matches
259
130
  * (wildcards included).
260
131
  */
261
- declare const permissionsFromAcRole: (role: BetterAuthAcRole) => string[];
132
+ export declare const permissionsFromAcRole: (role: BetterAuthAcRole) => string[];
262
133
  /**
263
134
  * Builds a fail-closed `@velajs/authz` {@link PermissionResolver} from a
264
135
  * better-auth access-control role table (`{ roleName: acRole }` — the same map
@@ -274,7 +145,7 @@ declare const permissionsFromAcRole: (role: BetterAuthAcRole) => string[];
274
145
  * await authz.can(identityFromUser(user), 'posts:write');
275
146
  * ```
276
147
  */
277
- declare const betterAuthAcResolver: (roles: Readonly<Record<string, BetterAuthAcRole>>) => PermissionResolver;
148
+ export declare const betterAuthAcResolver: (roles: Readonly<Record<string, BetterAuthAcRole>>) => PermissionResolver;
278
149
  //#endregion
279
- export { AUTH_ISSUER_KEY, AUTH_PRINCIPAL_TYPE_KEY, AUTH_SESSION_KEY, AUTH_USER_KEY, AuthGuard, type AuthUser, BETTER_AUTH_ISSUER, BETTER_AUTH_OPTIONS, type BetterAuthAcRole, BetterAuthCatchallController, type BetterAuthInstance, BetterAuthModule, type BetterAuthModuleOptions, BetterAuthService, CurrentSession, CurrentUser, OPTIONAL_AUTH_KEY, OptionalAuth, PUBLIC_KEY, PermissionGuard, Public, REQUIRE_PERMISSION_KEY, ROLES_KEY, RequirePermission, Roles, RolesGuard, type Session, type User, betterAuthAcResolver, createBetterAuthCatchallController, identityFromUser, permissionsFromAcRole };
150
+ export { type AuthUser, type BetterAuthAcRole, type BetterAuthInstance, type BetterAuthModuleOptions, type BetterAuthRuntimeOptions, BetterAuthService, type Session, type User };
280
151
  //# sourceMappingURL=index.d.ts.map