@delmaredigital/payload-better-auth 0.5.6 → 0.6.1
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/dist/components/management/ApiKeysManagementClient.d.ts +16 -7
- package/dist/components/management/ApiKeysManagementClient.js +293 -286
- package/dist/components/management/views/ApiKeysView.js +55 -9
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/plugin/index.d.ts +8 -8
- package/dist/plugin/index.js +131 -63
- package/dist/types/apiKey.d.ts +19 -47
- package/dist/types/apiKey.js +10 -5
- package/dist/utils/apiKeyAccess.d.ts +47 -95
- package/dist/utils/apiKeyAccess.js +128 -284
- package/dist/utils/generatePermissions.d.ts +11 -0
- package/dist/utils/generatePermissions.js +30 -0
- package/package.json +1 -1
- package/dist/utils/generateScopes.d.ts +0 -20
- package/dist/utils/generateScopes.js +0 -110
|
@@ -2,8 +2,53 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { DefaultTemplate } from '@payloadcms/next/templates';
|
|
3
3
|
import { getVisibleEntities } from '@payloadcms/ui/shared';
|
|
4
4
|
import { ApiKeysManagementClient } from '../ApiKeysManagementClient.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { getApiKeyPermissionsConfig } from '../../../plugin/index.js';
|
|
6
|
+
import { generateCollectionPermissions } from '../../../utils/generatePermissions.js';
|
|
7
|
+
/**
|
|
8
|
+
* Fetch organizations the current user belongs to.
|
|
9
|
+
* Returns an empty array if the members/organizations collections don't exist.
|
|
10
|
+
*/ async function getUserOrganizations(payload, userId) {
|
|
11
|
+
try {
|
|
12
|
+
// Check if members and organizations collections exist
|
|
13
|
+
const collectionSlugs = payload.config.collections.map((c)=>c.slug);
|
|
14
|
+
if (!collectionSlugs.includes('members') || !collectionSlugs.includes('organizations')) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
// Find all memberships for this user
|
|
18
|
+
const memberships = await payload.find({
|
|
19
|
+
collection: 'members',
|
|
20
|
+
where: {
|
|
21
|
+
user: {
|
|
22
|
+
equals: userId
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
limit: 100,
|
|
26
|
+
depth: 0,
|
|
27
|
+
overrideAccess: true
|
|
28
|
+
});
|
|
29
|
+
if (memberships.docs.length === 0) return [];
|
|
30
|
+
// Fetch organization details
|
|
31
|
+
const orgIds = memberships.docs.map((m)=>m.organization);
|
|
32
|
+
const orgs = await payload.find({
|
|
33
|
+
collection: 'organizations',
|
|
34
|
+
where: {
|
|
35
|
+
id: {
|
|
36
|
+
in: orgIds
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
limit: 100,
|
|
40
|
+
depth: 0,
|
|
41
|
+
overrideAccess: true
|
|
42
|
+
});
|
|
43
|
+
return orgs.docs.map((org)=>({
|
|
44
|
+
id: org.id,
|
|
45
|
+
name: org.name || String(org.id)
|
|
46
|
+
}));
|
|
47
|
+
} catch {
|
|
48
|
+
// Collections might not exist or have different schemas — return empty
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
7
52
|
/**
|
|
8
53
|
* API Keys management view for Payload admin panel.
|
|
9
54
|
* Server component that provides the admin layout.
|
|
@@ -16,11 +61,12 @@ import { buildAvailableScopes } from '../../../utils/generateScopes.js';
|
|
|
16
61
|
const visibleEntities = getVisibleEntities({
|
|
17
62
|
req
|
|
18
63
|
});
|
|
19
|
-
// Build
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
//
|
|
23
|
-
const
|
|
64
|
+
// Build permission definitions from collections
|
|
65
|
+
const permissionsConfig = getApiKeyPermissionsConfig();
|
|
66
|
+
const permissions = generateCollectionPermissions(payload.config.collections, permissionsConfig?.excludeCollections);
|
|
67
|
+
// Fetch user's organizations if the organization plugin is in use
|
|
68
|
+
const userId = req.user?.id;
|
|
69
|
+
const organizations = userId ? await getUserOrganizations(payload, userId) : [];
|
|
24
70
|
return /*#__PURE__*/ _jsx(DefaultTemplate, {
|
|
25
71
|
i18n: req.i18n,
|
|
26
72
|
locale: req.locale,
|
|
@@ -31,8 +77,8 @@ import { buildAvailableScopes } from '../../../utils/generateScopes.js';
|
|
|
31
77
|
user: req.user ?? undefined,
|
|
32
78
|
visibleEntities: visibleEntities,
|
|
33
79
|
children: /*#__PURE__*/ _jsx(ApiKeysManagementClient, {
|
|
34
|
-
|
|
35
|
-
|
|
80
|
+
permissions: permissions,
|
|
81
|
+
organizations: organizations
|
|
36
82
|
})
|
|
37
83
|
});
|
|
38
84
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,16 +10,16 @@ export { payloadAdapter, detectDbType, resolveIdType } from './adapter/index.js'
|
|
|
10
10
|
export type { PayloadAdapterConfig, DbType } from './adapter/index.js';
|
|
11
11
|
export { betterAuthCollections } from './adapter/collections.js';
|
|
12
12
|
export type { BetterAuthCollectionsOptions } from './adapter/collections.js';
|
|
13
|
-
export { createBetterAuthPlugin, betterAuthStrategy, resetAuthInstance,
|
|
13
|
+
export { createBetterAuthPlugin, betterAuthStrategy, resetAuthInstance, getApiKeyPermissionsConfig, } from './plugin/index.js';
|
|
14
14
|
export type { Auth, CreateAuthFunction, BetterAuthPluginOptions, BetterAuthPluginAdminOptions, BetterAuthStrategyOptions, } from './plugin/index.js';
|
|
15
15
|
export type { BetterAuthReturn, PayloadWithAuth, PayloadRequestWithBetterAuth, CollectionHookWithBetterAuth, EndpointWithBetterAuth, RoleArray, } from './types/betterAuth.js';
|
|
16
16
|
export type { User, Session as BetterAuthSession, Account, Verification, Apikey, Passkey, Organization, Member, Invitation, Team, TeamMember, TwoFactor, BaseUserFields, BaseSessionFields, BaseAccountFields, UserPluginFields, SessionPluginFields, BetterAuthFullSchema, ModelKey, PluginId, } from './generated-types.js';
|
|
17
|
-
export type {
|
|
18
|
-
export {
|
|
17
|
+
export type { PermissionDefinition, ApiKeyPermissionsConfig, } from './types/apiKey.js';
|
|
18
|
+
export { generateCollectionPermissions, } from './utils/generatePermissions.js';
|
|
19
19
|
export { normalizeRoles, hasAnyRole, hasAllRoles, hasAdminRoles, isAdmin, isAdminField, isAdminOrSelf, canUpdateOwnFields, isAuthenticated, isAuthenticatedField, hasRole, hasRoleField, requireAllRoles, } from './utils/access.js';
|
|
20
20
|
export type { RoleCheckConfig, SelfAccessConfig, FieldUpdateConfig, } from './utils/access.js';
|
|
21
|
-
export { extractApiKeyFromRequest,
|
|
22
|
-
export type {
|
|
21
|
+
export { extractApiKeyFromRequest, requirePermission, requireAnyPermission, requireAllPermissions, allowSessionOrPermission, allowSessionOrAnyPermission, requireApiKey, } from './utils/apiKeyAccess.js';
|
|
22
|
+
export type { ApiKeyPermissionConfig, PermissionCheck, } from './utils/apiKeyAccess.js';
|
|
23
23
|
export { detectAuthConfig } from './utils/detectAuthConfig.js';
|
|
24
24
|
export type { AuthDetectionResult } from './utils/detectAuthConfig.js';
|
|
25
25
|
export { getServerSession, getServerUser, createSessionHelpers } from './utils/session.js';
|
package/dist/index.js
CHANGED
|
@@ -10,13 +10,13 @@ export { payloadAdapter, detectDbType, resolveIdType } from './adapter/index.js'
|
|
|
10
10
|
// Collection generator plugin
|
|
11
11
|
export { betterAuthCollections } from './adapter/collections.js';
|
|
12
12
|
// Payload plugin and strategy
|
|
13
|
-
export { createBetterAuthPlugin, betterAuthStrategy, resetAuthInstance,
|
|
14
|
-
//
|
|
15
|
-
export {
|
|
13
|
+
export { createBetterAuthPlugin, betterAuthStrategy, resetAuthInstance, getApiKeyPermissionsConfig } from './plugin/index.js';
|
|
14
|
+
// Permission utilities
|
|
15
|
+
export { generateCollectionPermissions } from './utils/generatePermissions.js';
|
|
16
16
|
// Access control utilities
|
|
17
17
|
export { normalizeRoles, hasAnyRole, hasAllRoles, hasAdminRoles, isAdmin, isAdminField, isAdminOrSelf, canUpdateOwnFields, isAuthenticated, isAuthenticatedField, hasRole, hasRoleField, requireAllRoles } from './utils/access.js';
|
|
18
|
-
// API key
|
|
19
|
-
export { extractApiKeyFromRequest,
|
|
18
|
+
// API key permission enforcement utilities
|
|
19
|
+
export { extractApiKeyFromRequest, requirePermission, requireAnyPermission, requireAllPermissions, allowSessionOrPermission, allowSessionOrAnyPermission, requireApiKey } from './utils/apiKeyAccess.js';
|
|
20
20
|
// Auth config detection utility
|
|
21
21
|
export { detectAuthConfig } from './utils/detectAuthConfig.js';
|
|
22
22
|
// Session utilities
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { Plugin, AuthStrategy, BasePayload } from 'payload';
|
|
7
7
|
import type { betterAuth, BetterAuthOptions } from 'better-auth';
|
|
8
|
-
import type {
|
|
8
|
+
import type { ApiKeyPermissionsConfig } from '../types/apiKey.js';
|
|
9
9
|
export type Auth = ReturnType<typeof betterAuth>;
|
|
10
10
|
export type { PayloadWithAuth } from '../types/betterAuth.js';
|
|
11
11
|
export type CreateAuthFunction = (payload: BasePayload) => any;
|
|
@@ -97,11 +97,11 @@ export type BetterAuthPluginAdminOptions = {
|
|
|
97
97
|
passkeys?: string;
|
|
98
98
|
};
|
|
99
99
|
/**
|
|
100
|
-
* API key
|
|
101
|
-
* Controls which
|
|
102
|
-
* When not provided,
|
|
100
|
+
* API key permissions configuration.
|
|
101
|
+
* Controls which permissions are available when creating API keys.
|
|
102
|
+
* When not provided, permissions are auto-generated from Payload collections.
|
|
103
103
|
*/
|
|
104
|
-
apiKey?:
|
|
104
|
+
apiKey?: ApiKeyPermissionsConfig;
|
|
105
105
|
};
|
|
106
106
|
export type BetterAuthPluginOptions = {
|
|
107
107
|
/**
|
|
@@ -133,10 +133,10 @@ export type BetterAuthPluginOptions = {
|
|
|
133
133
|
admin?: BetterAuthPluginAdminOptions;
|
|
134
134
|
};
|
|
135
135
|
/**
|
|
136
|
-
* Get the
|
|
137
|
-
* Used by the ApiKeysView to
|
|
136
|
+
* Get the stored API key permissions config.
|
|
137
|
+
* Used by the ApiKeysView server component to generate permission definitions.
|
|
138
138
|
*/
|
|
139
|
-
export declare function
|
|
139
|
+
export declare function getApiKeyPermissionsConfig(): ApiKeyPermissionsConfig | undefined;
|
|
140
140
|
/**
|
|
141
141
|
* Payload plugin that initializes Better Auth.
|
|
142
142
|
*
|
package/dist/plugin/index.js
CHANGED
|
@@ -4,22 +4,25 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/ import { detectAuthConfig } from '../utils/detectAuthConfig.js';
|
|
6
6
|
import { detectEnabledPlugins } from '../utils/detectEnabledPlugins.js';
|
|
7
|
-
import { buildAvailableScopes, scopesToPermissions } from '../utils/generateScopes.js';
|
|
8
7
|
import { hasAnyRole, normalizeRoles } from '../utils/access.js';
|
|
9
8
|
// Track auth instance for HMR
|
|
10
9
|
let authInstance = null;
|
|
11
|
-
// Store API key
|
|
12
|
-
let
|
|
10
|
+
// Store API key permissions config for access by management views
|
|
11
|
+
let apiKeyPermissionsConfig = undefined;
|
|
13
12
|
/**
|
|
14
|
-
* Get the
|
|
15
|
-
* Used by the ApiKeysView to
|
|
16
|
-
*/ export function
|
|
17
|
-
return
|
|
13
|
+
* Get the stored API key permissions config.
|
|
14
|
+
* Used by the ApiKeysView server component to generate permission definitions.
|
|
15
|
+
*/ export function getApiKeyPermissionsConfig() {
|
|
16
|
+
return apiKeyPermissionsConfig;
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
20
|
-
* Handle API key creation
|
|
21
|
-
*
|
|
22
|
-
|
|
19
|
+
* Handle API key creation server-side.
|
|
20
|
+
* Passes permissions directly from the client to Better Auth's server API.
|
|
21
|
+
*
|
|
22
|
+
* When `organizationId` is provided in the body, it is validated against the
|
|
23
|
+
* user's memberships and stored in the API key's metadata. This allows the
|
|
24
|
+
* `betterAuthStrategy` to resolve organization context for API key requests.
|
|
25
|
+
*/ async function handleApiKeyCreate(authApi, headers, body, payload, membersCollection = 'members') {
|
|
23
26
|
try {
|
|
24
27
|
// Get the current session to find the user
|
|
25
28
|
const session = await authApi.getSession({
|
|
@@ -35,16 +38,51 @@ let apiKeyScopesConfig = undefined;
|
|
|
35
38
|
}
|
|
36
39
|
});
|
|
37
40
|
}
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
// If organizationId is provided, validate that the user is a member
|
|
42
|
+
const organizationId = body.organizationId;
|
|
43
|
+
if (organizationId && payload) {
|
|
44
|
+
try {
|
|
45
|
+
const memberships = await payload.find({
|
|
46
|
+
collection: membersCollection,
|
|
47
|
+
where: {
|
|
48
|
+
and: [
|
|
49
|
+
{
|
|
50
|
+
user: {
|
|
51
|
+
equals: session.user.id
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
organization: {
|
|
56
|
+
equals: organizationId
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
limit: 1,
|
|
62
|
+
depth: 0
|
|
63
|
+
});
|
|
64
|
+
if (memberships.docs.length === 0) {
|
|
65
|
+
return new Response(JSON.stringify({
|
|
66
|
+
error: 'You are not a member of this organization'
|
|
67
|
+
}), {
|
|
68
|
+
status: 403,
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Members collection might not exist — allow creation without org binding
|
|
76
|
+
}
|
|
46
77
|
}
|
|
47
|
-
//
|
|
78
|
+
// Permissions come directly from the client in BA's native format
|
|
79
|
+
const permissions = body.permissions;
|
|
80
|
+
// Merge organizationId into metadata if provided
|
|
81
|
+
const existingMetadata = body.metadata;
|
|
82
|
+
const metadata = organizationId ? {
|
|
83
|
+
...existingMetadata || {},
|
|
84
|
+
organizationId
|
|
85
|
+
} : existingMetadata;
|
|
48
86
|
const createOptions = {
|
|
49
87
|
body: {
|
|
50
88
|
name: body.name,
|
|
@@ -52,10 +90,7 @@ let apiKeyScopesConfig = undefined;
|
|
|
52
90
|
expiresIn: body.expiresIn,
|
|
53
91
|
prefix: body.prefix,
|
|
54
92
|
permissions: permissions && Object.keys(permissions).length > 0 ? permissions : undefined,
|
|
55
|
-
metadata:
|
|
56
|
-
...body.metadata,
|
|
57
|
-
scopes
|
|
58
|
-
} : body.metadata
|
|
93
|
+
metadata: metadata && Object.keys(metadata).length > 0 ? metadata : undefined
|
|
59
94
|
}
|
|
60
95
|
};
|
|
61
96
|
// Call Better Auth's server-side API
|
|
@@ -69,38 +104,13 @@ let apiKeyScopesConfig = undefined;
|
|
|
69
104
|
}
|
|
70
105
|
});
|
|
71
106
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
'Content-Type': 'application/json'
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
} catch (createError) {
|
|
81
|
-
// Check if error is due to metadata being disabled
|
|
82
|
-
const errorMessage = createError instanceof Error ? createError.message : String(createError);
|
|
83
|
-
const isMetadataDisabled = errorMessage.toLowerCase().includes('metadata') && errorMessage.toLowerCase().includes('disabled');
|
|
84
|
-
if (isMetadataDisabled && createOptions.body.metadata) {
|
|
85
|
-
// Retry without metadata - key will still work, just won't show scopes in UI
|
|
86
|
-
console.warn('[better-auth] Metadata disabled, creating API key without scope metadata. Enable metadata with apiKey({ enableMetadata: true }) for better UX.');
|
|
87
|
-
const optionsWithoutMetadata = {
|
|
88
|
-
body: {
|
|
89
|
-
...createOptions.body,
|
|
90
|
-
metadata: undefined
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
const result = await authApi.createApiKey(optionsWithoutMetadata);
|
|
94
|
-
return new Response(JSON.stringify(result), {
|
|
95
|
-
status: 200,
|
|
96
|
-
headers: {
|
|
97
|
-
'Content-Type': 'application/json'
|
|
98
|
-
}
|
|
99
|
-
});
|
|
107
|
+
const result = await authApi.createApiKey(createOptions);
|
|
108
|
+
return new Response(JSON.stringify(result), {
|
|
109
|
+
status: 200,
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'application/json'
|
|
100
112
|
}
|
|
101
|
-
|
|
102
|
-
throw createError;
|
|
103
|
-
}
|
|
113
|
+
});
|
|
104
114
|
} catch (error) {
|
|
105
115
|
console.error('[better-auth] API key creation error:', error);
|
|
106
116
|
const message = error instanceof Error ? error.message : 'Failed to create API key';
|
|
@@ -171,9 +181,10 @@ let apiKeyScopesConfig = undefined;
|
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
|
-
// Guard API key mutation endpoints — require admin role
|
|
184
|
+
// Guard API key mutation and list endpoints — require admin role
|
|
175
185
|
const isApiKeyMutation = req.method === 'POST' && (pathname.endsWith('/api-key/create') || pathname.endsWith('/api-key/update') || pathname.endsWith('/api-key/delete'));
|
|
176
|
-
|
|
186
|
+
const isApiKeyList = req.method === 'GET' && pathname.endsWith('/api-key/list');
|
|
187
|
+
if (isApiKeyMutation || isApiKeyList) {
|
|
177
188
|
const session = await auth.api.getSession({
|
|
178
189
|
headers: req.headers
|
|
179
190
|
});
|
|
@@ -188,7 +199,7 @@ let apiKeyScopesConfig = undefined;
|
|
|
188
199
|
});
|
|
189
200
|
}
|
|
190
201
|
// Resolve required role: apiKey config > login config > default 'admin'
|
|
191
|
-
const requiredRole =
|
|
202
|
+
const requiredRole = apiKeyPermissionsConfig?.requiredRole ?? adminOptions?.login?.requiredRole ?? 'admin';
|
|
192
203
|
if (requiredRole !== null) {
|
|
193
204
|
// Find the auth collection slug from Payload's config
|
|
194
205
|
const authSlug = req.payload.config.collections.find((c)=>typeof c.auth === 'object' || c.auth === true)?.slug ?? 'users';
|
|
@@ -210,11 +221,10 @@ let apiKeyScopesConfig = undefined;
|
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
223
|
}
|
|
213
|
-
// Intercept API key creation requests
|
|
214
|
-
|
|
215
|
-
const isApiKeyCreate = req.method === 'POST' && pathname.endsWith('/api-key/create') && parsedBody?.scopes && Array.isArray(parsedBody.scopes);
|
|
224
|
+
// Intercept API key creation requests to inject userId from session
|
|
225
|
+
const isApiKeyCreate = req.method === 'POST' && pathname.endsWith('/api-key/create');
|
|
216
226
|
if (isApiKeyCreate && parsedBody) {
|
|
217
|
-
return
|
|
227
|
+
return handleApiKeyCreate(auth.api, req.headers, parsedBody, req.payload);
|
|
218
228
|
}
|
|
219
229
|
// Create a new Request for Better Auth
|
|
220
230
|
const request = new Request(url.toString(), {
|
|
@@ -436,8 +446,8 @@ let apiKeyScopesConfig = undefined;
|
|
|
436
446
|
* ```
|
|
437
447
|
*/ export function createBetterAuthPlugin(options) {
|
|
438
448
|
const { createAuth, authBasePath = '/auth', autoRegisterEndpoints = true, autoInjectAdminComponents = true } = options;
|
|
439
|
-
// Store API key
|
|
440
|
-
|
|
449
|
+
// Store API key permissions config for access by management views
|
|
450
|
+
apiKeyPermissionsConfig = options.admin?.apiKey;
|
|
441
451
|
return (incomingConfig)=>{
|
|
442
452
|
// Inject admin components if enabled
|
|
443
453
|
let config = autoInjectAdminComponents ? injectAdminComponents(incomingConfig, options) : incomingConfig;
|
|
@@ -601,6 +611,64 @@ let apiKeyScopesConfig = undefined;
|
|
|
601
611
|
// Members collection might not exist (org plugin not used), silently ignore
|
|
602
612
|
}
|
|
603
613
|
}
|
|
614
|
+
// If activeOrganizationId is not set (common with API key mock sessions),
|
|
615
|
+
// check if the API key has an organizationId in its metadata
|
|
616
|
+
if (!sessionFields.activeOrganizationId) {
|
|
617
|
+
const apiKeyHeader = headers.get('x-api-key') || headers.get('authorization')?.replace('Bearer ', '');
|
|
618
|
+
if (apiKeyHeader) {
|
|
619
|
+
const auth = payloadWithAuth.betterAuth;
|
|
620
|
+
const verifyApiKey = auth.api.verifyApiKey;
|
|
621
|
+
if (typeof verifyApiKey === 'function') {
|
|
622
|
+
try {
|
|
623
|
+
const verifyResult = await verifyApiKey({
|
|
624
|
+
body: {
|
|
625
|
+
key: apiKeyHeader
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
if (verifyResult.valid && verifyResult.key?.metadata) {
|
|
629
|
+
const metadata = typeof verifyResult.key.metadata === 'string' ? JSON.parse(verifyResult.key.metadata) : verifyResult.key.metadata;
|
|
630
|
+
if (metadata.organizationId) {
|
|
631
|
+
// Coerce to number if using serial IDs
|
|
632
|
+
let orgId = metadata.organizationId;
|
|
633
|
+
if (idType === 'number' && typeof orgId === 'string' && /^\d+$/.test(orgId)) {
|
|
634
|
+
orgId = parseInt(orgId, 10);
|
|
635
|
+
}
|
|
636
|
+
// Verify the user is actually a member of this org
|
|
637
|
+
try {
|
|
638
|
+
const memberships = await payload.find({
|
|
639
|
+
collection: membersCollection,
|
|
640
|
+
where: {
|
|
641
|
+
and: [
|
|
642
|
+
{
|
|
643
|
+
user: {
|
|
644
|
+
equals: sessionData.user.id
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
organization: {
|
|
649
|
+
equals: orgId
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
]
|
|
653
|
+
},
|
|
654
|
+
limit: 1,
|
|
655
|
+
depth: 0
|
|
656
|
+
});
|
|
657
|
+
if (memberships.docs.length > 0) {
|
|
658
|
+
sessionFields.activeOrganizationId = orgId;
|
|
659
|
+
organizationRole = memberships.docs[0].role;
|
|
660
|
+
}
|
|
661
|
+
} catch {
|
|
662
|
+
// Members collection might not exist, continue without org context
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
} catch {
|
|
667
|
+
// API key verification failed, continue without org context
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
604
672
|
return {
|
|
605
673
|
user: {
|
|
606
674
|
...users.docs[0],
|
package/dist/types/apiKey.d.ts
CHANGED
|
@@ -1,55 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* API Key
|
|
2
|
+
* API Key Permission Types
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses Better Auth's native permission format: Record<string, string[]>
|
|
5
|
+
* where keys are resource names (collection slugs) and values are action arrays.
|
|
6
|
+
*
|
|
7
|
+
* Convention: two actions per collection — 'read' and 'write'.
|
|
8
|
+
* - read: view records
|
|
9
|
+
* - write: full access (create, update, delete) — implies read
|
|
5
10
|
*/
|
|
6
11
|
/**
|
|
7
|
-
* A
|
|
8
|
-
*
|
|
12
|
+
* A permission definition for the admin UI.
|
|
13
|
+
* Describes a collection's available permission levels.
|
|
9
14
|
*/
|
|
10
|
-
export type
|
|
11
|
-
/**
|
|
15
|
+
export type PermissionDefinition = {
|
|
16
|
+
/** Collection slug (e.g., 'posts') */
|
|
17
|
+
slug: string;
|
|
18
|
+
/** Human-readable label (e.g., 'Posts') */
|
|
12
19
|
label: string;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Permission mapping: { resourceType: ['action1', 'action2'] }
|
|
17
|
-
* Maps to Better Auth's permission format.
|
|
18
|
-
* Use '*' for resource to match all resources.
|
|
19
|
-
* Use '*' in actions array to grant all actions on a resource.
|
|
20
|
-
*/
|
|
21
|
-
permissions: Record<string, string[]>;
|
|
22
|
-
/** If true, only admin users can create keys with this scope */
|
|
23
|
-
adminOnly?: boolean;
|
|
20
|
+
/** Available actions — always ['read', 'write'] for auto-generated */
|
|
21
|
+
actions: string[];
|
|
24
22
|
};
|
|
25
23
|
/**
|
|
26
|
-
* Configuration options for API key
|
|
27
|
-
* Can be used in plugin options to customize available scopes.
|
|
24
|
+
* Configuration options for API key permissions.
|
|
28
25
|
*/
|
|
29
|
-
export type
|
|
30
|
-
/**
|
|
31
|
-
* Custom scope definitions.
|
|
32
|
-
* Key is the scope ID (e.g., 'content:read'), value is the scope definition.
|
|
33
|
-
*/
|
|
34
|
-
scopes?: Record<string, ScopeDefinition>;
|
|
35
|
-
/**
|
|
36
|
-
* Include auto-generated collection scopes.
|
|
37
|
-
* When true (default), generates {collection}:read, {collection}:write, {collection}:delete
|
|
38
|
-
* for each Payload collection.
|
|
39
|
-
* @default true when no custom scopes provided, false when custom scopes provided
|
|
40
|
-
*/
|
|
41
|
-
includeCollectionScopes?: boolean;
|
|
26
|
+
export type ApiKeyPermissionsConfig = {
|
|
42
27
|
/**
|
|
43
|
-
* Collections to exclude from
|
|
44
|
-
*
|
|
45
|
-
* @default ['sessions', 'verifications', 'accounts', 'twoFactors']
|
|
28
|
+
* Collections to exclude from the permissions UI.
|
|
29
|
+
* @default ['sessions', 'verifications', 'accounts', 'twoFactors', 'apiKeys']
|
|
46
30
|
*/
|
|
47
31
|
excludeCollections?: string[];
|
|
48
|
-
/**
|
|
49
|
-
* Default scopes assigned to new API keys when user doesn't select any.
|
|
50
|
-
* If not provided, keys without scopes will have no permissions.
|
|
51
|
-
*/
|
|
52
|
-
defaultScopes?: string[];
|
|
53
32
|
/**
|
|
54
33
|
* Role(s) required to create, update, and delete API keys.
|
|
55
34
|
* - string: Single role required (e.g., 'admin')
|
|
@@ -59,10 +38,3 @@ export type ApiKeyScopesConfig = {
|
|
|
59
38
|
*/
|
|
60
39
|
requiredRole?: string | string[] | null;
|
|
61
40
|
};
|
|
62
|
-
/**
|
|
63
|
-
* Scope data passed to the API keys management client component.
|
|
64
|
-
*/
|
|
65
|
-
export type AvailableScope = ScopeDefinition & {
|
|
66
|
-
/** The scope ID (e.g., 'content:read') */
|
|
67
|
-
id: string;
|
|
68
|
-
};
|
package/dist/types/apiKey.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* API Key
|
|
2
|
+
* API Key Permission Types
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses Better Auth's native permission format: Record<string, string[]>
|
|
5
|
+
* where keys are resource names (collection slugs) and values are action arrays.
|
|
6
|
+
*
|
|
7
|
+
* Convention: two actions per collection — 'read' and 'write'.
|
|
8
|
+
* - read: view records
|
|
9
|
+
* - write: full access (create, update, delete) — implies read
|
|
5
10
|
*/ /**
|
|
6
|
-
* A
|
|
7
|
-
*
|
|
11
|
+
* A permission definition for the admin UI.
|
|
12
|
+
* Describes a collection's available permission levels.
|
|
8
13
|
*/ /**
|
|
9
|
-
*
|
|
14
|
+
* Configuration options for API key permissions.
|
|
10
15
|
*/ export { };
|