@delmaredigital/payload-better-auth 0.3.12 → 0.3.14
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/README.md +46 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/utils/session.d.ts +73 -14
- package/dist/utils/session.js +79 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -552,24 +552,62 @@ betterAuthStrategy({
|
|
|
552
552
|
|--------|------|-------------|
|
|
553
553
|
| `usersCollection` | `string` | The collection slug for users (default: `'users'`) |
|
|
554
554
|
|
|
555
|
-
### `getServerSession(payload, headers)`
|
|
555
|
+
### `getServerSession<TUser>(payload, headers)`
|
|
556
556
|
|
|
557
|
-
Get the current session on the server.
|
|
557
|
+
Get the current session on the server. Pass your Payload `User` type for full type safety:
|
|
558
558
|
|
|
559
559
|
```ts
|
|
560
|
-
|
|
561
|
-
|
|
560
|
+
import { getServerSession } from '@delmaredigital/payload-better-auth'
|
|
561
|
+
import type { User } from '@/payload-types'
|
|
562
|
+
|
|
563
|
+
const session = await getServerSession<User>(payload, headersList)
|
|
564
|
+
// session.user.role, session.user.firstName, etc. are fully typed
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### `getServerUser<TUser>(payload, headers)`
|
|
568
|
+
|
|
569
|
+
Get the current user on the server (shorthand for `session.user`):
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
import { getServerUser } from '@delmaredigital/payload-better-auth'
|
|
573
|
+
import type { User } from '@/payload-types'
|
|
574
|
+
|
|
575
|
+
const user = await getServerUser<User>(payload, headersList)
|
|
576
|
+
// user.role, user.firstName, etc. are fully typed
|
|
562
577
|
```
|
|
563
578
|
|
|
564
|
-
### `
|
|
579
|
+
### `createSessionHelpers<TUser>(options?)`
|
|
565
580
|
|
|
566
|
-
|
|
581
|
+
Create typed session helpers bound to your User type. Define once, import everywhere — no generics needed at call sites:
|
|
567
582
|
|
|
568
583
|
```ts
|
|
569
|
-
|
|
570
|
-
|
|
584
|
+
// lib/auth.ts
|
|
585
|
+
import { createSessionHelpers } from '@delmaredigital/payload-better-auth'
|
|
586
|
+
import type { User } from '@/payload-types'
|
|
587
|
+
|
|
588
|
+
export const { getServerSession, getServerUser } = createSessionHelpers<User>()
|
|
571
589
|
```
|
|
572
590
|
|
|
591
|
+
```ts
|
|
592
|
+
// app/page.tsx
|
|
593
|
+
import { getServerSession } from '@/lib/auth'
|
|
594
|
+
|
|
595
|
+
const session = await getServerSession(payload, headersList)
|
|
596
|
+
// session.user is typed as User — no generic needed
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Serial IDs (Payload default):** Better Auth always returns string IDs from `api.getSession()`, which causes Payload relationship fields to reject them. Pass `idType: 'number'` to coerce ID fields to numbers automatically:
|
|
600
|
+
|
|
601
|
+
```ts
|
|
602
|
+
export const { getServerSession, getServerUser } = createSessionHelpers<User>({
|
|
603
|
+
idType: 'number', // coerces user.id, session.userId, etc. to numbers
|
|
604
|
+
})
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
| Option | Type | Description |
|
|
608
|
+
|--------|------|-------------|
|
|
609
|
+
| `idType` | `'number' \| 'text'` | Set to `'number'` when using serial IDs to coerce string IDs to numbers. Matches the adapter's `adapterConfig.idType` option. |
|
|
610
|
+
|
|
573
611
|
### `withBetterAuthDefaults(options)`
|
|
574
612
|
|
|
575
613
|
Applies sensible defaults to Better Auth options. Useful for simplifying common configurations.
|
package/dist/index.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ export { extractApiKeyFromRequest, getApiKeyInfo, hasScope, hasAnyScope as hasAn
|
|
|
22
22
|
export type { ApiKeyInfo, ApiKeyAccessConfig, } from './utils/apiKeyAccess.js';
|
|
23
23
|
export { detectAuthConfig } from './utils/detectAuthConfig.js';
|
|
24
24
|
export type { AuthDetectionResult } from './utils/detectAuthConfig.js';
|
|
25
|
-
export { getServerSession, getServerUser } from './utils/session.js';
|
|
26
|
-
export type { Session } from './utils/session.js';
|
|
25
|
+
export { getServerSession, getServerUser, createSessionHelpers } from './utils/session.js';
|
|
26
|
+
export type { Session, SessionHelperOptions } from './utils/session.js';
|
|
27
27
|
export { firstUserAdminHooks } from './utils/firstUserAdmin.js';
|
|
28
28
|
export type { FirstUserAdminOptions } from './utils/firstUserAdmin.js';
|
|
29
29
|
export { withBetterAuthDefaults, apiKeyWithDefaults } from './utils/betterAuthDefaults.js';
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ export { extractApiKeyFromRequest, getApiKeyInfo, hasScope, hasAnyScope as hasAn
|
|
|
20
20
|
// Auth config detection utility
|
|
21
21
|
export { detectAuthConfig } from './utils/detectAuthConfig.js';
|
|
22
22
|
// Session utilities
|
|
23
|
-
export { getServerSession, getServerUser } from './utils/session.js';
|
|
23
|
+
export { getServerSession, getServerUser, createSessionHelpers } from './utils/session.js';
|
|
24
24
|
// First user admin hook utility
|
|
25
25
|
export { firstUserAdminHooks } from './utils/firstUserAdmin.js';
|
|
26
26
|
// Better Auth defaults utility
|
package/dist/utils/session.d.ts
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
import type { BasePayload } from 'payload';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
type DefaultUser = {
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
image?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
export type Session<TUser = DefaultUser> = {
|
|
15
|
+
user: TUser;
|
|
15
16
|
session: {
|
|
16
17
|
id: string;
|
|
17
18
|
expiresAt: Date;
|
|
@@ -21,35 +22,43 @@ export type Session = {
|
|
|
21
22
|
/**
|
|
22
23
|
* Get the current session from headers.
|
|
23
24
|
*
|
|
25
|
+
* Accepts an optional generic type parameter to narrow the user type.
|
|
26
|
+
* Pass your Payload-generated `User` type for full type safety.
|
|
27
|
+
*
|
|
24
28
|
* @example
|
|
25
29
|
* ```ts
|
|
26
30
|
* import { headers } from 'next/headers'
|
|
27
|
-
* import { getServerSession } from '@
|
|
31
|
+
* import { getServerSession } from '@delmaredigital/payload-better-auth'
|
|
32
|
+
* import type { User } from '@/payload-types'
|
|
28
33
|
*
|
|
29
34
|
* export default async function Page() {
|
|
30
35
|
* const headersList = await headers()
|
|
31
|
-
* const session = await getServerSession(payload, headersList)
|
|
36
|
+
* const session = await getServerSession<User>(payload, headersList)
|
|
32
37
|
*
|
|
33
38
|
* if (!session) {
|
|
34
39
|
* redirect('/login')
|
|
35
40
|
* }
|
|
36
41
|
*
|
|
42
|
+
* // session.user.role is fully typed
|
|
37
43
|
* return <div>Hello {session.user.name}</div>
|
|
38
44
|
* }
|
|
39
45
|
* ```
|
|
40
46
|
*/
|
|
41
|
-
export declare function getServerSession(payload: BasePayload, headers: Headers): Promise<Session | null>;
|
|
47
|
+
export declare function getServerSession<TUser = DefaultUser>(payload: BasePayload, headers: Headers): Promise<Session<TUser> | null>;
|
|
42
48
|
/**
|
|
43
49
|
* Get the current user from the session.
|
|
44
50
|
*
|
|
51
|
+
* Accepts an optional generic type parameter to narrow the user type.
|
|
52
|
+
*
|
|
45
53
|
* @example
|
|
46
54
|
* ```ts
|
|
47
55
|
* import { headers } from 'next/headers'
|
|
48
|
-
* import { getServerUser } from '@
|
|
56
|
+
* import { getServerUser } from '@delmaredigital/payload-better-auth'
|
|
57
|
+
* import type { User } from '@/payload-types'
|
|
49
58
|
*
|
|
50
59
|
* export default async function Page() {
|
|
51
60
|
* const headersList = await headers()
|
|
52
|
-
* const user = await getServerUser(payload, headersList)
|
|
61
|
+
* const user = await getServerUser<User>(payload, headersList)
|
|
53
62
|
*
|
|
54
63
|
* if (!user) {
|
|
55
64
|
* redirect('/login')
|
|
@@ -59,4 +68,54 @@ export declare function getServerSession(payload: BasePayload, headers: Headers)
|
|
|
59
68
|
* }
|
|
60
69
|
* ```
|
|
61
70
|
*/
|
|
62
|
-
export declare function getServerUser(payload: BasePayload, headers: Headers): Promise<
|
|
71
|
+
export declare function getServerUser<TUser = DefaultUser>(payload: BasePayload, headers: Headers): Promise<TUser | null>;
|
|
72
|
+
export type SessionHelperOptions = {
|
|
73
|
+
/**
|
|
74
|
+
* ID type strategy matching your adapter's `adapterConfig.idType`.
|
|
75
|
+
*
|
|
76
|
+
* Set to `'number'` when using Payload's default serial IDs.
|
|
77
|
+
* Better Auth always returns string IDs from `api.getSession()` —
|
|
78
|
+
* this option coerces `id` and `*Id` / `*_id` fields to numbers
|
|
79
|
+
* so they work directly in Payload relationship fields.
|
|
80
|
+
*
|
|
81
|
+
* @default undefined (no coercion)
|
|
82
|
+
*/
|
|
83
|
+
idType?: 'number' | 'text';
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Create typed session helpers bound to your User type.
|
|
87
|
+
*
|
|
88
|
+
* Define once in a shared file, then import the typed helpers
|
|
89
|
+
* everywhere — no generics needed at call sites.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* // lib/auth.ts
|
|
94
|
+
* import { createSessionHelpers } from '@delmaredigital/payload-better-auth'
|
|
95
|
+
* import type { User } from '@/payload-types'
|
|
96
|
+
*
|
|
97
|
+
* export const { getServerSession, getServerUser } = createSessionHelpers<User>()
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* // With serial IDs (Payload default) — coerces string IDs to numbers
|
|
103
|
+
* export const { getServerSession, getServerUser } = createSessionHelpers<User>({
|
|
104
|
+
* idType: 'number',
|
|
105
|
+
* })
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* // app/page.tsx
|
|
110
|
+
* import { getServerSession } from '@/lib/auth'
|
|
111
|
+
*
|
|
112
|
+
* const session = await getServerSession(payload, headersList)
|
|
113
|
+
* // session.user is typed as User — no generic needed
|
|
114
|
+
* // session.user.id is a number when idType: 'number'
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function createSessionHelpers<TUser = DefaultUser>(options?: SessionHelperOptions): {
|
|
118
|
+
getServerSession: (payload: BasePayload, headers: Headers) => Promise<Session<TUser> | null>;
|
|
119
|
+
getServerUser: (payload: BasePayload, headers: Headers) => Promise<TUser | null>;
|
|
120
|
+
};
|
|
121
|
+
export {};
|
package/dist/utils/session.js
CHANGED
|
@@ -5,19 +5,24 @@
|
|
|
5
5
|
*/ /**
|
|
6
6
|
* Get the current session from headers.
|
|
7
7
|
*
|
|
8
|
+
* Accepts an optional generic type parameter to narrow the user type.
|
|
9
|
+
* Pass your Payload-generated `User` type for full type safety.
|
|
10
|
+
*
|
|
8
11
|
* @example
|
|
9
12
|
* ```ts
|
|
10
13
|
* import { headers } from 'next/headers'
|
|
11
|
-
* import { getServerSession } from '@
|
|
14
|
+
* import { getServerSession } from '@delmaredigital/payload-better-auth'
|
|
15
|
+
* import type { User } from '@/payload-types'
|
|
12
16
|
*
|
|
13
17
|
* export default async function Page() {
|
|
14
18
|
* const headersList = await headers()
|
|
15
|
-
* const session = await getServerSession(payload, headersList)
|
|
19
|
+
* const session = await getServerSession<User>(payload, headersList)
|
|
16
20
|
*
|
|
17
21
|
* if (!session) {
|
|
18
22
|
* redirect('/login')
|
|
19
23
|
* }
|
|
20
24
|
*
|
|
25
|
+
* // session.user.role is fully typed
|
|
21
26
|
* return <div>Hello {session.user.name}</div>
|
|
22
27
|
* }
|
|
23
28
|
* ```
|
|
@@ -40,14 +45,17 @@
|
|
|
40
45
|
/**
|
|
41
46
|
* Get the current user from the session.
|
|
42
47
|
*
|
|
48
|
+
* Accepts an optional generic type parameter to narrow the user type.
|
|
49
|
+
*
|
|
43
50
|
* @example
|
|
44
51
|
* ```ts
|
|
45
52
|
* import { headers } from 'next/headers'
|
|
46
|
-
* import { getServerUser } from '@
|
|
53
|
+
* import { getServerUser } from '@delmaredigital/payload-better-auth'
|
|
54
|
+
* import type { User } from '@/payload-types'
|
|
47
55
|
*
|
|
48
56
|
* export default async function Page() {
|
|
49
57
|
* const headersList = await headers()
|
|
50
|
-
* const user = await getServerUser(payload, headersList)
|
|
58
|
+
* const user = await getServerUser<User>(payload, headersList)
|
|
51
59
|
*
|
|
52
60
|
* if (!user) {
|
|
53
61
|
* redirect('/login')
|
|
@@ -60,3 +68,70 @@
|
|
|
60
68
|
const session = await getServerSession(payload, headers);
|
|
61
69
|
return session?.user ?? null;
|
|
62
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Coerce numeric-string ID fields to numbers on a shallow object.
|
|
73
|
+
* Matches the adapter's heuristic: `id`, fields ending in `Id` or `_id`.
|
|
74
|
+
*/ function coerceIds(obj) {
|
|
75
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
76
|
+
const result = {
|
|
77
|
+
...obj
|
|
78
|
+
};
|
|
79
|
+
for (const [key, value] of Object.entries(result)){
|
|
80
|
+
if (typeof value !== 'string') continue;
|
|
81
|
+
if (key === 'id' || /(?:Id|_id)$/.test(key)) {
|
|
82
|
+
if (/^\d+$/.test(value)) {
|
|
83
|
+
result[key] = parseInt(value, 10);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create typed session helpers bound to your User type.
|
|
91
|
+
*
|
|
92
|
+
* Define once in a shared file, then import the typed helpers
|
|
93
|
+
* everywhere — no generics needed at call sites.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // lib/auth.ts
|
|
98
|
+
* import { createSessionHelpers } from '@delmaredigital/payload-better-auth'
|
|
99
|
+
* import type { User } from '@/payload-types'
|
|
100
|
+
*
|
|
101
|
+
* export const { getServerSession, getServerUser } = createSessionHelpers<User>()
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* // With serial IDs (Payload default) — coerces string IDs to numbers
|
|
107
|
+
* export const { getServerSession, getServerUser } = createSessionHelpers<User>({
|
|
108
|
+
* idType: 'number',
|
|
109
|
+
* })
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* // app/page.tsx
|
|
114
|
+
* import { getServerSession } from '@/lib/auth'
|
|
115
|
+
*
|
|
116
|
+
* const session = await getServerSession(payload, headersList)
|
|
117
|
+
* // session.user is typed as User — no generic needed
|
|
118
|
+
* // session.user.id is a number when idType: 'number'
|
|
119
|
+
* ```
|
|
120
|
+
*/ export function createSessionHelpers(options) {
|
|
121
|
+
const shouldCoerceIds = options?.idType === 'number';
|
|
122
|
+
const typedGetServerSession = async (payload, headers)=>{
|
|
123
|
+
const session = await getServerSession(payload, headers);
|
|
124
|
+
if (!session || !shouldCoerceIds) return session;
|
|
125
|
+
return {
|
|
126
|
+
user: coerceIds(session.user),
|
|
127
|
+
session: coerceIds(session.session)
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
getServerSession: typedGetServerSession,
|
|
132
|
+
getServerUser: async (payload, headers)=>{
|
|
133
|
+
const session = await typedGetServerSession(payload, headers);
|
|
134
|
+
return session?.user ?? null;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|