@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +19 -14
  3. package/dist/better-auth.service-BMkyFX-w.js +63 -0
  4. package/dist/better-auth.service-BMkyFX-w.js.map +1 -0
  5. package/dist/index.d.ts +280 -12
  6. package/dist/index.js +581 -15
  7. package/dist/index.js.map +1 -0
  8. package/dist/testing/index.d.ts +50 -2
  9. package/dist/testing/index.js +60 -3
  10. package/dist/testing/index.js.map +1 -0
  11. package/package.json +62 -42
  12. package/dist/better-auth.controller.d.ts +0 -21
  13. package/dist/better-auth.controller.js +0 -68
  14. package/dist/better-auth.module.d.ts +0 -52
  15. package/dist/better-auth.module.js +0 -138
  16. package/dist/better-auth.service.d.ts +0 -42
  17. package/dist/better-auth.service.js +0 -51
  18. package/dist/better-auth.tokens.d.ts +0 -5
  19. package/dist/better-auth.tokens.js +0 -4
  20. package/dist/better-auth.types.d.ts +0 -11
  21. package/dist/better-auth.types.js +0 -1
  22. package/dist/decorators/current-session.decorator.d.ts +0 -1
  23. package/dist/decorators/current-session.decorator.js +0 -7
  24. package/dist/decorators/current-user.decorator.d.ts +0 -1
  25. package/dist/decorators/current-user.decorator.js +0 -7
  26. package/dist/decorators/optional-auth.decorator.d.ts +0 -2
  27. package/dist/decorators/optional-auth.decorator.js +0 -5
  28. package/dist/decorators/public.decorator.d.ts +0 -2
  29. package/dist/decorators/public.decorator.js +0 -5
  30. package/dist/decorators/roles.decorator.d.ts +0 -2
  31. package/dist/decorators/roles.decorator.js +0 -5
  32. package/dist/guards/auth.guard.d.ts +0 -10
  33. package/dist/guards/auth.guard.js +0 -68
  34. package/dist/guards/roles.guard.d.ts +0 -5
  35. package/dist/guards/roles.guard.js +0 -36
  36. package/dist/testing/acting-as.d.ts +0 -46
  37. package/dist/testing/acting-as.js +0 -70
