@velajs/better-auth 0.6.0 → 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/CHANGELOG.md +12 -1
- package/README.md +19 -14
- package/dist/better-auth.service-BMkyFX-w.js +63 -0
- package/dist/better-auth.service-BMkyFX-w.js.map +1 -0
- package/dist/index.d.ts +280 -12
- package/dist/index.js +581 -15
- package/dist/index.js.map +1 -0
- package/dist/testing/index.d.ts +50 -2
- package/dist/testing/index.js +60 -3
- package/dist/testing/index.js.map +1 -0
- package/package.json +62 -42
- package/dist/better-auth.controller.d.ts +0 -21
- package/dist/better-auth.controller.js +0 -68
- package/dist/better-auth.module.d.ts +0 -52
- package/dist/better-auth.module.js +0 -138
- package/dist/better-auth.service.d.ts +0 -42
- package/dist/better-auth.service.js +0 -51
- package/dist/better-auth.tokens.d.ts +0 -5
- package/dist/better-auth.tokens.js +0 -4
- package/dist/better-auth.types.d.ts +0 -11
- package/dist/better-auth.types.js +0 -1
- package/dist/decorators/current-session.decorator.d.ts +0 -1
- package/dist/decorators/current-session.decorator.js +0 -7
- package/dist/decorators/current-user.decorator.d.ts +0 -1
- package/dist/decorators/current-user.decorator.js +0 -7
- package/dist/decorators/optional-auth.decorator.d.ts +0 -2
- package/dist/decorators/optional-auth.decorator.js +0 -5
- package/dist/decorators/public.decorator.d.ts +0 -2
- package/dist/decorators/public.decorator.js +0 -5
- package/dist/decorators/roles.decorator.d.ts +0 -2
- package/dist/decorators/roles.decorator.js +0 -5
- package/dist/guards/auth.guard.d.ts +0 -10
- package/dist/guards/auth.guard.js +0 -68
- package/dist/guards/roles.guard.d.ts +0 -5
- package/dist/guards/roles.guard.js +0 -36
- package/dist/testing/acting-as.d.ts +0 -46
- package/dist/testing/acting-as.js +0 -70
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- a468b57: Run guards before ordinary identity parameters, install authentication globally and deny application routes by default, remove the insecure `defaultPolicy: 'allow'` mode and implicit auth-path bypass, enforce canonical auth mount paths, reject ambiguous authorization engines, and key module instances by the actual auth/factory reference. Anonymous routes must now use explicit `@Public()` or `@OptionalAuth()` metadata. Verified sessions now publish Vela's framework-owned principal identity and, when present, the Better Auth organization plugin's `activeOrganizationId` so downstream throttling can partition by principal and tenant before IP fallback.
|
|
8
|
+
|
|
9
|
+
## 0.6.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 89f5473: Modernize the package build, validation, and release toolchain.
|
|
14
|
+
|
|
3
15
|
## 0.4.0 (2026-07-04)
|
|
4
16
|
|
|
5
17
|
- Rebuilt on vela 1.11 `defineModule` + `lazyProvider` + `provideGlobal` (lazy auth-builder deferral preserved; public API unchanged). Requires `@velajs/vela >=1.11.0`.
|
|
6
18
|
|
|
7
|
-
|
|
8
19
|
All notable changes to `@velajs/better-auth` are documented here. The format
|
|
9
20
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
10
21
|
|
package/README.md
CHANGED
|
@@ -8,13 +8,13 @@ pnpm add @velajs/better-auth better-auth
|
|
|
8
8
|
|
|
9
9
|
## Quick start
|
|
10
10
|
|
|
11
|
-
Construct your better-auth instance once
|
|
11
|
+
Construct your better-auth instance once and hand it to `BetterAuthModule.forRoot`. The module installs `AuthGuard` application-wide by default; use `@CurrentUser()` on authenticated routes and mark the small anonymous surface explicitly.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
14
|
import { betterAuth } from 'better-auth';
|
|
15
|
-
import { Module, Controller, Get,
|
|
15
|
+
import { Module, Controller, Get, VelaFactory } from '@velajs/vela';
|
|
16
16
|
import {
|
|
17
|
-
BetterAuthModule,
|
|
17
|
+
BetterAuthModule, CurrentUser, Public,
|
|
18
18
|
} from '@velajs/better-auth';
|
|
19
19
|
|
|
20
20
|
const auth = betterAuth({
|
|
@@ -24,7 +24,6 @@ const auth = betterAuth({
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
@Controller('/me')
|
|
27
|
-
@UseGuards(AuthGuard)
|
|
28
27
|
class MeController {
|
|
29
28
|
@Get() me(@CurrentUser() user: { id: string; email: string }) {
|
|
30
29
|
return { id: user.id, email: user.email };
|
|
@@ -35,7 +34,7 @@ class MeController {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
@Module({
|
|
38
|
-
imports: [BetterAuthModule.forRoot({ auth
|
|
37
|
+
imports: [BetterAuthModule.forRoot({ auth })],
|
|
39
38
|
controllers: [MeController],
|
|
40
39
|
})
|
|
41
40
|
class AppModule {}
|
|
@@ -51,13 +50,15 @@ export default app; // edge-compatible (.fetch)
|
|
|
51
50
|
```ts
|
|
52
51
|
BetterAuthModule.forRoot({
|
|
53
52
|
auth, // pre-constructed betterAuth({ ... }) instance
|
|
53
|
+
issuer: 'my-app:better-auth', // stable namespace paired with user ids
|
|
54
54
|
basePath: '/api/auth', // default — must match your better-auth config
|
|
55
|
-
isGlobal:
|
|
56
|
-
defaultPolicy: 'deny', // 'deny' | 'allow' for unauthenticated requests
|
|
55
|
+
isGlobal: true, // default — register AuthGuard as APP_GUARD
|
|
57
56
|
mountHandler: true, // mount /api/auth/* catch-all controller
|
|
58
57
|
});
|
|
59
58
|
```
|
|
60
59
|
|
|
60
|
+
Authentication has no allow-by-default compatibility mode. Use `@Public(true)` for routes that intentionally skip authentication, or `@OptionalAuth(true)` when the route accepts an anonymous identity. `isGlobal: false` is intended only for applications that install an equivalent global authentication guard themselves.
|
|
61
|
+
|
|
61
62
|
## Three composition patterns
|
|
62
63
|
|
|
63
64
|
### Pattern A — inline (simplest)
|
|
@@ -170,26 +171,30 @@ Under `forRootAsync`, the underlying `betterAuth({...})` instance is constructed
|
|
|
170
171
|
|
|
171
172
|
| Decorator | Purpose |
|
|
172
173
|
| ------------------- | ------------------------------------------------------------------------ |
|
|
173
|
-
| `@CurrentUser()` |
|
|
174
|
-
| `@CurrentSession()` |
|
|
174
|
+
| `@CurrentUser()` | Better-auth `User` from the request after guards run |
|
|
175
|
+
| `@CurrentSession()` | Better-auth `Session` after guards run |
|
|
175
176
|
| `@Public(true)` | Class or method — bypass AuthGuard entirely |
|
|
176
177
|
| `@OptionalAuth(true)` | Class or method — populate user if present, never throw 401 |
|
|
177
178
|
| `@Roles(['admin'])` | Method — read by `RolesGuard`. Compares against `user.role`. |
|
|
178
179
|
|
|
179
|
-
|
|
180
|
+
Optional identities are ordinary values: an anonymous caller receives the actual `undefined`, so normal truthiness checks are safe.
|
|
180
181
|
|
|
181
182
|
```ts
|
|
182
183
|
handle(@CurrentUser() user: User | undefined) {
|
|
183
|
-
return { hasUser: user
|
|
184
|
+
return { hasUser: Boolean(user) };
|
|
184
185
|
}
|
|
185
186
|
```
|
|
186
187
|
|
|
187
188
|
## Guards
|
|
188
189
|
|
|
189
|
-
- **`AuthGuard`** — singleton. Reads `Authorization` header / cookies via `auth.api.getSession`, populates `REQUEST_CONTEXT
|
|
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.
|
|
190
191
|
- **`RolesGuard`** — singleton. Reads `@Roles([...])` metadata, compares against `user.role`. Use with `@UseGuards(AuthGuard, RolesGuard)` — order matters.
|
|
191
192
|
|
|
192
|
-
Global registration
|
|
193
|
+
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
|
+
|
|
195
|
+
When using `ThrottlerModule`, import Better Auth first. Vela then rate-limits by
|
|
196
|
+
the verified issuer, subject, principal type, and active organization before it
|
|
197
|
+
falls back to a platform-attested client address.
|
|
193
198
|
|
|
194
199
|
## Edge-safe DB adapters
|
|
195
200
|
|
|
@@ -223,7 +228,7 @@ class CustomCatchallController {
|
|
|
223
228
|
}
|
|
224
229
|
```
|
|
225
230
|
|
|
226
|
-
Pass `basePath: '/auth'` to `BetterAuthModule.forRoot`
|
|
231
|
+
Pass `basePath: '/auth'` to `BetterAuthModule.forRoot` to mount the generated controller there, and keep your `betterAuth({ basePath: '/auth' })` config in sync. A custom catch-all must carry `@Public(true)` itself. Base paths must be canonical absolute paths: no root mount, trailing slash, wildcards, query/fragment, backslashes, or dot segments.
|
|
227
232
|
|
|
228
233
|
## License
|
|
229
234
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Inject, Injectable, InjectionToken } from "@velajs/vela";
|
|
2
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorateMetadata.js
|
|
3
|
+
function __decorateMetadata(k, v) {
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorateParam.js
|
|
8
|
+
function __decorateParam(paramIndex, decorator) {
|
|
9
|
+
return function(target, key) {
|
|
10
|
+
decorator(target, key, paramIndex);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorate.js
|
|
15
|
+
function __decorate(decorators, target, key, desc) {
|
|
16
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
17
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
18
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
19
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/better-auth.service.ts
|
|
23
|
+
/**
|
|
24
|
+
* Internal token holding the auth-construction closure with its inject deps
|
|
25
|
+
* closed over. Resolves cheaply at module load (just captures references);
|
|
26
|
+
* the inner call happens lazily on first auth use (see `BetterAuthService`).
|
|
27
|
+
*
|
|
28
|
+
* Not exported from the public surface — only the service consumes it.
|
|
29
|
+
*/
|
|
30
|
+
const BETTER_AUTH_BUILDER = new InjectionToken("vela.better-auth.Builder");
|
|
31
|
+
let BetterAuthService = class BetterAuthService {
|
|
32
|
+
build;
|
|
33
|
+
cached;
|
|
34
|
+
constructor(build) {
|
|
35
|
+
this.build = build;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The underlying better-auth instance. Constructed once on first access.
|
|
39
|
+
* Safe to call from any request-time code path (guards, controllers,
|
|
40
|
+
* services invoked from handlers).
|
|
41
|
+
*/
|
|
42
|
+
get auth() {
|
|
43
|
+
if (!this.cached) this.cached = this.build();
|
|
44
|
+
return this.cached;
|
|
45
|
+
}
|
|
46
|
+
/** Convenience accessor — equivalent to `service.auth.api`. */
|
|
47
|
+
get api() {
|
|
48
|
+
return this.auth.api;
|
|
49
|
+
}
|
|
50
|
+
/** Convenience accessor — equivalent to `service.auth.handler`. */
|
|
51
|
+
get handler() {
|
|
52
|
+
return this.auth.handler;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
BetterAuthService = __decorate([
|
|
56
|
+
Injectable(),
|
|
57
|
+
__decorateParam(0, Inject(BETTER_AUTH_BUILDER)),
|
|
58
|
+
__decorateMetadata("design:paramtypes", [Function])
|
|
59
|
+
], BetterAuthService);
|
|
60
|
+
//#endregion
|
|
61
|
+
export { __decorateMetadata as a, __decorateParam as i, BetterAuthService as n, __decorate as r, BETTER_AUTH_BUILDER as t };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=better-auth.service-BMkyFX-w.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-auth.service-BMkyFX-w.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 bindings are populated 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 {\n private cached: BetterAuthInstance | undefined;\n\n constructor(@Inject(BETTER_AUTH_BUILDER) private readonly build: () => BetterAuthInstance) {}\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(): BetterAuthInstance {\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(): BetterAuthInstance['api'] {\n return this.auth.api;\n }\n\n /** Convenience accessor — equivalent to `service.auth.handler`. */\n get handler(): BetterAuthInstance['handler'] {\n return this.auth.handler;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,MAAa,sBAAsB,IAAI,eACrC,0BACF;AAoBO,IAAA,oBAAA,MAAM,kBAAkB;CAG6B;CAF1D;CAEA,YAAY,OAA+E;EAAjC,KAAA,QAAA;CAAkC;;;;;;CAO5F,IAAI,OAA2B;EAC7B,IAAI,CAAC,KAAK,QAAQ,KAAK,SAAS,KAAK,MAAM;EAC3C,OAAO,KAAK;CACd;;CAGA,IAAI,MAAiC;EACnC,OAAO,KAAK,KAAK;CACnB;;CAGA,IAAI,UAAyC;EAC3C,OAAO,KAAK,KAAK;CACnB;AACF;;CAzBC,WAAW;oBAIG,OAAO,mBAAmB,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,280 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
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
|
+
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
|
+
//#region src/better-auth.module.d.ts
|
|
24
|
+
/**
|
|
25
|
+
* Options for {@link BetterAuthModule.forRootAsync}.
|
|
26
|
+
*
|
|
27
|
+
* The `Inject` type parameter captures the literal `inject` tuple at the call
|
|
28
|
+
* site (via `const` inference) so `useFactory` parameters are typed from the
|
|
29
|
+
* inject array, position-by-position — no `as const`, no `(...deps: any[])`:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* 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), ...) }),
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
interface ForRootAsyncOptions<Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]> {
|
|
40
|
+
inject?: Inject;
|
|
41
|
+
imports?: DynamicModule['imports'];
|
|
42
|
+
useFactory: (...deps: InferTokens<Inject>) => BetterAuthInstance;
|
|
43
|
+
isGlobal?: boolean;
|
|
44
|
+
mountHandler?: boolean;
|
|
45
|
+
basePath?: string;
|
|
46
|
+
issuer?: string;
|
|
47
|
+
/** @deprecated Authentication is deny-by-default. Only `'deny'` is accepted. */
|
|
48
|
+
defaultPolicy?: 'deny';
|
|
49
|
+
key?: string;
|
|
50
|
+
}
|
|
51
|
+
declare class BetterAuthModule {
|
|
52
|
+
/**
|
|
53
|
+
* Synchronous registration. The auth instance is constructed by the consumer
|
|
54
|
+
* at module-load time and passed in directly. Use this when the inputs to
|
|
55
|
+
* `betterAuth({...})` are available at startup (Node apps with a static DB
|
|
56
|
+
* connection, in-memory adapters, etc.).
|
|
57
|
+
*/
|
|
58
|
+
static forRoot(options: BetterAuthModuleOptions & {
|
|
59
|
+
isGlobal?: boolean;
|
|
60
|
+
key?: string;
|
|
61
|
+
}): DynamicModule;
|
|
62
|
+
/**
|
|
63
|
+
* Deferred / DI-driven registration. The user factory runs **lazily**, on the
|
|
64
|
+
* first time anything reads `BetterAuthService.auth` (or `.api` / `.handler`).
|
|
65
|
+
* In normal request handling that's `AuthGuard.canActivate` or the catch-all
|
|
66
|
+
* 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).
|
|
102
|
+
*/
|
|
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'];
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/better-auth.controller.d.ts
|
|
111
|
+
/**
|
|
112
|
+
* Build a catch-all controller that mounts better-auth's handler at `basePath`
|
|
113
|
+
* (default `/api/auth`). This is a factory because vela reads a controller's
|
|
114
|
+
* route off the class at decoration time, so a custom base path needs its own
|
|
115
|
+
* decorated class — the path can't be parametrized on a single shared class.
|
|
116
|
+
*
|
|
117
|
+
* Two base paths to keep consistent:
|
|
118
|
+
* - this `basePath` is RELATIVE to vela's `globalPrefix` (always prepended);
|
|
119
|
+
* - better-auth routes against its OWN absolute `basePath` (the one you pass to
|
|
120
|
+
* `betterAuth({ basePath })`), which must equal `globalPrefix + basePath`.
|
|
121
|
+
*
|
|
122
|
+
* Both default to `/api/auth`, so the no-prefix / no-config case just works.
|
|
123
|
+
*/
|
|
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;
|
|
131
|
+
//#endregion
|
|
132
|
+
//#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;
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/guards/auth.guard.d.ts
|
|
140
|
+
declare class AuthGuard implements CanActivate {
|
|
141
|
+
private readonly auth;
|
|
142
|
+
private readonly opts;
|
|
143
|
+
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;
|
|
179
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/decorators/current-user.decorator.d.ts
|
|
183
|
+
declare const CurrentUser: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/decorators/current-session.decorator.d.ts
|
|
186
|
+
declare const CurrentSession: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/decorators/public.decorator.d.ts
|
|
189
|
+
declare const Public: import("@velajs/vela").ReflectableDecorator<boolean>;
|
|
190
|
+
declare const PUBLIC_KEY: string;
|
|
191
|
+
//#endregion
|
|
192
|
+
//#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;
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/authz-bridge.d.ts
|
|
226
|
+
/**
|
|
227
|
+
* A better-auth user carrying the optional `role` field contributed by the
|
|
228
|
+
* admin plugin. `role` may be a single role, a comma-separated list, or an
|
|
229
|
+
* array — {@link identityFromUser} normalizes all three.
|
|
230
|
+
*/
|
|
231
|
+
type AuthUser = User & {
|
|
232
|
+
role?: string | string[] | null;
|
|
233
|
+
};
|
|
234
|
+
/** Stable issuer namespace used for better-auth session principals. */
|
|
235
|
+
declare const BETTER_AUTH_ISSUER = "better-auth";
|
|
236
|
+
/**
|
|
237
|
+
* Adapts a better-auth user into a stable `@velajs/authz` {@link Identity}.
|
|
238
|
+
* The issuer scopes `user.id` as both `subject` and the compatibility `userId`;
|
|
239
|
+
* the admin-plugin `role` field supplies local roles.
|
|
240
|
+
*
|
|
241
|
+
* Fail-closed: a missing user (`null`/`undefined`, i.e. an unauthenticated
|
|
242
|
+
* request) maps to the zero-privilege identity `{ roles: [] }`, so downstream
|
|
243
|
+
* `can()` checks grant nothing.
|
|
244
|
+
*/
|
|
245
|
+
declare const identityFromUser: (user: AuthUser | null | undefined, issuer?: string, principalType?: 'user' | 'service') => Identity;
|
|
246
|
+
/**
|
|
247
|
+
* The minimal slice of a better-auth access-control role consumed here. Both
|
|
248
|
+
* `createAccessControl(...).newRole(...)` and the standalone `role(...)` return
|
|
249
|
+
* `{ authorize, statements }`; `statements` is the `{ resource: actions[] }`
|
|
250
|
+
* grant map for that role — the only accessor {@link betterAuthAcResolver}
|
|
251
|
+
* reads.
|
|
252
|
+
*/
|
|
253
|
+
interface BetterAuthAcRole {
|
|
254
|
+
readonly statements: Readonly<Record<string, readonly string[]>>;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Flattens a better-auth AC role's `statements` into `resource:action`
|
|
258
|
+
* permission strings — the granted-side format `@velajs/authz` matches
|
|
259
|
+
* (wildcards included).
|
|
260
|
+
*/
|
|
261
|
+
declare const permissionsFromAcRole: (role: BetterAuthAcRole) => string[];
|
|
262
|
+
/**
|
|
263
|
+
* Builds a fail-closed `@velajs/authz` {@link PermissionResolver} from a
|
|
264
|
+
* better-auth access-control role table (`{ roleName: acRole }` — the same map
|
|
265
|
+
* shape passed to better-auth's admin/organization plugins). An identity's
|
|
266
|
+
* `roles` are unioned into their granted permission strings; unknown roles
|
|
267
|
+
* contribute nothing.
|
|
268
|
+
*
|
|
269
|
+
* ```ts
|
|
270
|
+
* const ac = createAccessControl({ posts: ['read', 'write'] });
|
|
271
|
+
* const authz = createAuthz({
|
|
272
|
+
* resolver: betterAuthAcResolver({ editor: ac.newRole({ posts: ['write'] }) }),
|
|
273
|
+
* });
|
|
274
|
+
* await authz.can(identityFromUser(user), 'posts:write');
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
declare const betterAuthAcResolver: (roles: Readonly<Record<string, BetterAuthAcRole>>) => PermissionResolver;
|
|
278
|
+
//#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 };
|
|
280
|
+
//# sourceMappingURL=index.d.ts.map
|