@beignet/provider-auth-better-auth 0.0.41 → 0.0.43
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 +8 -0
- package/README.md +43 -10
- package/dist/index.d.ts +51 -66
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -174
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/skills/auth-provider/SKILL.md +32 -4
- package/src/index.ts +214 -189
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ schema, and auth routes.
|
|
|
17
17
|
|
|
18
18
|
- Wraps a Better Auth instance with a simple, stable API
|
|
19
19
|
- Extends `ports.auth` with `getSession`, `getUser`, and `requireUser` methods
|
|
20
|
+
- Exposes a direct port adapter for custom app wiring
|
|
20
21
|
- Maintains type safety for your custom User type
|
|
21
22
|
- Records auth checks in devtools when devtools is installed
|
|
22
23
|
|
|
@@ -117,7 +118,7 @@ import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
|
117
118
|
import { auth } from "@/lib/better-auth";
|
|
118
119
|
|
|
119
120
|
export const providers = [
|
|
120
|
-
createBetterAuthProvider(auth),
|
|
121
|
+
createBetterAuthProvider({ auth }),
|
|
121
122
|
// ...other providers
|
|
122
123
|
];
|
|
123
124
|
```
|
|
@@ -386,21 +387,53 @@ interface AuthSession<User = unknown, Session = unknown> {
|
|
|
386
387
|
}
|
|
387
388
|
```
|
|
388
389
|
|
|
389
|
-
### `createBetterAuthProvider(auth)`
|
|
390
|
+
### `createBetterAuthProvider({ auth })`
|
|
390
391
|
|
|
391
392
|
Factory function that creates the provider:
|
|
392
393
|
|
|
393
394
|
```ts
|
|
394
395
|
function createBetterAuthProvider<User = unknown, Session = unknown>(
|
|
395
|
-
|
|
396
|
-
):
|
|
396
|
+
options: CreateBetterAuthProviderOptions<User, Session>
|
|
397
|
+
): BetterAuthProvider<User, Session>
|
|
397
398
|
```
|
|
398
399
|
|
|
399
400
|
**Parameters:**
|
|
400
|
-
- `auth`: A Better Auth server instance configured in your application
|
|
401
|
+
- `options.auth`: A Better Auth server instance configured in your application
|
|
401
402
|
|
|
402
403
|
**Returns:** A Beignet provider that can be registered with the server
|
|
403
404
|
|
|
405
|
+
### `createBetterAuthPort({ auth, instrumentation? })`
|
|
406
|
+
|
|
407
|
+
Create the shared `AuthPort` directly when an app needs custom provider setup
|
|
408
|
+
around its Better Auth instance:
|
|
409
|
+
|
|
410
|
+
```ts
|
|
411
|
+
// server/providers.ts
|
|
412
|
+
import { createProvider } from "@beignet/core/providers";
|
|
413
|
+
import { createBetterAuthPort } from "@beignet/provider-auth-better-auth";
|
|
414
|
+
import { auth } from "@/lib/better-auth";
|
|
415
|
+
|
|
416
|
+
export const appAuthProvider = createProvider({
|
|
417
|
+
name: "app-auth",
|
|
418
|
+
setup({ ports }) {
|
|
419
|
+
return {
|
|
420
|
+
ports: {
|
|
421
|
+
auth: createBetterAuthPort({ auth, instrumentation: ports }),
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
export const providers = [appAuthProvider] as const;
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Define and register `appAuthProvider` in `server/providers.ts`, where provider
|
|
431
|
+
audit can recognize the lower-level adapter as valid registration, and keep
|
|
432
|
+
`auth` deferred in `infra/port-wiring.ts`. Outside lifecycle provider wiring,
|
|
433
|
+
pass a Beignet provider instrumentation target explicitly when the adapter
|
|
434
|
+
should emit the same redacted auth events. The adapter does not own or stop the
|
|
435
|
+
Better Auth instance.
|
|
436
|
+
|
|
404
437
|
## Advanced usage
|
|
405
438
|
|
|
406
439
|
### Route-scoped authentication
|
|
@@ -462,8 +495,8 @@ export const requireAuth = async (
|
|
|
462
495
|
Configure multiple strategies through Better Auth when its plugins cover the
|
|
463
496
|
required session, bearer, or API-key behavior. When an app needs authentication
|
|
464
497
|
outside Better Auth, compose that logic in an app-owned `AuthPort` adapter or
|
|
465
|
-
HTTP auth hook
|
|
466
|
-
|
|
498
|
+
HTTP auth hook. Use `createBetterAuthPort(...)` when the app needs direct
|
|
499
|
+
wiring for its existing Better Auth instance.
|
|
467
500
|
|
|
468
501
|
## Type safety
|
|
469
502
|
|
|
@@ -476,7 +509,7 @@ type MyUser = {
|
|
|
476
509
|
role: "admin" | "user";
|
|
477
510
|
};
|
|
478
511
|
|
|
479
|
-
const authProvider = createBetterAuthProvider<MyUser>(auth);
|
|
512
|
+
const authProvider = createBetterAuthProvider<MyUser>({ auth });
|
|
480
513
|
|
|
481
514
|
// Later, in your routes:
|
|
482
515
|
const user = await ctx.ports.auth.requireUser(req);
|
|
@@ -544,7 +577,7 @@ const auth = betterAuth({ database: db });
|
|
|
544
577
|
|
|
545
578
|
const server = await createNextServer({
|
|
546
579
|
ports: initialPorts,
|
|
547
|
-
providers: [createBetterAuthProvider(auth)],
|
|
580
|
+
providers: [createBetterAuthProvider({ auth })],
|
|
548
581
|
context: appContext,
|
|
549
582
|
routes,
|
|
550
583
|
});
|
|
@@ -568,7 +601,7 @@ const authHook = {
|
|
|
568
601
|
|
|
569
602
|
const server = await createNextServer({
|
|
570
603
|
ports: initialPorts,
|
|
571
|
-
providers: [createBetterAuthProvider(auth)],
|
|
604
|
+
providers: [createBetterAuthProvider({ auth })],
|
|
572
605
|
hooks: [authHook],
|
|
573
606
|
context: appContext,
|
|
574
607
|
routes,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beignet/provider-auth-better-auth
|
|
3
3
|
*
|
|
4
|
-
* Better Auth
|
|
5
|
-
* This provider wraps an already-configured Better Auth server instance
|
|
6
|
-
* and exposes the shared Beignet AuthPort on ctx.ports.auth.
|
|
4
|
+
* Better Auth adapters for Beignet's shared authentication port.
|
|
7
5
|
*
|
|
8
6
|
* The provider does NOT own:
|
|
9
7
|
* - Database schema
|
|
@@ -15,60 +13,20 @@
|
|
|
15
13
|
*
|
|
16
14
|
* @example
|
|
17
15
|
* ```ts
|
|
18
|
-
* //
|
|
16
|
+
* // Configure Better Auth in your app.
|
|
19
17
|
* import { betterAuth } from "better-auth";
|
|
18
|
+
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
20
19
|
* import { db } from "./db";
|
|
21
20
|
*
|
|
22
21
|
* export const auth = betterAuth({
|
|
23
22
|
* database: db,
|
|
24
|
-
* // ...other Better Auth config
|
|
25
|
-
* });
|
|
26
|
-
*
|
|
27
|
-
* // Own this shape in your app instead of exporting provider-inferred types.
|
|
28
|
-
* export type AuthUser = {
|
|
29
|
-
* id: string;
|
|
30
|
-
* name?: string | null;
|
|
31
|
-
* email?: string | null;
|
|
32
|
-
* image?: string | null;
|
|
33
|
-
* };
|
|
34
|
-
*
|
|
35
|
-
* // 2. Define your ports type
|
|
36
|
-
* import type { AuthPort } from "@beignet/core/ports";
|
|
37
|
-
*
|
|
38
|
-
* export type AppPorts = {
|
|
39
|
-
* auth: AuthPort<AuthUser>;
|
|
40
|
-
* // ...other ports
|
|
41
|
-
* };
|
|
42
|
-
*
|
|
43
|
-
* // 3. Wire the provider into Beignet
|
|
44
|
-
* import { createNextServer } from "@beignet/next";
|
|
45
|
-
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
46
|
-
*
|
|
47
|
-
* const server = await createNextServer({
|
|
48
|
-
* ports: basePorts,
|
|
49
|
-
* providers: [
|
|
50
|
-
* createBetterAuthProvider(auth),
|
|
51
|
-
* // ...other providers
|
|
52
|
-
* ],
|
|
53
|
-
* });
|
|
54
|
-
*
|
|
55
|
-
* // 4. Use the auth port through route-scoped auth hooks
|
|
56
|
-
* import { createAuthHooks } from "@beignet/core/server";
|
|
57
|
-
*
|
|
58
|
-
* const auth = createAuthHooks<AppContext>()({
|
|
59
|
-
* resolve: ({ ctx }) => {
|
|
60
|
-
* return ctx.auth ? { user: ctx.auth.user } : null;
|
|
61
|
-
* },
|
|
62
23
|
* });
|
|
63
24
|
*
|
|
64
|
-
* const
|
|
65
|
-
* ports: basePorts,
|
|
66
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
67
|
-
* context: async ({ ports }) => ({ ports }),
|
|
68
|
-
* });
|
|
25
|
+
* export const authProvider = createBetterAuthProvider({ auth });
|
|
69
26
|
* ```
|
|
70
27
|
*/
|
|
71
|
-
import { type AuthPort
|
|
28
|
+
import { type AuthPort } from "@beignet/core/ports";
|
|
29
|
+
import { type AnyProviderConfigSchema, type ProviderInstrumentationTarget, type ServiceProvider } from "@beignet/core/providers";
|
|
72
30
|
/**
|
|
73
31
|
* Minimal type representing a Better Auth server instance.
|
|
74
32
|
* This is the interface we expect from the Better Auth library.
|
|
@@ -96,32 +54,59 @@ export type BetterAuthServer<User = unknown, Session = unknown> = {
|
|
|
96
54
|
};
|
|
97
55
|
};
|
|
98
56
|
/**
|
|
99
|
-
*
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
57
|
+
* Options for adapting an existing Better Auth server to Beignet's auth port.
|
|
58
|
+
*/
|
|
59
|
+
export interface CreateBetterAuthPortOptions<User = unknown, Session = unknown> {
|
|
60
|
+
/**
|
|
61
|
+
* Better Auth server instance configured and owned by the application.
|
|
62
|
+
*/
|
|
63
|
+
auth: BetterAuthServer<User, Session>;
|
|
64
|
+
/**
|
|
65
|
+
* Optional provider instrumentation target.
|
|
66
|
+
*/
|
|
67
|
+
instrumentation?: ProviderInstrumentationTarget;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Options for installing an existing Better Auth server as a Beignet provider.
|
|
71
|
+
*/
|
|
72
|
+
export interface CreateBetterAuthProviderOptions<User = unknown, Session = unknown> {
|
|
73
|
+
/**
|
|
74
|
+
* Better Auth server instance configured and owned by the application.
|
|
75
|
+
*/
|
|
76
|
+
auth: BetterAuthServer<User, Session>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Ports contributed by `createBetterAuthProvider`.
|
|
80
|
+
*/
|
|
81
|
+
export interface BetterAuthProviderPorts<User = unknown, Session = unknown> {
|
|
82
|
+
/**
|
|
83
|
+
* Beignet auth port backed by Better Auth.
|
|
84
|
+
*/
|
|
85
|
+
auth: AuthPort<User, Session>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Better Auth lifecycle provider with stable contributed-port typing.
|
|
89
|
+
*/
|
|
90
|
+
export type BetterAuthProvider<User = unknown, Session = unknown> = ServiceProvider<unknown, AnyProviderConfigSchema<void>, Pick<BetterAuthProviderPorts<User, Session>, keyof BetterAuthProviderPorts<User, Session>>>;
|
|
91
|
+
/**
|
|
92
|
+
* Create a Beignet auth port backed by an existing Better Auth server.
|
|
106
93
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
94
|
+
* Use this for custom app wiring. The adapter does not own the Better Auth
|
|
95
|
+
* instance or add lifecycle hooks.
|
|
96
|
+
*/
|
|
97
|
+
export declare function createBetterAuthPort<User = unknown, Session = unknown>({ auth, instrumentation: instrumentationTarget, }: CreateBetterAuthPortOptions<User, Session>): AuthPort<User, Session>;
|
|
98
|
+
/**
|
|
99
|
+
* Create a Beignet provider for an existing Better Auth server.
|
|
109
100
|
*
|
|
110
101
|
* @example
|
|
111
102
|
* ```ts
|
|
112
103
|
* import { betterAuth } from "better-auth";
|
|
113
104
|
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
114
|
-
* import {
|
|
105
|
+
* import { db } from "./db";
|
|
115
106
|
*
|
|
116
107
|
* const auth = betterAuth({ database: db });
|
|
117
|
-
*
|
|
118
|
-
* const server = await createNextServer({
|
|
119
|
-
* ports: basePorts,
|
|
120
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
121
|
-
* });
|
|
108
|
+
* const provider = createBetterAuthProvider({ auth });
|
|
122
109
|
* ```
|
|
123
110
|
*/
|
|
124
|
-
export declare function createBetterAuthProvider<User = unknown, Session = unknown>(auth:
|
|
125
|
-
auth: AuthPort<User, Session, AuthRequestLike>;
|
|
126
|
-
}, unknown, void>;
|
|
111
|
+
export declare function createBetterAuthProvider<User = unknown, Session = unknown>({ auth, }: CreateBetterAuthProviderOptions<User, Session>): BetterAuthProvider<User, Session>;
|
|
127
112
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,KAAK,QAAQ,EAGd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,uBAAuB,EAG5B,KAAK,6BAA6B,EAClC,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI;IAChE;;;OAGG;IACH,GAAG,EAAE;QACH;;;;WAIG;QACH,UAAU,CAAC,OAAO,EAAE;YAClB,OAAO,EAAE,OAAO,CAAC;SAClB,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,IAAI,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;KACtD,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,2BAA2B,CAC1C,IAAI,GAAG,OAAO,EACd,OAAO,GAAG,OAAO;IAEjB;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC;;OAEG;IACH,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B,CAC9C,IAAI,GAAG,OAAO,EACd,OAAO,GAAG,OAAO;IAEjB;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IACxE;;OAEG;IACH,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAC5B,IAAI,GAAG,OAAO,EACd,OAAO,GAAG,OAAO,IACf,eAAe,CACjB,OAAO,EACP,uBAAuB,CAAC,IAAI,CAAC,EAC7B,IAAI,CACF,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,EACtC,MAAM,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAC7C,CACF,CAAC;AAMF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,EACtE,IAAI,EACJ,eAAe,EAAE,qBAAqB,GACvC,EAAE,2BAA2B,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CA6HtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1E,IAAI,GACL,EAAE,+BAA+B,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,kBAAkB,CACpE,IAAI,EACJ,OAAO,CACR,CAiBA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beignet/provider-auth-better-auth
|
|
3
3
|
*
|
|
4
|
-
* Better Auth
|
|
5
|
-
* This provider wraps an already-configured Better Auth server instance
|
|
6
|
-
* and exposes the shared Beignet AuthPort on ctx.ports.auth.
|
|
4
|
+
* Better Auth adapters for Beignet's shared authentication port.
|
|
7
5
|
*
|
|
8
6
|
* The provider does NOT own:
|
|
9
7
|
* - Database schema
|
|
@@ -15,57 +13,16 @@
|
|
|
15
13
|
*
|
|
16
14
|
* @example
|
|
17
15
|
* ```ts
|
|
18
|
-
* //
|
|
16
|
+
* // Configure Better Auth in your app.
|
|
19
17
|
* import { betterAuth } from "better-auth";
|
|
18
|
+
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
20
19
|
* import { db } from "./db";
|
|
21
20
|
*
|
|
22
21
|
* export const auth = betterAuth({
|
|
23
22
|
* database: db,
|
|
24
|
-
* // ...other Better Auth config
|
|
25
|
-
* });
|
|
26
|
-
*
|
|
27
|
-
* // Own this shape in your app instead of exporting provider-inferred types.
|
|
28
|
-
* export type AuthUser = {
|
|
29
|
-
* id: string;
|
|
30
|
-
* name?: string | null;
|
|
31
|
-
* email?: string | null;
|
|
32
|
-
* image?: string | null;
|
|
33
|
-
* };
|
|
34
|
-
*
|
|
35
|
-
* // 2. Define your ports type
|
|
36
|
-
* import type { AuthPort } from "@beignet/core/ports";
|
|
37
|
-
*
|
|
38
|
-
* export type AppPorts = {
|
|
39
|
-
* auth: AuthPort<AuthUser>;
|
|
40
|
-
* // ...other ports
|
|
41
|
-
* };
|
|
42
|
-
*
|
|
43
|
-
* // 3. Wire the provider into Beignet
|
|
44
|
-
* import { createNextServer } from "@beignet/next";
|
|
45
|
-
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
46
|
-
*
|
|
47
|
-
* const server = await createNextServer({
|
|
48
|
-
* ports: basePorts,
|
|
49
|
-
* providers: [
|
|
50
|
-
* createBetterAuthProvider(auth),
|
|
51
|
-
* // ...other providers
|
|
52
|
-
* ],
|
|
53
|
-
* });
|
|
54
|
-
*
|
|
55
|
-
* // 4. Use the auth port through route-scoped auth hooks
|
|
56
|
-
* import { createAuthHooks } from "@beignet/core/server";
|
|
57
|
-
*
|
|
58
|
-
* const auth = createAuthHooks<AppContext>()({
|
|
59
|
-
* resolve: ({ ctx }) => {
|
|
60
|
-
* return ctx.auth ? { user: ctx.auth.user } : null;
|
|
61
|
-
* },
|
|
62
23
|
* });
|
|
63
24
|
*
|
|
64
|
-
* const
|
|
65
|
-
* ports: basePorts,
|
|
66
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
67
|
-
* context: async ({ ports }) => ({ ports }),
|
|
68
|
-
* });
|
|
25
|
+
* export const authProvider = createBetterAuthProvider({ auth });
|
|
69
26
|
* ```
|
|
70
27
|
*/
|
|
71
28
|
import { AuthUnauthorizedError, } from "@beignet/core/ports";
|
|
@@ -74,148 +31,152 @@ function errorMessage(error) {
|
|
|
74
31
|
return error instanceof Error ? error.message : String(error);
|
|
75
32
|
}
|
|
76
33
|
/**
|
|
77
|
-
* Create a Beignet
|
|
34
|
+
* Create a Beignet auth port backed by an existing Better Auth server.
|
|
78
35
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
36
|
+
* Use this for custom app wiring. The adapter does not own the Better Auth
|
|
37
|
+
* instance or add lifecycle hooks.
|
|
38
|
+
*/
|
|
39
|
+
export function createBetterAuthPort({ auth, instrumentation: instrumentationTarget, }) {
|
|
40
|
+
const instrumentation = createProviderInstrumentation(instrumentationTarget, {
|
|
41
|
+
providerName: "auth-better-auth",
|
|
42
|
+
watcher: "auth",
|
|
43
|
+
});
|
|
44
|
+
async function resolveSession(req) {
|
|
45
|
+
const session = await auth.api.getSession({
|
|
46
|
+
headers: req.headers,
|
|
47
|
+
});
|
|
48
|
+
if (!session) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
user: session.user,
|
|
53
|
+
session: session.session,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function recordAuthEvent(event) {
|
|
57
|
+
instrumentation.custom({
|
|
58
|
+
name: `auth.${event.operation}`,
|
|
59
|
+
label: `Auth ${event.operation}`,
|
|
60
|
+
summary: event.summary,
|
|
61
|
+
details: {
|
|
62
|
+
operation: event.operation,
|
|
63
|
+
authenticated: event.authenticated,
|
|
64
|
+
durationMs: event.durationMs,
|
|
65
|
+
...(event.error ? { error: event.error } : {}),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
async getSession(req) {
|
|
71
|
+
const startedAt = Date.now();
|
|
72
|
+
try {
|
|
73
|
+
const session = await resolveSession(req);
|
|
74
|
+
recordAuthEvent({
|
|
75
|
+
operation: "getSession",
|
|
76
|
+
authenticated: session != null,
|
|
77
|
+
summary: session ? "Session found" : "No session",
|
|
78
|
+
durationMs: Date.now() - startedAt,
|
|
79
|
+
});
|
|
80
|
+
return session;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
recordAuthEvent({
|
|
84
|
+
operation: "getSession.failed",
|
|
85
|
+
authenticated: false,
|
|
86
|
+
summary: "Session lookup failed",
|
|
87
|
+
durationMs: Date.now() - startedAt,
|
|
88
|
+
error: errorMessage(error),
|
|
89
|
+
});
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
async getUser(req) {
|
|
94
|
+
const startedAt = Date.now();
|
|
95
|
+
try {
|
|
96
|
+
const session = await resolveSession(req);
|
|
97
|
+
recordAuthEvent({
|
|
98
|
+
operation: "getUser",
|
|
99
|
+
authenticated: session != null,
|
|
100
|
+
summary: session ? "User found" : "No user",
|
|
101
|
+
durationMs: Date.now() - startedAt,
|
|
102
|
+
});
|
|
103
|
+
return session?.user ?? null;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
recordAuthEvent({
|
|
107
|
+
operation: "getUser.failed",
|
|
108
|
+
authenticated: false,
|
|
109
|
+
summary: "User lookup failed",
|
|
110
|
+
durationMs: Date.now() - startedAt,
|
|
111
|
+
error: errorMessage(error),
|
|
112
|
+
});
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async requireUser(req) {
|
|
117
|
+
const startedAt = Date.now();
|
|
118
|
+
try {
|
|
119
|
+
const session = await resolveSession(req);
|
|
120
|
+
if (!session) {
|
|
121
|
+
recordAuthEvent({
|
|
122
|
+
operation: "requireUser",
|
|
123
|
+
authenticated: false,
|
|
124
|
+
summary: "Unauthorized",
|
|
125
|
+
durationMs: Date.now() - startedAt,
|
|
126
|
+
});
|
|
127
|
+
throw new AuthUnauthorizedError();
|
|
128
|
+
}
|
|
129
|
+
recordAuthEvent({
|
|
130
|
+
operation: "requireUser",
|
|
131
|
+
authenticated: true,
|
|
132
|
+
summary: "Authorized",
|
|
133
|
+
durationMs: Date.now() - startedAt,
|
|
134
|
+
});
|
|
135
|
+
return session.user;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (error instanceof AuthUnauthorizedError) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
recordAuthEvent({
|
|
142
|
+
operation: "requireUser.failed",
|
|
143
|
+
authenticated: false,
|
|
144
|
+
summary: "Required user lookup failed",
|
|
145
|
+
durationMs: Date.now() - startedAt,
|
|
146
|
+
error: errorMessage(error),
|
|
147
|
+
});
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create a Beignet provider for an existing Better Auth server.
|
|
87
155
|
*
|
|
88
156
|
* @example
|
|
89
157
|
* ```ts
|
|
90
158
|
* import { betterAuth } from "better-auth";
|
|
91
159
|
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
92
|
-
* import {
|
|
160
|
+
* import { db } from "./db";
|
|
93
161
|
*
|
|
94
162
|
* const auth = betterAuth({ database: db });
|
|
95
|
-
*
|
|
96
|
-
* const server = await createNextServer({
|
|
97
|
-
* ports: basePorts,
|
|
98
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
99
|
-
* });
|
|
163
|
+
* const provider = createBetterAuthProvider({ auth });
|
|
100
164
|
* ```
|
|
101
165
|
*/
|
|
102
|
-
export function createBetterAuthProvider(auth) {
|
|
166
|
+
export function createBetterAuthProvider({ auth, }) {
|
|
103
167
|
return createProvider({
|
|
104
168
|
name: "auth-better-auth",
|
|
169
|
+
metadata: {
|
|
170
|
+
packageName: "@beignet/provider-auth-better-auth",
|
|
171
|
+
ports: ["auth"],
|
|
172
|
+
watchers: ["auth"],
|
|
173
|
+
},
|
|
105
174
|
async setup({ ports }) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
110
|
-
async function resolveSession(req) {
|
|
111
|
-
const session = await auth.api.getSession({
|
|
112
|
-
headers: req.headers,
|
|
113
|
-
});
|
|
114
|
-
if (!session) {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
user: session.user,
|
|
119
|
-
session: session.session,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function recordAuthEvent(event) {
|
|
123
|
-
instrumentation.custom({
|
|
124
|
-
name: `auth.${event.operation}`,
|
|
125
|
-
label: `Auth ${event.operation}`,
|
|
126
|
-
summary: event.summary,
|
|
127
|
-
details: {
|
|
128
|
-
operation: event.operation,
|
|
129
|
-
authenticated: event.authenticated,
|
|
130
|
-
durationMs: event.durationMs,
|
|
131
|
-
...(event.error ? { error: event.error } : {}),
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
const authPort = {
|
|
136
|
-
async getSession(req) {
|
|
137
|
-
const startedAt = Date.now();
|
|
138
|
-
try {
|
|
139
|
-
const session = await resolveSession(req);
|
|
140
|
-
recordAuthEvent({
|
|
141
|
-
operation: "getSession",
|
|
142
|
-
authenticated: session != null,
|
|
143
|
-
summary: session ? "Session found" : "No session",
|
|
144
|
-
durationMs: Date.now() - startedAt,
|
|
145
|
-
});
|
|
146
|
-
return session;
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
recordAuthEvent({
|
|
150
|
-
operation: "getSession.failed",
|
|
151
|
-
authenticated: false,
|
|
152
|
-
summary: "Session lookup failed",
|
|
153
|
-
durationMs: Date.now() - startedAt,
|
|
154
|
-
error: errorMessage(error),
|
|
155
|
-
});
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
async getUser(req) {
|
|
160
|
-
const startedAt = Date.now();
|
|
161
|
-
try {
|
|
162
|
-
const session = await resolveSession(req);
|
|
163
|
-
recordAuthEvent({
|
|
164
|
-
operation: "getUser",
|
|
165
|
-
authenticated: session != null,
|
|
166
|
-
summary: session ? "User found" : "No user",
|
|
167
|
-
durationMs: Date.now() - startedAt,
|
|
168
|
-
});
|
|
169
|
-
return session?.user ?? null;
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
recordAuthEvent({
|
|
173
|
-
operation: "getUser.failed",
|
|
174
|
-
authenticated: false,
|
|
175
|
-
summary: "User lookup failed",
|
|
176
|
-
durationMs: Date.now() - startedAt,
|
|
177
|
-
error: errorMessage(error),
|
|
178
|
-
});
|
|
179
|
-
throw error;
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
async requireUser(req) {
|
|
183
|
-
const startedAt = Date.now();
|
|
184
|
-
try {
|
|
185
|
-
const session = await resolveSession(req);
|
|
186
|
-
if (!session) {
|
|
187
|
-
recordAuthEvent({
|
|
188
|
-
operation: "requireUser",
|
|
189
|
-
authenticated: false,
|
|
190
|
-
summary: "Unauthorized",
|
|
191
|
-
durationMs: Date.now() - startedAt,
|
|
192
|
-
});
|
|
193
|
-
throw new AuthUnauthorizedError();
|
|
194
|
-
}
|
|
195
|
-
recordAuthEvent({
|
|
196
|
-
operation: "requireUser",
|
|
197
|
-
authenticated: true,
|
|
198
|
-
summary: "Authorized",
|
|
199
|
-
durationMs: Date.now() - startedAt,
|
|
200
|
-
});
|
|
201
|
-
return session.user;
|
|
202
|
-
}
|
|
203
|
-
catch (error) {
|
|
204
|
-
if (error instanceof AuthUnauthorizedError) {
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
recordAuthEvent({
|
|
208
|
-
operation: "requireUser.failed",
|
|
209
|
-
authenticated: false,
|
|
210
|
-
summary: "Required user lookup failed",
|
|
211
|
-
durationMs: Date.now() - startedAt,
|
|
212
|
-
error: errorMessage(error),
|
|
213
|
-
});
|
|
214
|
-
throw error;
|
|
215
|
-
}
|
|
175
|
+
return {
|
|
176
|
+
ports: {
|
|
177
|
+
auth: createBetterAuthPort({ auth, instrumentation: ports }),
|
|
216
178
|
},
|
|
217
179
|
};
|
|
218
|
-
return { ports: { auth: authPort } };
|
|
219
180
|
},
|
|
220
181
|
});
|
|
221
182
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beignet/provider-auth-better-auth",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.43",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Better Auth provider for Beignet - adds auth port for authentication and session management",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"better-auth": ">=1.3.26 <1.7.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
+
"@beignet/core": "*",
|
|
64
65
|
"@beignet/devtools": "*",
|
|
65
66
|
"@types/bun": "^1.3.13",
|
|
66
67
|
"@types/node": "^20.10.0",
|
|
@@ -85,7 +86,8 @@
|
|
|
85
86
|
"registration": {
|
|
86
87
|
"required": true,
|
|
87
88
|
"tokens": [
|
|
88
|
-
"createBetterAuthProvider"
|
|
89
|
+
"createBetterAuthProvider",
|
|
90
|
+
"createBetterAuthPort"
|
|
89
91
|
]
|
|
90
92
|
}
|
|
91
93
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: auth-provider
|
|
3
|
-
description: "Wire Beignet Better Auth integration with @beignet/provider-auth-better-auth: app-owned Better Auth config, auth routes, AuthPort typing,
|
|
3
|
+
description: "Wire Beignet Better Auth integration with @beignet/provider-auth-better-auth: app-owned Better Auth config, createBetterAuthProvider and createBetterAuthPort wiring, auth routes, AuthPort typing, server context, createAuthHooks, requireUser helpers, policies, devtools auth instrumentation, tests, and doctor drift. Use when adding, fixing, or reviewing Better Auth setup in a Beignet app."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Beignet Better Auth Provider
|
|
@@ -35,11 +35,39 @@ Create Better Auth in an app-local module, then register the provider in
|
|
|
35
35
|
import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
36
36
|
import { auth } from "@/lib/better-auth";
|
|
37
37
|
|
|
38
|
-
export const providers = [createBetterAuthProvider(auth)] as const;
|
|
38
|
+
export const providers = [createBetterAuthProvider({ auth })] as const;
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
For custom provider setup, adapt the same caller-owned instance inside an
|
|
42
|
+
app-local provider:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// server/providers.ts
|
|
46
|
+
import { createProvider } from "@beignet/core/providers";
|
|
47
|
+
import { createBetterAuthPort } from "@beignet/provider-auth-better-auth";
|
|
48
|
+
import { auth } from "@/lib/better-auth";
|
|
49
|
+
|
|
50
|
+
export const appAuthProvider = createProvider({
|
|
51
|
+
name: "app-auth",
|
|
52
|
+
setup({ ports }) {
|
|
53
|
+
return {
|
|
54
|
+
ports: {
|
|
55
|
+
auth: createBetterAuthPort({ auth, instrumentation: ports }),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export const providers = [appAuthProvider] as const;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Keep `appAuthProvider` in `server/providers.ts` and register it in that file's
|
|
65
|
+
exported provider list. Provider audit recognizes the direct-adapter token, and
|
|
66
|
+
`auth` remains deferred because the app-local provider contributes it during
|
|
67
|
+
startup. Neither factory owns or closes `auth`.
|
|
68
|
+
|
|
69
|
+
Add `auth` to `definePorts(...)` as a deferred port when either the convenience
|
|
70
|
+
provider or the app-local provider contributes it at startup.
|
|
43
71
|
|
|
44
72
|
```ts
|
|
45
73
|
export const initialPorts = definePorts<AppPorts>()({
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beignet/provider-auth-better-auth
|
|
3
3
|
*
|
|
4
|
-
* Better Auth
|
|
5
|
-
* This provider wraps an already-configured Better Auth server instance
|
|
6
|
-
* and exposes the shared Beignet AuthPort on ctx.ports.auth.
|
|
4
|
+
* Better Auth adapters for Beignet's shared authentication port.
|
|
7
5
|
*
|
|
8
6
|
* The provider does NOT own:
|
|
9
7
|
* - Database schema
|
|
@@ -15,57 +13,16 @@
|
|
|
15
13
|
*
|
|
16
14
|
* @example
|
|
17
15
|
* ```ts
|
|
18
|
-
* //
|
|
16
|
+
* // Configure Better Auth in your app.
|
|
19
17
|
* import { betterAuth } from "better-auth";
|
|
18
|
+
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
20
19
|
* import { db } from "./db";
|
|
21
20
|
*
|
|
22
21
|
* export const auth = betterAuth({
|
|
23
22
|
* database: db,
|
|
24
|
-
* // ...other Better Auth config
|
|
25
|
-
* });
|
|
26
|
-
*
|
|
27
|
-
* // Own this shape in your app instead of exporting provider-inferred types.
|
|
28
|
-
* export type AuthUser = {
|
|
29
|
-
* id: string;
|
|
30
|
-
* name?: string | null;
|
|
31
|
-
* email?: string | null;
|
|
32
|
-
* image?: string | null;
|
|
33
|
-
* };
|
|
34
|
-
*
|
|
35
|
-
* // 2. Define your ports type
|
|
36
|
-
* import type { AuthPort } from "@beignet/core/ports";
|
|
37
|
-
*
|
|
38
|
-
* export type AppPorts = {
|
|
39
|
-
* auth: AuthPort<AuthUser>;
|
|
40
|
-
* // ...other ports
|
|
41
|
-
* };
|
|
42
|
-
*
|
|
43
|
-
* // 3. Wire the provider into Beignet
|
|
44
|
-
* import { createNextServer } from "@beignet/next";
|
|
45
|
-
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
46
|
-
*
|
|
47
|
-
* const server = await createNextServer({
|
|
48
|
-
* ports: basePorts,
|
|
49
|
-
* providers: [
|
|
50
|
-
* createBetterAuthProvider(auth),
|
|
51
|
-
* // ...other providers
|
|
52
|
-
* ],
|
|
53
|
-
* });
|
|
54
|
-
*
|
|
55
|
-
* // 4. Use the auth port through route-scoped auth hooks
|
|
56
|
-
* import { createAuthHooks } from "@beignet/core/server";
|
|
57
|
-
*
|
|
58
|
-
* const auth = createAuthHooks<AppContext>()({
|
|
59
|
-
* resolve: ({ ctx }) => {
|
|
60
|
-
* return ctx.auth ? { user: ctx.auth.user } : null;
|
|
61
|
-
* },
|
|
62
23
|
* });
|
|
63
24
|
*
|
|
64
|
-
* const
|
|
65
|
-
* ports: basePorts,
|
|
66
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
67
|
-
* context: async ({ ports }) => ({ ports }),
|
|
68
|
-
* });
|
|
25
|
+
* export const authProvider = createBetterAuthProvider({ auth });
|
|
69
26
|
* ```
|
|
70
27
|
*/
|
|
71
28
|
|
|
@@ -75,8 +32,11 @@ import {
|
|
|
75
32
|
AuthUnauthorizedError,
|
|
76
33
|
} from "@beignet/core/ports";
|
|
77
34
|
import {
|
|
35
|
+
type AnyProviderConfigSchema,
|
|
78
36
|
createProvider,
|
|
79
37
|
createProviderInstrumentation,
|
|
38
|
+
type ProviderInstrumentationTarget,
|
|
39
|
+
type ServiceProvider,
|
|
80
40
|
} from "@beignet/core/providers";
|
|
81
41
|
|
|
82
42
|
/**
|
|
@@ -103,169 +63,234 @@ export type BetterAuthServer<User = unknown, Session = unknown> = {
|
|
|
103
63
|
};
|
|
104
64
|
};
|
|
105
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Options for adapting an existing Better Auth server to Beignet's auth port.
|
|
68
|
+
*/
|
|
69
|
+
export interface CreateBetterAuthPortOptions<
|
|
70
|
+
User = unknown,
|
|
71
|
+
Session = unknown,
|
|
72
|
+
> {
|
|
73
|
+
/**
|
|
74
|
+
* Better Auth server instance configured and owned by the application.
|
|
75
|
+
*/
|
|
76
|
+
auth: BetterAuthServer<User, Session>;
|
|
77
|
+
/**
|
|
78
|
+
* Optional provider instrumentation target.
|
|
79
|
+
*/
|
|
80
|
+
instrumentation?: ProviderInstrumentationTarget;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for installing an existing Better Auth server as a Beignet provider.
|
|
85
|
+
*/
|
|
86
|
+
export interface CreateBetterAuthProviderOptions<
|
|
87
|
+
User = unknown,
|
|
88
|
+
Session = unknown,
|
|
89
|
+
> {
|
|
90
|
+
/**
|
|
91
|
+
* Better Auth server instance configured and owned by the application.
|
|
92
|
+
*/
|
|
93
|
+
auth: BetterAuthServer<User, Session>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Ports contributed by `createBetterAuthProvider`.
|
|
98
|
+
*/
|
|
99
|
+
export interface BetterAuthProviderPorts<User = unknown, Session = unknown> {
|
|
100
|
+
/**
|
|
101
|
+
* Beignet auth port backed by Better Auth.
|
|
102
|
+
*/
|
|
103
|
+
auth: AuthPort<User, Session>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Better Auth lifecycle provider with stable contributed-port typing.
|
|
108
|
+
*/
|
|
109
|
+
export type BetterAuthProvider<
|
|
110
|
+
User = unknown,
|
|
111
|
+
Session = unknown,
|
|
112
|
+
> = ServiceProvider<
|
|
113
|
+
unknown,
|
|
114
|
+
AnyProviderConfigSchema<void>,
|
|
115
|
+
Pick<
|
|
116
|
+
BetterAuthProviderPorts<User, Session>,
|
|
117
|
+
keyof BetterAuthProviderPorts<User, Session>
|
|
118
|
+
>
|
|
119
|
+
>;
|
|
120
|
+
|
|
106
121
|
function errorMessage(error: unknown): string {
|
|
107
122
|
return error instanceof Error ? error.message : String(error);
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
/**
|
|
111
|
-
* Create a Beignet
|
|
112
|
-
*
|
|
113
|
-
* This factory accepts an already-configured Better Auth server instance
|
|
114
|
-
* and returns a provider that can be registered with Beignet.
|
|
115
|
-
*
|
|
116
|
-
* The provider extends ports.auth with an AuthPort that wraps the
|
|
117
|
-
* Better Auth instance with a simple, stable API.
|
|
118
|
-
*
|
|
119
|
-
* @param auth - A Better Auth server instance configured in the application
|
|
120
|
-
* @returns A Beignet provider that extends ports.auth
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```ts
|
|
124
|
-
* import { betterAuth } from "better-auth";
|
|
125
|
-
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
126
|
-
* import { createNextServer } from "@beignet/next";
|
|
126
|
+
* Create a Beignet auth port backed by an existing Better Auth server.
|
|
127
127
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* const server = await createNextServer({
|
|
131
|
-
* ports: basePorts,
|
|
132
|
-
* providers: [createBetterAuthProvider(auth)],
|
|
133
|
-
* });
|
|
134
|
-
* ```
|
|
128
|
+
* Use this for custom app wiring. The adapter does not own the Better Auth
|
|
129
|
+
* instance or add lifecycle hooks.
|
|
135
130
|
*/
|
|
136
|
-
export function
|
|
137
|
-
auth
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
export function createBetterAuthPort<User = unknown, Session = unknown>({
|
|
132
|
+
auth,
|
|
133
|
+
instrumentation: instrumentationTarget,
|
|
134
|
+
}: CreateBetterAuthPortOptions<User, Session>): AuthPort<User, Session> {
|
|
135
|
+
const instrumentation = createProviderInstrumentation(instrumentationTarget, {
|
|
136
|
+
providerName: "auth-better-auth",
|
|
137
|
+
watcher: "auth",
|
|
138
|
+
});
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
async function resolveSession(req: AuthRequestLike) {
|
|
141
|
+
const session = await auth.api.getSession({
|
|
142
|
+
headers: req.headers,
|
|
143
|
+
});
|
|
144
|
+
if (!session) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
user: session.user,
|
|
150
|
+
session: session.session,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
function recordAuthEvent(event: {
|
|
155
|
+
operation: string;
|
|
156
|
+
authenticated: boolean;
|
|
157
|
+
summary: string;
|
|
158
|
+
durationMs: number;
|
|
159
|
+
error?: string;
|
|
160
|
+
}) {
|
|
161
|
+
instrumentation.custom({
|
|
162
|
+
name: `auth.${event.operation}`,
|
|
163
|
+
label: `Auth ${event.operation}`,
|
|
164
|
+
summary: event.summary,
|
|
165
|
+
details: {
|
|
166
|
+
operation: event.operation,
|
|
167
|
+
authenticated: event.authenticated,
|
|
168
|
+
durationMs: event.durationMs,
|
|
169
|
+
...(event.error ? { error: event.error } : {}),
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
async getSession(req) {
|
|
176
|
+
const startedAt = Date.now();
|
|
177
|
+
try {
|
|
178
|
+
const session = await resolveSession(req);
|
|
179
|
+
recordAuthEvent({
|
|
180
|
+
operation: "getSession",
|
|
181
|
+
authenticated: session != null,
|
|
182
|
+
summary: session ? "Session found" : "No session",
|
|
183
|
+
durationMs: Date.now() - startedAt,
|
|
151
184
|
});
|
|
152
|
-
if (!session) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
185
|
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
return session;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
recordAuthEvent({
|
|
189
|
+
operation: "getSession.failed",
|
|
190
|
+
authenticated: false,
|
|
191
|
+
summary: "Session lookup failed",
|
|
192
|
+
durationMs: Date.now() - startedAt,
|
|
193
|
+
error: errorMessage(error),
|
|
194
|
+
});
|
|
195
|
+
throw error;
|
|
160
196
|
}
|
|
197
|
+
},
|
|
161
198
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
label: `Auth ${event.operation}`,
|
|
172
|
-
summary: event.summary,
|
|
173
|
-
details: {
|
|
174
|
-
operation: event.operation,
|
|
175
|
-
authenticated: event.authenticated,
|
|
176
|
-
durationMs: event.durationMs,
|
|
177
|
-
...(event.error ? { error: event.error } : {}),
|
|
178
|
-
},
|
|
199
|
+
async getUser(req) {
|
|
200
|
+
const startedAt = Date.now();
|
|
201
|
+
try {
|
|
202
|
+
const session = await resolveSession(req);
|
|
203
|
+
recordAuthEvent({
|
|
204
|
+
operation: "getUser",
|
|
205
|
+
authenticated: session != null,
|
|
206
|
+
summary: session ? "User found" : "No user",
|
|
207
|
+
durationMs: Date.now() - startedAt,
|
|
179
208
|
});
|
|
209
|
+
return session?.user ?? null;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
recordAuthEvent({
|
|
212
|
+
operation: "getUser.failed",
|
|
213
|
+
authenticated: false,
|
|
214
|
+
summary: "User lookup failed",
|
|
215
|
+
durationMs: Date.now() - startedAt,
|
|
216
|
+
error: errorMessage(error),
|
|
217
|
+
});
|
|
218
|
+
throw error;
|
|
180
219
|
}
|
|
220
|
+
},
|
|
181
221
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
} catch (error) {
|
|
196
|
-
recordAuthEvent({
|
|
197
|
-
operation: "getSession.failed",
|
|
198
|
-
authenticated: false,
|
|
199
|
-
summary: "Session lookup failed",
|
|
200
|
-
durationMs: Date.now() - startedAt,
|
|
201
|
-
error: errorMessage(error),
|
|
202
|
-
});
|
|
203
|
-
throw error;
|
|
204
|
-
}
|
|
205
|
-
},
|
|
222
|
+
async requireUser(req) {
|
|
223
|
+
const startedAt = Date.now();
|
|
224
|
+
try {
|
|
225
|
+
const session = await resolveSession(req);
|
|
226
|
+
if (!session) {
|
|
227
|
+
recordAuthEvent({
|
|
228
|
+
operation: "requireUser",
|
|
229
|
+
authenticated: false,
|
|
230
|
+
summary: "Unauthorized",
|
|
231
|
+
durationMs: Date.now() - startedAt,
|
|
232
|
+
});
|
|
233
|
+
throw new AuthUnauthorizedError();
|
|
234
|
+
}
|
|
206
235
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
} catch (error) {
|
|
219
|
-
recordAuthEvent({
|
|
220
|
-
operation: "getUser.failed",
|
|
221
|
-
authenticated: false,
|
|
222
|
-
summary: "User lookup failed",
|
|
223
|
-
durationMs: Date.now() - startedAt,
|
|
224
|
-
error: errorMessage(error),
|
|
225
|
-
});
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
},
|
|
236
|
+
recordAuthEvent({
|
|
237
|
+
operation: "requireUser",
|
|
238
|
+
authenticated: true,
|
|
239
|
+
summary: "Authorized",
|
|
240
|
+
durationMs: Date.now() - startedAt,
|
|
241
|
+
});
|
|
242
|
+
return session.user;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error instanceof AuthUnauthorizedError) {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
229
247
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
248
|
+
recordAuthEvent({
|
|
249
|
+
operation: "requireUser.failed",
|
|
250
|
+
authenticated: false,
|
|
251
|
+
summary: "Required user lookup failed",
|
|
252
|
+
durationMs: Date.now() - startedAt,
|
|
253
|
+
error: errorMessage(error),
|
|
254
|
+
});
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
243
260
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Create a Beignet provider for an existing Better Auth server.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* import { betterAuth } from "better-auth";
|
|
267
|
+
* import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";
|
|
268
|
+
* import { db } from "./db";
|
|
269
|
+
*
|
|
270
|
+
* const auth = betterAuth({ database: db });
|
|
271
|
+
* const provider = createBetterAuthProvider({ auth });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
export function createBetterAuthProvider<User = unknown, Session = unknown>({
|
|
275
|
+
auth,
|
|
276
|
+
}: CreateBetterAuthProviderOptions<User, Session>): BetterAuthProvider<
|
|
277
|
+
User,
|
|
278
|
+
Session
|
|
279
|
+
> {
|
|
280
|
+
return createProvider({
|
|
281
|
+
name: "auth-better-auth",
|
|
282
|
+
metadata: {
|
|
283
|
+
packageName: "@beignet/provider-auth-better-auth",
|
|
284
|
+
ports: ["auth"],
|
|
285
|
+
watchers: ["auth"],
|
|
286
|
+
},
|
|
255
287
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
error: errorMessage(error),
|
|
262
|
-
});
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
},
|
|
288
|
+
async setup({ ports }) {
|
|
289
|
+
return {
|
|
290
|
+
ports: {
|
|
291
|
+
auth: createBetterAuthPort({ auth, instrumentation: ports }),
|
|
292
|
+
} satisfies BetterAuthProviderPorts<User, Session>,
|
|
266
293
|
};
|
|
267
|
-
|
|
268
|
-
return { ports: { auth: authPort } };
|
|
269
294
|
},
|
|
270
295
|
});
|
|
271
296
|
}
|