@@ -1,138 +0,0 @@
1
- import { defineModule, lazyProvider, provideGlobal, stableHash } from "@velajs/vela";
2
- import { createBetterAuthCatchallController } from "./better-auth.controller.js";
3
- import { BetterAuthService, BETTER_AUTH_BUILDER } from "./better-auth.service.js";
4
- import { BETTER_AUTH_OPTIONS } from "./better-auth.tokens.js";
5
- import { AuthGuard } from "./guards/auth.guard.js";
6
- import { RolesGuard } from "./guards/roles.guard.js";
7
- const DEFAULT_BASE_PATH = '/api/auth';
8
- function normalize(options) {
9
- return {
10
- basePath: options.basePath ?? DEFAULT_BASE_PATH,
11
- isGlobal: options.isGlobal ?? false,
12
- defaultPolicy: options.defaultPolicy ?? 'deny',
13
- mountHandler: options.mountHandler ?? true
14
- };
15
- }
16
- /** Providers, controllers, and exports shared by both entry points. */ function commonContributions(n) {
17
- return {
18
- providers: [
19
- BetterAuthService,
20
- AuthGuard,
21
- RolesGuard
22
- ],
23
- controllers: n.mountHandler ? [
24
- createBetterAuthCatchallController(n.basePath)
25
- ] : [],
26
- exports: [
27
- BetterAuthService,
28
- BETTER_AUTH_OPTIONS,
29
- AuthGuard,
30
- RolesGuard
31
- ]
32
- };
33
- }
34
- /**
35
- * The blessed engine generates `forRoot`. `setup` runs once per instance at
36
- * call time: it re-provides {@link BETTER_AUTH_OPTIONS} with defaults applied,
37
- * derives the auth builder from those options, mounts the catch-all controller,
38
- * and — via the `global:` slot — registers the app-wide guard when `isGlobal`.
39
- *
40
- * `isGlobal` here means "apply AuthGuard app-wide", NOT "make this a global
41
- * module", so the default `isGlobal → global: true` extras transform is
42
- * replaced with identity; the flag reaches `setup` through the options bag.
43
- */ const authModuleHost = defineModule({
44
- name: 'BetterAuth',
45
- optionsToken: BETTER_AUTH_OPTIONS,
46
- transform: (definition)=>definition,
47
- // The auth instance is a stateful value — key off the structural subset only.
48
- key: (options)=>stableHash(normalize(options)),
49
- setup: ({ OPTIONS, options })=>{
50
- const n = normalize(options);
51
- const common = commonContributions(n);
52
- const auth = options.auth;
53
- return {
54
- providers: [
55
- // Override the auto-provided raw bag with the normalized shape so
56
- // BETTER_AUTH_OPTIONS consumers always see defaults + the auth instance.
57
- {
58
- provide: OPTIONS,
59
- useValue: {
60
- ...n,
61
- auth
62
- }
63
- },
64
- // Eager auth: the builder hands back the instance the caller passed in.
65
- lazyProvider({
66
- provide: BETTER_AUTH_BUILDER,
67
- inject: [
68
- OPTIONS
69
- ],
70
- useFactory: (o)=>o.auth
71
- }),
72
- ...common.providers
73
- ],
74
- controllers: common.controllers,
75
- exports: common.exports,
76
- global: n.isGlobal ? {
77
- guards: [
78
- AuthGuard
79
- ]
80
- } : undefined
81
- };
82
- }
83
- });
84
- export class BetterAuthModule {
85
- /**
86
- * Synchronous registration. The auth instance is constructed by the consumer
87
- * at module-load time and passed in directly. Use this when the inputs to
88
- * `betterAuth({...})` are available at startup (Node apps with a static DB
89
- * connection, in-memory adapters, etc.).
90
- */ static forRoot(options) {
91
- // Delegate to the generated static, then rebrand the module identity so the
92
- // public `BetterAuthModule` class is the one registered (consistent with
93
- // `forRootAsync` and better diagnostics).
94
- return {
95
- ...authModuleHost.ConfigurableModuleClass.forRoot(options),
96
- module: BetterAuthModule
97
- };
98
- }
99
- /**
100
- * Deferred / DI-driven registration. The user factory runs **lazily**, on the
101
- * first time anything reads `BetterAuthService.auth` (or `.api` / `.handler`).
102
- * In normal request handling that's `AuthGuard.canActivate` or the catch-all
103
- * controller's `.handle`. At module load the factory does NOT run — it's only
104
- * captured behind {@link lazyProvider}'s memoized thunk. This is what makes
105
- * Cloudflare bindings (D1, KV, R2) work: the binding isn't ready at boot, but
106
- * it IS by the time a request flows through and the guard / catch-all reads
107
- * the service. Inject deps resolve at module load (cheap BindingRef wrappers);
108
- * their *values* are read at first auth use, inside your factory body.
109
- */ static forRootAsync(options) {
110
- const n = normalize(options);
111
- const common = commonContributions(n);
112
- return {
113
- module: BetterAuthModule,
114
- key: options.key ?? stableHash({
115
- ...n,
116
- inject: options.inject
117
- }),
118
- imports: options.imports ?? [],
119
- providers: [
120
- {
121
- provide: BETTER_AUTH_OPTIONS,
122
- useValue: n
123
- },
124
- // The deferred auth builder: `lazyProvider` wraps the user factory in a
125
- // memoized thunk, replacing the hand-rolled `(...deps) => () => f(...deps)`.
126
- lazyProvider({
127
- provide: BETTER_AUTH_BUILDER,
128
- inject: options.inject,
129
- useFactory: options.useFactory
130
- }),
131
- ...common.providers,
132
- ...n.isGlobal ? provideGlobal('guard', AuthGuard) : []
133
- ],
134
- controllers: common.controllers,
135
- exports: common.exports
136
- };
137
- }
138
- }
@@ -1,42 +0,0 @@
1
- import { InjectionToken } from '@velajs/vela';
2
- import type { BetterAuthInstance } from './better-auth.types';
3
- /**
4
- * Internal token holding the auth-construction closure with its inject deps
5
- * closed over. Resolves cheaply at module load (just captures references);
6
- * the inner call happens lazily on first auth use (see `BetterAuthService`).
7
- *
8
- * Not exported from the public surface — only the service consumes it.
9
- */
10
- export declare const BETTER_AUTH_BUILDER: InjectionToken<() => BetterAuthInstance>;
11
- /**
12
- * The single injectable consumers reach for to interact with better-auth.
13
- * Wraps the underlying `betterAuth({...})` instance with lazy construction:
14
- *
15
- * - `forRoot({ auth })` — the builder returns the eagerly-provided instance,
16
- * so the first `.auth` / `.api` / `.handler` access is effectively a
17
- * read-and-cache.
18
- * - `forRootAsync({ inject, useFactory })` — the builder wraps the user's
19
- * factory + inject deps. First access triggers `useFactory(...deps)`. This
20
- * is what makes Cloudflare D1/KV bindings work: at module load the factory
21
- * doesn't run; on first request (when AuthGuard or the catch-all calls
22
- * `service.api` / `service.handler`), the bindings are populated and the
23
- * factory can read them safely.
24
- *
25
- * Used directly by AuthGuard and the catch-all controller. Consumers in
26
- * application code inject the same way: `@Inject(BetterAuthService)`.
27
- */
28
- export declare class BetterAuthService {
29
- private readonly build;
30
- private cached;
31
- constructor(build: () => BetterAuthInstance);
32
- /**
33
- * The underlying better-auth instance. Constructed once on first access.
34
- * Safe to call from any request-time code path (guards, controllers,
35
- * services invoked from handlers).
36
- */
37
- get auth(): BetterAuthInstance;
38
- /** Convenience accessor — equivalent to `service.auth.api`. */
39
- get api(): BetterAuthInstance['api'];
40
- /** Convenience accessor — equivalent to `service.auth.handler`. */
41
- get handler(): BetterAuthInstance['handler'];
42
- }
@@ -1,51 +0,0 @@
1
- function _ts_decorate(decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- 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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- }
7
- function _ts_metadata(k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- }
10
- function _ts_param(paramIndex, decorator) {
11
- return function(target, key) {
12
- decorator(target, key, paramIndex);
13
- };
14
- }
15
- import { Inject, Injectable, InjectionToken } from "@velajs/vela";
16
- /**
17
- * Internal token holding the auth-construction closure with its inject deps
18
- * closed over. Resolves cheaply at module load (just captures references);
19
- * the inner call happens lazily on first auth use (see `BetterAuthService`).
20
- *
21
- * Not exported from the public surface — only the service consumes it.
22
- */ export const BETTER_AUTH_BUILDER = new InjectionToken('vela.better-auth.Builder');
23
- export class BetterAuthService {
24
- build;
25
- cached;
26
- constructor(build){
27
- this.build = build;
28
- }
29
- /**
30
- * The underlying better-auth instance. Constructed once on first access.
31
- * Safe to call from any request-time code path (guards, controllers,
32
- * services invoked from handlers).
33
- */ get auth() {
34
- if (!this.cached) this.cached = this.build();
35
- return this.cached;
36
- }
37
- /** Convenience accessor — equivalent to `service.auth.api`. */ get api() {
38
- return this.auth.api;
39
- }
40
- /** Convenience accessor — equivalent to `service.auth.handler`. */ get handler() {
41
- return this.auth.handler;
42
- }
43
- }
44
- BetterAuthService = _ts_decorate([
45
- Injectable(),
46
- _ts_param(0, Inject(BETTER_AUTH_BUILDER)),
47
- _ts_metadata("design:type", Function),
48
- _ts_metadata("design:paramtypes", [
49
- Function
50
- ])
51
- ], BetterAuthService);
@@ -1,5 +0,0 @@
1
- import { InjectionToken } from '@velajs/vela';
2
- import type { BetterAuthModuleOptions } from './better-auth.types';
3
- export declare const BETTER_AUTH_OPTIONS: InjectionToken<BetterAuthModuleOptions>;
4
- export declare const AUTH_USER_KEY: unique symbol;
5
- export declare const AUTH_SESSION_KEY: unique symbol;
@@ -1,4 +0,0 @@
1
- import { InjectionToken } from "@velajs/vela";
2
- export const BETTER_AUTH_OPTIONS = new InjectionToken('vela.BetterAuthOptions');
3
- export const AUTH_USER_KEY = Symbol.for('vela.better-auth.user');
4
- export const AUTH_SESSION_KEY = Symbol.for('vela.better-auth.session');
@@ -1,11 +0,0 @@
1
- import type { Auth, Session as BASession, User as BAUser } from 'better-auth';
2
- export type BetterAuthInstance = Auth<any>;
3
- export interface BetterAuthModuleOptions {
4
- auth: BetterAuthInstance;
5
- basePath?: string;
6
- isGlobal?: boolean;
7
- defaultPolicy?: 'deny' | 'allow';
8
- mountHandler?: boolean;
9
- }
10
- export type User = BAUser;
11
- export type Session = BASession;
@@ -1 +0,0 @@
1
- export { };
@@ -1 +0,0 @@
1
- export declare const CurrentSession: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
@@ -1,7 +0,0 @@
1
- import { createLazyParamDecorator, REQUEST_CONTEXT } from "@velajs/vela";
2
- import { AUTH_SESSION_KEY } from "../better-auth.tokens.js";
3
- export const CurrentSession = createLazyParamDecorator((_data, ctx)=>{
4
- const honoCtx = ctx.getContext();
5
- const reqCtx = honoCtx.get('container').resolve(REQUEST_CONTEXT);
6
- return reqCtx.get(AUTH_SESSION_KEY);
7
- });
@@ -1 +0,0 @@
1
- export declare const CurrentUser: (data?: unknown, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
@@ -1,7 +0,0 @@
1
- import { createLazyParamDecorator, REQUEST_CONTEXT } from "@velajs/vela";
2
- import { AUTH_USER_KEY } from "../better-auth.tokens.js";
3
- export const CurrentUser = createLazyParamDecorator((_data, ctx)=>{
4
- const honoCtx = ctx.getContext();
5
- const reqCtx = honoCtx.get('container').resolve(REQUEST_CONTEXT);
6
- return reqCtx.get(AUTH_USER_KEY);
7
- });
@@ -1,2 +0,0 @@
1
- export declare const OptionalAuth: import("@velajs/vela").ReflectableDecorator<boolean>;
2
- export declare const OPTIONAL_AUTH_KEY: string;
@@ -1,5 +0,0 @@
1
- import { Reflector } from "@velajs/vela";
2
- export const OptionalAuth = Reflector.createDecorator({
3
- key: 'vela.auth.optional'
4
- });
5
- export const OPTIONAL_AUTH_KEY = OptionalAuth.KEY;
@@ -1,2 +0,0 @@
1
- export declare const Public: import("@velajs/vela").ReflectableDecorator<boolean>;
2
- export declare const PUBLIC_KEY: string;
@@ -1,5 +0,0 @@
1
- import { Reflector } from "@velajs/vela";
2
- export const Public = Reflector.createDecorator({
3
- key: 'vela.auth.public'
4
- });
5
- export const PUBLIC_KEY = Public.KEY;
@@ -1,2 +0,0 @@
1
- export declare const Roles: import("@velajs/vela").ReflectableDecorator<string[]>;
2
- export declare const ROLES_KEY: string;
@@ -1,5 +0,0 @@
1
- import { Reflector } from "@velajs/vela";
2
- export const Roles = Reflector.createDecorator({
3
- key: 'vela.auth.roles'
4
- });
5
- export const ROLES_KEY = Roles.KEY;
@@ -1,10 +0,0 @@
1
- import { type CanActivate, type ExecutionContext } from '@velajs/vela';
2
- import { BetterAuthService } from '../better-auth.service';
3
- import type { BetterAuthModuleOptions } from '../better-auth.types';
4
- export declare class AuthGuard implements CanActivate {
5
- private readonly auth;
6
- private readonly opts;
7
- private readonly reflector;
8
- constructor(auth: BetterAuthService, opts: BetterAuthModuleOptions);
9
- canActivate(context: ExecutionContext): Promise<boolean>;
10
- }
@@ -1,68 +0,0 @@
1
- function _ts_decorate(decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- 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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- }
7
- function _ts_metadata(k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- }
10
- function _ts_param(paramIndex, decorator) {
11
- return function(target, key) {
12
- decorator(target, key, paramIndex);
13
- };
14
- }
15
- import { Inject, Injectable, REQUEST_CONTEXT, Reflector, UnauthorizedException } from "@velajs/vela";
16
- import { AUTH_SESSION_KEY, AUTH_USER_KEY, BETTER_AUTH_OPTIONS } from "../better-auth.tokens.js";
17
- import { BetterAuthService } from "../better-auth.service.js";
18
- import { OptionalAuth } from "../decorators/optional-auth.decorator.js";
19
- import { Public } from "../decorators/public.decorator.js";
20
- export class AuthGuard {
21
- auth;
22
- opts;
23
- reflector = new Reflector();
24
- constructor(// Inject BetterAuthService rather than the raw better-auth instance.
25
- // The service's lazy `.auth` getter defers construction to first use, so
26
- // forRootAsync factories that depend on values only available at
27
- // request time (Cloudflare D1/KV bindings, etc.) build safely on the
28
- // first canActivate — not at module-load bootstrap.
29
- auth, opts){
30
- this.auth = auth;
31
- this.opts = opts;
32
- }
33
- async canActivate(context) {
34
- if (this.reflector.getAllAndOverride(Public, context)) return true;
35
- const request = context.getRequest();
36
- const path = new URL(request.url).pathname;
37
- const basePath = this.opts.basePath ?? '/api/auth';
38
- if (path === basePath || path.startsWith(`${basePath}/`)) return true;
39
- const data = await this.auth.api.getSession({
40
- headers: request.headers
41
- });
42
- if (data) {
43
- const reqCtx = resolveRequestContext(context);
44
- reqCtx.set(AUTH_USER_KEY, data.user);
45
- reqCtx.set(AUTH_SESSION_KEY, data.session);
46
- return true;
47
- }
48
- if (this.opts.defaultPolicy === 'allow' || this.reflector.getAllAndOverride(OptionalAuth, context)) {
49
- return true;
50
- }
51
- throw new UnauthorizedException('Authentication required');
52
- }
53
- }
54
- AuthGuard = _ts_decorate([
55
- Injectable(),
56
- _ts_param(0, Inject(BetterAuthService)),
57
- _ts_param(1, Inject(BETTER_AUTH_OPTIONS)),
58
- _ts_metadata("design:type", Function),
59
- _ts_metadata("design:paramtypes", [
60
- typeof BetterAuthService === "undefined" ? Object : BetterAuthService,
61
- typeof BetterAuthModuleOptions === "undefined" ? Object : BetterAuthModuleOptions
62
- ])
63
- ], AuthGuard);
64
- function resolveRequestContext(context) {
65
- const honoCtx = context.getContext();
66
- const container = honoCtx.get('container');
67
- return container.resolve(REQUEST_CONTEXT);
68
- }
@@ -1,5 +0,0 @@
1
- import { type CanActivate, type ExecutionContext } from '@velajs/vela';
2
- export declare class RolesGuard implements CanActivate {
3
- private readonly reflector;
4
- canActivate(context: ExecutionContext): boolean;
5
- }
@@ -1,36 +0,0 @@
1
- function _ts_decorate(decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- 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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- }
7
- import { ForbiddenException, Injectable, REQUEST_CONTEXT, Reflector } from "@velajs/vela";
8
- import { AUTH_USER_KEY } from "../better-auth.tokens.js";
9
- import { Roles } from "../decorators/roles.decorator.js";
10
- export class RolesGuard {
11
- reflector = new Reflector();
12
- canActivate(context) {
13
- const required = this.reflector.getAllAndOverride(Roles, context);
14
- if (!required || required.length === 0) return true;
15
- const honoCtx = context.getContext();
16
- const reqCtx = honoCtx.get('container').resolve(REQUEST_CONTEXT);
17
- const user = reqCtx.get(AUTH_USER_KEY);
18
- if (!user) {
19
- throw new ForbiddenException('Role check requires authentication');
20
- }
21
- const userRoles = normalizeRoles(user.role);
22
- const ok = required.some((r)=>userRoles.includes(r));
23
- if (!ok) {
24
- throw new ForbiddenException(`Insufficient role; one of [${required.join(', ')}] required`);
25
- }
26
- return true;
27
- }
28
- }
29
- RolesGuard = _ts_decorate([
30
- Injectable()
31
- ], RolesGuard);
32
- function normalizeRoles(role) {
33
- if (!role) return [];
34
- if (Array.isArray(role)) return role;
35
- return role.split(',').map((r)=>r.trim()).filter(Boolean);
36
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * The slice of `@velajs/testing`'s `TestingModule` this resolver depends on.
3
- * Declared structurally so `@velajs/better-auth/testing` carries NO runtime
4
- * (or type) dependency on `@velajs/testing` — a real `TestingModule` satisfies
5
- * it, and the resolver stays assignable to `@velajs/testing`'s `ActingAsResolver`.
6
- */
7
- export interface TestModuleLike {
8
- get<T>(token: unknown): T;
9
- }
10
- /**
11
- * A test principal. Opaque `Record<string, unknown>` to stay compatible with
12
- * `@velajs/testing`'s `TestPrincipal`. Recognized fields:
13
- *
14
- * - `id` — reuse the user with this id if it already exists.
15
- * - `email` — reuse the user with this email, else create one.
16
- * - `name` — display name for a created user (defaults to the email).
17
- *
18
- * Any other fields are forwarded to `internalAdapter.createUser` (e.g. `role`)
19
- * when a new user is minted, so role-guarded routes can be exercised.
20
- */
21
- export type ActingAsPrincipal = Record<string, unknown>;
22
- /**
23
- * actingAs — a `@velajs/testing` auth resolver for better-auth.
24
- *
25
- * Resolves {@link BetterAuthService} from the module, mints a REAL better-auth
26
- * session for `principal` through `auth.$context.internalAdapter`, and returns
27
- * a `Headers` carrying a properly signed session cookie. Guarded routes
28
- * (`AuthGuard`) then accept requests carrying those headers because
29
- * `auth.api.getSession` validates the cookie against the same session store.
30
- *
31
- * The signature `(module, principal) => Promise<Headers>` is exactly
32
- * `@velajs/testing`'s `ActingAsResolver`, so it plugs straight in:
33
- *
34
- * @example
35
- * ```ts
36
- * import { actingAs } from '@velajs/better-auth/testing';
37
- *
38
- * // As the default resolver for the module:
39
- * module.setAuthResolver(actingAs);
40
- * await module.http.get('/me').actingAs({ email: 'ada@example.com' }).send();
41
- *
42
- * // Or passed per-request:
43
- * await module.http.get('/me').actingAs({ id: existingUserId }, actingAs).send();
44
- * ```
45
- */
46
- export declare function actingAs(module: TestModuleLike, principal: ActingAsPrincipal): Promise<Headers>;
@@ -1,70 +0,0 @@
1
- // Mechanics adapted from @stratal/testing (MIT, © Temitayo Fadojutimi):
2
- // mint a real better-auth session via `$context.internalAdapter` and hand back
3
- // a signed session-cookie header. Adapted to better-auth >=1.6: the session
4
- // cookie is signed with the package's own `makeSignature` (better-auth/crypto)
5
- // — the same primitive better-auth's built-in test cookie builder uses — so no
6
- // endpoint-context shim or `setSessionCookie` mock is needed.
7
- import { makeSignature } from "better-auth/crypto";
8
- import { BetterAuthService } from "../better-auth.service.js";
9
- /**
10
- * actingAs — a `@velajs/testing` auth resolver for better-auth.
11
- *
12
- * Resolves {@link BetterAuthService} from the module, mints a REAL better-auth
13
- * session for `principal` through `auth.$context.internalAdapter`, and returns
14
- * a `Headers` carrying a properly signed session cookie. Guarded routes
15
- * (`AuthGuard`) then accept requests carrying those headers because
16
- * `auth.api.getSession` validates the cookie against the same session store.
17
- *
18
- * The signature `(module, principal) => Promise<Headers>` is exactly
19
- * `@velajs/testing`'s `ActingAsResolver`, so it plugs straight in:
20
- *
21
- * @example
22
- * ```ts
23
- * import { actingAs } from '@velajs/better-auth/testing';
24
- *
25
- * // As the default resolver for the module:
26
- * module.setAuthResolver(actingAs);
27
- * await module.http.get('/me').actingAs({ email: 'ada@example.com' }).send();
28
- *
29
- * // Or passed per-request:
30
- * await module.http.get('/me').actingAs({ id: existingUserId }, actingAs).send();
31
- * ```
32
- */ export async function actingAs(module, principal) {
33
- const auth = module.get(BetterAuthService).auth;
34
- const ctx = await auth.$context;
35
- const internalAdapter = ctx.internalAdapter;
36
- const id = typeof principal.id === 'string' ? principal.id : undefined;
37
- const email = typeof principal.email === 'string' ? principal.email : undefined;
38
- const name = typeof principal.name === 'string' ? principal.name : undefined;
39
- // `findUserById`/`createUser` yield a bare user; `findUserByEmail` nests it
40
- // under `{ user, accounts }` — normalize to the id we need.
41
- let user = null;
42
- if (id) user = await internalAdapter.findUserById(id);
43
- if (!user && email) {
44
- const found = await internalAdapter.findUserByEmail(email);
45
- user = found?.user ?? null;
46
- }
47
- if (!user) {
48
- if (!email) {
49
- throw new Error('actingAs: principal must carry an `email` (to create a user) or an ' + '`id` matching an existing user.');
50
- }
51
- const { id: _id, email: _email, name: _name, ...extra } = principal;
52
- user = await internalAdapter.createUser({
53
- ...extra,
54
- email,
55
- name: name ?? email,
56
- ...id ? {
57
- id
58
- } : {}
59
- });
60
- }
61
- const session = await internalAdapter.createSession(user.id, false, {
62
- ipAddress: '127.0.0.1',
63
- userAgent: 'vela-test'
64
- });
65
- const cookieName = ctx.authCookies.sessionToken.name;
66
- const signedToken = `${session.token}.${await makeSignature(session.token, ctx.secret)}`;
67
- const headers = new Headers();
68
- headers.set('Cookie', `${cookieName}=${signedToken}`);
69
- return headers;
70
- }