@dunx/auth 0.1.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 +201 -0
- package/README.md +251 -0
- package/dist/auth.d.ts +42 -0
- package/dist/context.d.ts +45 -0
- package/dist/drizzle.d.ts +41 -0
- package/dist/drizzle.js +92 -0
- package/dist/drizzle.js.map +10 -0
- package/dist/errors.d.ts +8 -0
- package/dist/guard.d.ts +33 -0
- package/dist/handler.d.ts +43 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +373 -0
- package/dist/index.js.map +18 -0
- package/dist/module.d.ts +61 -0
- package/dist/options.d.ts +32 -0
- package/dist/password.d.ts +24 -0
- package/dist/redis.d.ts +41 -0
- package/package.json +78 -0
package/dist/drizzle.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __name = (target, name) => {
|
|
6
|
+
Object.defineProperty(target, "name", {
|
|
7
|
+
value: name,
|
|
8
|
+
enumerable: false,
|
|
9
|
+
configurable: true
|
|
10
|
+
});
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
14
|
+
var __typeError = (msg) => {
|
|
15
|
+
throw TypeError(msg);
|
|
16
|
+
};
|
|
17
|
+
var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
18
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
19
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
20
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
21
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
22
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
23
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
24
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
25
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
26
|
+
var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
27
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
28
|
+
kind: __decoratorStrings[kind],
|
|
29
|
+
name,
|
|
30
|
+
metadata,
|
|
31
|
+
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
32
|
+
});
|
|
33
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
34
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
35
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
|
|
36
|
+
flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
40
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
41
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
42
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
43
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
44
|
+
get [name]() {
|
|
45
|
+
return __privateGet(this, extra);
|
|
46
|
+
},
|
|
47
|
+
set [name](x) {
|
|
48
|
+
__privateSet(this, extra, x);
|
|
49
|
+
}
|
|
50
|
+
}, name));
|
|
51
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
52
|
+
for (var i = decorators.length - 1;i >= 0; i--) {
|
|
53
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
54
|
+
if (k) {
|
|
55
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
|
|
56
|
+
if (k ^ 3)
|
|
57
|
+
access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
58
|
+
if (k > 2)
|
|
59
|
+
access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
60
|
+
}
|
|
61
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
|
|
62
|
+
done._ = 1;
|
|
63
|
+
if (k ^ 4 || it === undefined)
|
|
64
|
+
__expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
65
|
+
else if (typeof it !== "object" || it === null)
|
|
66
|
+
__typeError("Object expected");
|
|
67
|
+
else
|
|
68
|
+
__expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
69
|
+
}
|
|
70
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/drizzle.ts
|
|
74
|
+
import {
|
|
75
|
+
drizzleAdapter
|
|
76
|
+
} from "better-auth/adapters/drizzle";
|
|
77
|
+
var PROVIDERS = Object.freeze({
|
|
78
|
+
postgres: "pg",
|
|
79
|
+
mysql: "mysql",
|
|
80
|
+
mariadb: "mysql",
|
|
81
|
+
sqlite: "sqlite"
|
|
82
|
+
});
|
|
83
|
+
var drizzleDatabase = (connection, config = {}) => drizzleAdapter(connection.db, {
|
|
84
|
+
...config,
|
|
85
|
+
provider: PROVIDERS[connection.dialect]
|
|
86
|
+
});
|
|
87
|
+
export {
|
|
88
|
+
drizzleDatabase
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
//# debugId=FE2DA2CE89A5EE9A64756E2164756E21
|
|
92
|
+
//# sourceMappingURL=drizzle.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/drizzle.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n drizzleAdapter,\n type DB,\n type DrizzleAdapterConfig,\n} from 'better-auth/adapters/drizzle';\n\n/**\n * The two members of `@dunx/infra/db`'s `DbConnection` this reads.\n *\n * Restated structurally rather than imported, for the same reason `@dunx/http`\n * restates Standard Schema: it keeps `@dunx/auth`'s dependency list at `@dunx/core`\n * and `@dunx/http`, and it means a bare `drizzle({ client, schema })` handle works\n * here too. An `@dunx/infra/db` connection satisfies it with no adapter in between —\n * `dialect` is exactly that union and `db` is exactly `unknown`.\n */\nexport interface DrizzleSource {\n readonly dialect: 'postgres' | 'mysql' | 'mariadb' | 'sqlite';\n /** The drizzle handle — `BunSQLiteDatabase` or `BunSQLDatabase`. */\n readonly db: unknown;\n}\n\n/** What better-auth's drizzle adapter calls each of the dialects `@dunx/infra/db` reports. */\nconst PROVIDERS: Readonly<\n Record<DrizzleSource['dialect'], DrizzleAdapterConfig['provider']>\n> = Object.freeze({\n postgres: 'pg',\n mysql: 'mysql',\n mariadb: 'mysql',\n sqlite: 'sqlite',\n});\n\n/**\n * better-auth's `database` option over a connection the app already opened. Nothing\n * here connects: the point is that the app keeps **one** pool, one SQLite handle and\n * one shutdown path, instead of better-auth opening a second.\n *\n * ```ts\n * AuthModule.forRootAsync({\n * useFactory: (connection: DbConnection) => ({\n * database: drizzleDatabase(connection),\n * }),\n * inject: [DbConnection],\n * });\n * ```\n *\n * The `provider` comes from the connection's own dialect, so swapping `bun:sqlite`\n * for `Bun.SQL` needs no edit at the call site. The schema does not have to be passed\n * either — `@dunx/infra/db` builds its handle with `drizzle({ client, schema })` and\n * the adapter reads `db._.fullSchema`, so the better-auth tables being in the app's\n * schema object is the whole requirement.\n *\n * dunx ships **no** schema for those tables. They are better-auth's, they change with\n * its plugins, and its own CLI generates them: `bunx @better-auth/cli generate`. A\n * copy of them inside a framework is a copy that silently rots against the library\n * that reads it.\n */\nexport const drizzleDatabase = (\n connection: DrizzleSource,\n config: Omit<DrizzleAdapterConfig, 'provider'> = {},\n): ReturnType<typeof drizzleAdapter> =>\n // `db` is `unknown` on the contract because it cannot promise either backend's\n // handle. Narrowing it is what that contract documents.\n drizzleAdapter(connection.db as DB, {\n ...config,\n provider: PROVIDERS[connection.dialect],\n });\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAsBA,IAAM,YAEF,OAAO,OAAO;AAAA,EAChB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AACV,CAAC;AA2BM,IAAM,kBAAkB,CAC7B,YACA,SAAiD,CAAC,MAIlD,eAAe,WAAW,IAAU;AAAA,KAC/B;AAAA,EACH,UAAU,UAAU,WAAW;AACjC,CAAC;",
|
|
8
|
+
"debugId": "FE2DA2CE89A5EE9A64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AppError } from '@dunx/core';
|
|
2
|
+
/**
|
|
3
|
+
* Raised by `@dunx/auth`'s own wiring. better-auth's failures propagate as its
|
|
4
|
+
* `APIError`, and a rejected request is an `HttpError` from `@dunx/http`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AuthError extends AppError {
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
package/dist/guard.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Middleware, type Next, type RouteContext } from '@dunx/http';
|
|
2
|
+
import type { BunRequest } from 'bun';
|
|
3
|
+
import { Auth } from './auth.js';
|
|
4
|
+
import { AuthContext } from './context.js';
|
|
5
|
+
/**
|
|
6
|
+
* What roles a user holds. better-auth's `admin` plugin stores them in a single
|
|
7
|
+
* `role` column, comma-separated for more than one; a custom plugin may use an
|
|
8
|
+
* array. Both read the same here, and a user with none reads as `[]` rather than
|
|
9
|
+
* throwing — an app may well not use roles at all.
|
|
10
|
+
*/
|
|
11
|
+
export declare const rolesOf: (user: object) => readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Authenticates every request it sees through better-auth's own session lookup, and
|
|
14
|
+
* composes with the metadata `@dunx/http` already carries:
|
|
15
|
+
*
|
|
16
|
+
* - `@Public()` — skipped outright. No session lookup, no rejection, no role check.
|
|
17
|
+
* That is what makes it safe to install globally: better-auth's own endpoints are
|
|
18
|
+
* `@Public()`, and a sign-in route that needed a session could never be reached.
|
|
19
|
+
* A public route that wants to *adapt* to an optional caller injects `Auth` and
|
|
20
|
+
* calls `auth.api.getSession({ headers: req.headers })` itself — one line, and it
|
|
21
|
+
* does not put a lookup on every public request in the app.
|
|
22
|
+
* - `@Roles('admin')` — a 403 unless the caller holds one of them.
|
|
23
|
+
*
|
|
24
|
+
* Install it globally with `HttpFactory.create(root, { middleware: [SessionGuard] })`
|
|
25
|
+
* and opt routes out with `@Public()`, or scope it with `@UseGuards(SessionGuard)`
|
|
26
|
+
* and leave the rest of the app open. `AuthModule` registers it either way.
|
|
27
|
+
*/
|
|
28
|
+
export declare class SessionGuard implements Middleware {
|
|
29
|
+
private readonly auth;
|
|
30
|
+
private readonly context;
|
|
31
|
+
constructor(auth: Auth, context: AuthContext);
|
|
32
|
+
handle(req: BunRequest, ctx: RouteContext, next: Next): Promise<Response>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Ctor } from '@dunx/core';
|
|
2
|
+
import { type Input, type RouteSchemas } from '@dunx/http';
|
|
3
|
+
/**
|
|
4
|
+
* better-auth's handler is a plain `(request: Request) => Promise<Response>`, so
|
|
5
|
+
* mounting it is five one-line routes and nothing else. Every endpoint the library
|
|
6
|
+
* and its plugins declare lives under one wildcard — dunx does not restate, wrap or
|
|
7
|
+
* re-dispatch a single one of them.
|
|
8
|
+
*
|
|
9
|
+
* `Bun.serve` matches `<basePath>/*` natively (verified on Bun 1.3.14), so Bun is
|
|
10
|
+
* still the router. All five verbs are mounted because a plugin may declare any of
|
|
11
|
+
* them; better-auth's own endpoints are `GET` and `POST`.
|
|
12
|
+
*
|
|
13
|
+
* The `Response` is returned untouched — `buildRoutes` passes one straight through,
|
|
14
|
+
* which is what keeps better-auth's `Set-Cookie` headers and redirects intact.
|
|
15
|
+
*
|
|
16
|
+
* `@Public()` at class scope, so all five inherit it — `mergeMeta` reads the class's
|
|
17
|
+
* record under the handler's. Without it a globally installed `SessionGuard` would
|
|
18
|
+
* demand a session from the sign-in endpoint, and no session could ever be created.
|
|
19
|
+
*
|
|
20
|
+
* `inject(Auth)` in a field rather than a constructor parameter, because a bare
|
|
21
|
+
* class in `controllers` is bound as a class provider and would then need
|
|
22
|
+
* `@dunx/transform`'s transform to have run. This way mounting works in an app that
|
|
23
|
+
* never added the preload.
|
|
24
|
+
*/
|
|
25
|
+
export declare class AuthHandler {
|
|
26
|
+
#private;
|
|
27
|
+
get({ req }: Input<RouteSchemas>): Promise<Response>;
|
|
28
|
+
post({ req }: Input<RouteSchemas>): Promise<Response>;
|
|
29
|
+
put({ req }: Input<RouteSchemas>): Promise<Response>;
|
|
30
|
+
patch({ req }: Input<RouteSchemas>): Promise<Response>;
|
|
31
|
+
delete({ req }: Input<RouteSchemas>): Promise<Response>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The controller `AuthModule` registers, prefixed with `AuthOptions.mountAt`.
|
|
35
|
+
*
|
|
36
|
+
* A subclass rather than `@Controller(...)` on {@link AuthHandler} itself: the prefix
|
|
37
|
+
* is only known once the module is configured, and mutating the shared class from a
|
|
38
|
+
* factory would make two configurations fight over one prefix. The subclass declares
|
|
39
|
+
* nothing of its own and inherits everything — `discoverRoutes` walks the prototype
|
|
40
|
+
* chain for the routes, and `metaOf` and `prefixOf` are plain lookups, so `@Public()`
|
|
41
|
+
* comes down from the base while the prefix stays own to the subclass.
|
|
42
|
+
*/
|
|
43
|
+
export declare const mountHandler: (mountAt: string) => Ctor<AuthHandler>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Auth, type Principal } from './auth.js';
|
|
2
|
+
export { AuthContext } from './context.js';
|
|
3
|
+
export { AuthError } from './errors.js';
|
|
4
|
+
export { rolesOf, SessionGuard } from './guard.js';
|
|
5
|
+
export { AuthHandler, mountHandler } from './handler.js';
|
|
6
|
+
export { AuthModule } from './module.js';
|
|
7
|
+
export { AuthOptions, DEFAULT_BASE_PATH, normalizeBasePath, } from './options.js';
|
|
8
|
+
export { bunPassword } from './password.js';
|
|
9
|
+
export { redisStorage, type RedisStore } from './redis.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __name = (target, name) => {
|
|
6
|
+
Object.defineProperty(target, "name", {
|
|
7
|
+
value: name,
|
|
8
|
+
enumerable: false,
|
|
9
|
+
configurable: true
|
|
10
|
+
});
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
14
|
+
var __typeError = (msg) => {
|
|
15
|
+
throw TypeError(msg);
|
|
16
|
+
};
|
|
17
|
+
var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
18
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
19
|
+
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
20
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
21
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
22
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
23
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
24
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
25
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
26
|
+
var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
27
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
28
|
+
kind: __decoratorStrings[kind],
|
|
29
|
+
name,
|
|
30
|
+
metadata,
|
|
31
|
+
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
32
|
+
});
|
|
33
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
34
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
35
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
|
|
36
|
+
flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
40
|
+
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
41
|
+
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
42
|
+
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
43
|
+
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
44
|
+
get [name]() {
|
|
45
|
+
return __privateGet(this, extra);
|
|
46
|
+
},
|
|
47
|
+
set [name](x) {
|
|
48
|
+
__privateSet(this, extra, x);
|
|
49
|
+
}
|
|
50
|
+
}, name));
|
|
51
|
+
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
52
|
+
for (var i = decorators.length - 1;i >= 0; i--) {
|
|
53
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
54
|
+
if (k) {
|
|
55
|
+
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
|
|
56
|
+
if (k ^ 3)
|
|
57
|
+
access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
58
|
+
if (k > 2)
|
|
59
|
+
access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
60
|
+
}
|
|
61
|
+
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
|
|
62
|
+
done._ = 1;
|
|
63
|
+
if (k ^ 4 || it === undefined)
|
|
64
|
+
__expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
65
|
+
else if (typeof it !== "object" || it === null)
|
|
66
|
+
__typeError("Object expected");
|
|
67
|
+
else
|
|
68
|
+
__expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
69
|
+
}
|
|
70
|
+
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/errors.ts
|
|
74
|
+
import { AppError } from "@dunx/core";
|
|
75
|
+
|
|
76
|
+
class AuthError extends AppError {
|
|
77
|
+
name = "AuthError";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/auth.ts
|
|
81
|
+
class Auth {
|
|
82
|
+
constructor() {
|
|
83
|
+
if (new.target === Auth) {
|
|
84
|
+
throw new AuthError("Auth is a contract, not an implementation. Bind one with " + "AuthModule.forRoot({ ... }) or AuthModule.forRootAsync({ useFactory }).");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// src/context.ts
|
|
89
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
90
|
+
import { RequestContext } from "@dunx/core";
|
|
91
|
+
import { HttpError, HttpStatusCode } from "@dunx/http";
|
|
92
|
+
|
|
93
|
+
class AuthContext {
|
|
94
|
+
context;
|
|
95
|
+
#storage = new AsyncLocalStorage;
|
|
96
|
+
constructor(context) {
|
|
97
|
+
this.context = context;
|
|
98
|
+
}
|
|
99
|
+
current() {
|
|
100
|
+
return this.#storage.getStore();
|
|
101
|
+
}
|
|
102
|
+
require() {
|
|
103
|
+
const principal = this.current();
|
|
104
|
+
if (!principal) {
|
|
105
|
+
throw new HttpError(HttpStatusCode.UNAUTHORIZED, "UNAUTHENTICATED");
|
|
106
|
+
}
|
|
107
|
+
return principal;
|
|
108
|
+
}
|
|
109
|
+
run(principal, callback) {
|
|
110
|
+
this.context.updateContext({ userId: principal.user.id });
|
|
111
|
+
return this.#storage.run(principal, callback);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
Object.defineProperty(AuthContext, Symbol.for("dunx.deps"), {
|
|
115
|
+
value: () => [RequestContext]
|
|
116
|
+
});
|
|
117
|
+
// src/guard.ts
|
|
118
|
+
import {
|
|
119
|
+
HttpError as HttpError2,
|
|
120
|
+
HttpStatusCode as HttpStatusCode2,
|
|
121
|
+
PUBLIC,
|
|
122
|
+
ROLES
|
|
123
|
+
} from "@dunx/http";
|
|
124
|
+
var rolesOf = (user) => {
|
|
125
|
+
const role = user.role;
|
|
126
|
+
if (typeof role === "string") {
|
|
127
|
+
return role.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(role)) {
|
|
130
|
+
return role.filter((entry) => typeof entry === "string");
|
|
131
|
+
}
|
|
132
|
+
return [];
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
class SessionGuard {
|
|
136
|
+
auth;
|
|
137
|
+
context;
|
|
138
|
+
constructor(auth, context) {
|
|
139
|
+
this.auth = auth;
|
|
140
|
+
this.context = context;
|
|
141
|
+
}
|
|
142
|
+
async handle(req, ctx, next) {
|
|
143
|
+
if (ctx.get(PUBLIC))
|
|
144
|
+
return next();
|
|
145
|
+
const principal = await this.auth.api.getSession({
|
|
146
|
+
headers: req.headers
|
|
147
|
+
});
|
|
148
|
+
if (!principal) {
|
|
149
|
+
throw new HttpError2(HttpStatusCode2.UNAUTHORIZED, "UNAUTHENTICATED");
|
|
150
|
+
}
|
|
151
|
+
const required = ctx.get(ROLES);
|
|
152
|
+
if (required !== undefined && required.length > 0) {
|
|
153
|
+
const held = rolesOf(principal.user);
|
|
154
|
+
if (!required.some((role) => held.includes(role))) {
|
|
155
|
+
throw new HttpError2(HttpStatusCode2.FORBIDDEN, `Requires one of: ${required.join(", ")}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return this.context.run(principal, next);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
Object.defineProperty(SessionGuard, Symbol.for("dunx.deps"), {
|
|
162
|
+
value: () => [Auth, AuthContext]
|
|
163
|
+
});
|
|
164
|
+
// src/handler.ts
|
|
165
|
+
import { inject } from "@dunx/core";
|
|
166
|
+
import {
|
|
167
|
+
Controller,
|
|
168
|
+
Delete,
|
|
169
|
+
Get,
|
|
170
|
+
Patch,
|
|
171
|
+
Post,
|
|
172
|
+
Public,
|
|
173
|
+
Put
|
|
174
|
+
} from "@dunx/http";
|
|
175
|
+
|
|
176
|
+
// src/password.ts
|
|
177
|
+
var bunPassword = {
|
|
178
|
+
hash: (password) => Bun.password.hash(password, { algorithm: "bcrypt", cost: 10 }),
|
|
179
|
+
verify: async ({
|
|
180
|
+
hash,
|
|
181
|
+
password
|
|
182
|
+
}) => {
|
|
183
|
+
try {
|
|
184
|
+
return await Bun.password.verify(password, hash);
|
|
185
|
+
} catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/options.ts
|
|
192
|
+
var DEFAULT_BASE_PATH = "/api/auth";
|
|
193
|
+
var normalizeBasePath = (basePath) => {
|
|
194
|
+
const normalized = `/${basePath}`.replace(/\/{2,}/g, "/").replace(/\/$/, "");
|
|
195
|
+
if (normalized.length < 2) {
|
|
196
|
+
throw new AuthError(`"${basePath}" is not a usable basePath. The handler mounts at ` + "<basePath>/*, so at the root it would claim every route in the app. " + `Use something like ${DEFAULT_BASE_PATH}.`);
|
|
197
|
+
}
|
|
198
|
+
return normalized;
|
|
199
|
+
};
|
|
200
|
+
var withBunPassword = (options) => {
|
|
201
|
+
const email = options.emailAndPassword;
|
|
202
|
+
if (!email?.enabled || email.password)
|
|
203
|
+
return options;
|
|
204
|
+
return {
|
|
205
|
+
...options,
|
|
206
|
+
emailAndPassword: { ...email, password: bunPassword }
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
class AuthOptions {
|
|
211
|
+
options;
|
|
212
|
+
basePath;
|
|
213
|
+
mountAt;
|
|
214
|
+
constructor(init, mountAt) {
|
|
215
|
+
this.basePath = normalizeBasePath(init.basePath ?? DEFAULT_BASE_PATH);
|
|
216
|
+
this.mountAt = mountAt === undefined ? this.basePath : normalizeBasePath(mountAt);
|
|
217
|
+
this.options = withBunPassword({ ...init, basePath: this.basePath });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
Object.defineProperty(AuthOptions, Symbol.for("dunx.deps"), {
|
|
221
|
+
value: () => [{ unresolved: "init: O" }, { unresolved: "mountAt?: string" }]
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// src/handler.ts
|
|
225
|
+
var _dec = [
|
|
226
|
+
Public()
|
|
227
|
+
];
|
|
228
|
+
var _dec2 = [
|
|
229
|
+
Get("/*")
|
|
230
|
+
];
|
|
231
|
+
var _dec3 = [
|
|
232
|
+
Post("/*")
|
|
233
|
+
];
|
|
234
|
+
var _dec4 = [
|
|
235
|
+
Put("/*")
|
|
236
|
+
];
|
|
237
|
+
var _dec5 = [
|
|
238
|
+
Patch("/*")
|
|
239
|
+
];
|
|
240
|
+
var _dec6 = [
|
|
241
|
+
Delete("/*")
|
|
242
|
+
];
|
|
243
|
+
var _auth = new WeakMap;
|
|
244
|
+
var _verified = new WeakMap;
|
|
245
|
+
var _dispatch = new WeakSet, _dispatch_fn;
|
|
246
|
+
_dispatch_fn = function(req) {
|
|
247
|
+
if (!__privateGet(this, _verified)) {
|
|
248
|
+
__privateSet(this, _verified, true);
|
|
249
|
+
const basePath = __privateGet(this, _auth).options.basePath ?? DEFAULT_BASE_PATH;
|
|
250
|
+
const { pathname } = new URL(req.url);
|
|
251
|
+
if (!pathname.startsWith(`${basePath}/`)) {
|
|
252
|
+
throw new AuthError(`${pathname} reached the auth handler, but better-auth is configured with ` + `basePath ${basePath} and would answer 404 to everything under it. A ` + "global prefix is the usual cause: mount the handler at the path without " + "the prefix and give better-auth the full one \u2014 see AuthOptions.mountAt.");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return __privateGet(this, _auth).handler(req);
|
|
256
|
+
};
|
|
257
|
+
var _init = __decoratorStart(undefined);
|
|
258
|
+
|
|
259
|
+
class AuthHandler {
|
|
260
|
+
constructor() {
|
|
261
|
+
__privateAdd(this, _auth, inject(Auth));
|
|
262
|
+
__privateAdd(this, _verified, false);
|
|
263
|
+
__privateAdd(this, _dispatch);
|
|
264
|
+
__runInitializers(_init, 5, this);
|
|
265
|
+
}
|
|
266
|
+
get({ req }) {
|
|
267
|
+
return __privateMethod(this, _dispatch, _dispatch_fn).call(this, req);
|
|
268
|
+
}
|
|
269
|
+
post({ req }) {
|
|
270
|
+
return __privateMethod(this, _dispatch, _dispatch_fn).call(this, req);
|
|
271
|
+
}
|
|
272
|
+
put({ req }) {
|
|
273
|
+
return __privateMethod(this, _dispatch, _dispatch_fn).call(this, req);
|
|
274
|
+
}
|
|
275
|
+
patch({ req }) {
|
|
276
|
+
return __privateMethod(this, _dispatch, _dispatch_fn).call(this, req);
|
|
277
|
+
}
|
|
278
|
+
delete({ req }) {
|
|
279
|
+
return __privateMethod(this, _dispatch, _dispatch_fn).call(this, req);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
__decorateElement(_init, 1, "get", _dec2, AuthHandler);
|
|
283
|
+
__decorateElement(_init, 1, "post", _dec3, AuthHandler);
|
|
284
|
+
__decorateElement(_init, 1, "put", _dec4, AuthHandler);
|
|
285
|
+
__decorateElement(_init, 1, "patch", _dec5, AuthHandler);
|
|
286
|
+
__decorateElement(_init, 1, "delete", _dec6, AuthHandler);
|
|
287
|
+
AuthHandler = __decorateElement(_init, 0, "AuthHandler", _dec, AuthHandler);
|
|
288
|
+
__runInitializers(_init, 1, AuthHandler);
|
|
289
|
+
__decoratorMetadata(_init, AuthHandler);
|
|
290
|
+
let _AuthHandler = AuthHandler;
|
|
291
|
+
var mountHandler = (mountAt) => Controller(mountAt)(class MountedAuthHandler extends AuthHandler {
|
|
292
|
+
});
|
|
293
|
+
// src/module.ts
|
|
294
|
+
import {
|
|
295
|
+
provide,
|
|
296
|
+
RequestContext as RequestContext2
|
|
297
|
+
} from "@dunx/core";
|
|
298
|
+
import { betterAuth } from "better-auth";
|
|
299
|
+
var build = (options, mountAt) => {
|
|
300
|
+
const auth = Auth;
|
|
301
|
+
return {
|
|
302
|
+
module: AuthModule,
|
|
303
|
+
controllers: [mountHandler(mountAt)],
|
|
304
|
+
providers: [
|
|
305
|
+
options,
|
|
306
|
+
provide(auth, {
|
|
307
|
+
useFactory: (resolved) => betterAuth(resolved.options),
|
|
308
|
+
inject: [AuthOptions]
|
|
309
|
+
}),
|
|
310
|
+
provide(AuthContext, {
|
|
311
|
+
useFactory: (context) => new AuthContext(context),
|
|
312
|
+
inject: [RequestContext2]
|
|
313
|
+
}),
|
|
314
|
+
provide(SessionGuard, {
|
|
315
|
+
useFactory: (instance, context) => new SessionGuard(instance, context),
|
|
316
|
+
inject: [auth, AuthContext]
|
|
317
|
+
})
|
|
318
|
+
]
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
class AuthModule {
|
|
323
|
+
static forRoot(options, mountAt) {
|
|
324
|
+
const resolved = new AuthOptions(options, mountAt);
|
|
325
|
+
return build(provide(AuthOptions, { useValue: resolved }), resolved.mountAt);
|
|
326
|
+
}
|
|
327
|
+
static forRootAsync(provider, mountAt) {
|
|
328
|
+
const mounted = normalizeBasePath(mountAt ?? DEFAULT_BASE_PATH);
|
|
329
|
+
return build(provide(AuthOptions, {
|
|
330
|
+
useFactory: async (...deps) => {
|
|
331
|
+
const resolved = new AuthOptions(await provider.useFactory(...deps), mounted);
|
|
332
|
+
if (mountAt === undefined && resolved.basePath !== mounted) {
|
|
333
|
+
throw new AuthError(`The factory returned basePath ${resolved.basePath}, but the handler ` + `is mounted at ${mounted} \u2014 the table was built before the factory ` + "ran, so it could not follow. Pass the route path as " + "forRootAsync's second argument.");
|
|
334
|
+
}
|
|
335
|
+
return resolved;
|
|
336
|
+
},
|
|
337
|
+
inject: provider.inject ?? []
|
|
338
|
+
}), mounted);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// src/redis.ts
|
|
342
|
+
var redisStorage = (connection) => ({
|
|
343
|
+
get: (key) => connection.get(key),
|
|
344
|
+
getAndDelete: (key) => connection.getdel(key),
|
|
345
|
+
increment: async (key, ttl) => {
|
|
346
|
+
const value = await connection.incr(key);
|
|
347
|
+
if (value === 1)
|
|
348
|
+
await connection.expire(key, ttl);
|
|
349
|
+
return value;
|
|
350
|
+
},
|
|
351
|
+
set: (key, value, ttl) => connection.set(key, value, ttl === undefined ? {} : { ex: ttl }),
|
|
352
|
+
delete: async (key) => {
|
|
353
|
+
await connection.del(key);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
export {
|
|
357
|
+
rolesOf,
|
|
358
|
+
redisStorage,
|
|
359
|
+
normalizeBasePath,
|
|
360
|
+
mountHandler,
|
|
361
|
+
bunPassword,
|
|
362
|
+
SessionGuard,
|
|
363
|
+
DEFAULT_BASE_PATH,
|
|
364
|
+
AuthOptions,
|
|
365
|
+
AuthModule,
|
|
366
|
+
AuthHandler,
|
|
367
|
+
AuthError,
|
|
368
|
+
AuthContext,
|
|
369
|
+
Auth
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
//# debugId=0D5B2BADC3F2673664756E2164756E21
|
|
373
|
+
//# sourceMappingURL=index.js.map
|