@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.
@@ -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 { getApiKeyScopesConfig } from '../../../plugin/index.js';
6
- import { buildAvailableScopes } from '../../../utils/generateScopes.js';
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 available scopes from plugin config and collections
20
- const scopesConfig = getApiKeyScopesConfig();
21
- const availableScopes = buildAvailableScopes(payload.config.collections, scopesConfig);
22
- // Get default scopes from config
23
- const defaultScopes = scopesConfig?.defaultScopes ?? [];
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
- availableScopes: availableScopes,
35
- defaultScopes: defaultScopes
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, getApiKeyScopesConfig, } from './plugin/index.js';
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 { ScopeDefinition, ApiKeyScopesConfig, AvailableScope, } from './types/apiKey.js';
18
- export { generateScopesFromCollections, buildAvailableScopes, scopesToPermissions, } from './utils/generateScopes.js';
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, getApiKeyInfo, hasScope, hasAnyScope as hasAnyScopeKey, hasAllScopes as hasAllScopesKey, requireScope, requireAnyScope, requireAllScopes as requireAllScopesKey, allowSessionOrScope, allowSessionOrAnyScope, validateApiKey, } from './utils/apiKeyAccess.js';
22
- export type { ApiKeyInfo, ApiKeyAccessConfig, } from './utils/apiKeyAccess.js';
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, getApiKeyScopesConfig } from './plugin/index.js';
14
- // Scope utilities
15
- export { generateScopesFromCollections, buildAvailableScopes, scopesToPermissions } from './utils/generateScopes.js';
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 scope enforcement utilities
19
- export { extractApiKeyFromRequest, getApiKeyInfo, hasScope, hasAnyScope as hasAnyScopeKey, hasAllScopes as hasAllScopesKey, requireScope, requireAnyScope, requireAllScopes as requireAllScopesKey, allowSessionOrScope, allowSessionOrAnyScope, validateApiKey } from './utils/apiKeyAccess.js';
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
@@ -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 { ApiKeyScopesConfig } from '../types/apiKey.js';
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 scopes configuration.
101
- * Controls which permission scopes are available when creating API keys.
102
- * When not provided, scopes are auto-generated from Payload collections.
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?: ApiKeyScopesConfig;
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 configured API key scopes config.
137
- * Used by the ApiKeysView to build available scopes.
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 getApiKeyScopesConfig(): ApiKeyScopesConfig | undefined;
139
+ export declare function getApiKeyPermissionsConfig(): ApiKeyPermissionsConfig | undefined;
140
140
  /**
141
141
  * Payload plugin that initializes Better Auth.
142
142
  *
@@ -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 scopes config for access by management views
12
- let apiKeyScopesConfig = undefined;
10
+ // Store API key permissions config for access by management views
11
+ let apiKeyPermissionsConfig = undefined;
13
12
  /**
14
- * Get the configured API key scopes config.
15
- * Used by the ApiKeysView to build available scopes.
16
- */ export function getApiKeyScopesConfig() {
17
- return apiKeyScopesConfig;
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 with scopes server-side.
21
- * Converts scopes to permissions and calls Better Auth's server API.
22
- */ async function handleApiKeyCreateWithScopes(authApi, payload, headers, body) {
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
- // Extract scopes from the request body
39
- const scopes = body.scopes ?? [];
40
- // Build permissions from scopes if any are provided
41
- let permissions;
42
- if (scopes.length > 0) {
43
- const scopesConfig = getApiKeyScopesConfig();
44
- const availableScopes = buildAvailableScopes(payload.config.collections, scopesConfig);
45
- permissions = scopesToPermissions(scopes, availableScopes);
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
- // Build the API key creation options
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: scopes.length > 0 ? {
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
- try {
73
- const result = await authApi.createApiKey(createOptions);
74
- return new Response(JSON.stringify(result), {
75
- status: 200,
76
- headers: {
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
- // Re-throw other errors
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
- if (isApiKeyMutation) {
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 = apiKeyScopesConfig?.requiredRole ?? adminOptions?.login?.requiredRole ?? 'admin';
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 with scopes
214
- // Better Auth's API key create endpoint is POST /api-key/create
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 handleApiKeyCreateWithScopes(auth.api, req.payload, req.headers, parsedBody);
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 scopes config for access by management views
440
- apiKeyScopesConfig = options.admin?.apiKey;
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],
@@ -1,55 +1,34 @@
1
1
  /**
2
- * API Key Scope Types
2
+ * API Key Permission Types
3
3
  *
4
- * Provides typed configuration for API key permission scopes.
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 single permission scope definition.
8
- * Scopes are human-readable permission groups (like GitHub OAuth scopes).
12
+ * A permission definition for the admin UI.
13
+ * Describes a collection's available permission levels.
9
14
  */
10
- export type ScopeDefinition = {
11
- /** Human-readable label for the scope (e.g., "Read Content") */
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
- /** Description of what this scope allows (e.g., "View posts, pages, and comments") */
14
- description: string;
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 scopes.
27
- * Can be used in plugin options to customize available scopes.
24
+ * Configuration options for API key permissions.
28
25
  */
29
- export type ApiKeyScopesConfig = {
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 auto-generated scopes.
44
- * Useful for hiding sensitive collections like 'sessions' or 'verifications'.
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
- };
@@ -1,10 +1,15 @@
1
1
  /**
2
- * API Key Scope Types
2
+ * API Key Permission Types
3
3
  *
4
- * Provides typed configuration for API key permission scopes.
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 single permission scope definition.
7
- * Scopes are human-readable permission groups (like GitHub OAuth scopes).
11
+ * A permission definition for the admin UI.
12
+ * Describes a collection's available permission levels.
8
13
  */ /**
9
- * Scope data passed to the API keys management client component.
14
+ * Configuration options for API key permissions.
10
15
  */ export { };