@payloadcms/figma 0.0.1-alpha.74 → 0.0.1-alpha.75

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.
@@ -0,0 +1,3 @@
1
+ import type { Field, TabAsField } from 'payload';
2
+ export declare function encodePointForWrite(field: Field | TabAsField, value: unknown): unknown;
3
+ export declare function decodePointFromRead(field: Field | TabAsField, value: unknown): unknown;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Payload stores a point as GeoJSON; the v1 server uses a bare `[lng, lat]`
3
+ * tuple. Unwrap on write, re-wrap on read so afterRead can restore the tuple.
4
+ */ function isPlainObject(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ function isCoordinateTuple(value) {
8
+ return Array.isArray(value) && value.length === 2 && value.every((v)=>typeof v === 'number');
9
+ }
10
+ function pointLeafToWire(value) {
11
+ if (isPlainObject(value) && isCoordinateTuple(value.coordinates)) {
12
+ return value.coordinates;
13
+ }
14
+ return value;
15
+ }
16
+ function pointLeafFromWire(value) {
17
+ if (isCoordinateTuple(value)) {
18
+ return {
19
+ type: 'Point',
20
+ coordinates: value
21
+ };
22
+ }
23
+ return value;
24
+ }
25
+ function mapPoint(field, value, mapLeaf) {
26
+ if (field.type !== 'point' || value === null || value === undefined) {
27
+ return value;
28
+ }
29
+ if ('localized' in field && field.localized && isPlainObject(value)) {
30
+ const result = {};
31
+ for (const [locale, localeValue] of Object.entries(value)){
32
+ result[locale] = mapLeaf(localeValue);
33
+ }
34
+ return result;
35
+ }
36
+ return mapLeaf(value);
37
+ }
38
+ export function encodePointForWrite(field, value) {
39
+ return mapPoint(field, value, pointLeafToWire);
40
+ }
41
+ export function decodePointFromRead(field, value) {
42
+ return mapPoint(field, value, pointLeafFromWire);
43
+ }
@@ -0,0 +1,3 @@
1
+ import type { Field, Payload, TabAsField } from 'payload';
2
+ export declare function encodeRichTextForWrite(field: Field | TabAsField, value: unknown): unknown;
3
+ export declare function decodeRichTextFromRead(field: Field | TabAsField, value: unknown, payload: Payload, fieldPath: string, collectionSlug: string): unknown;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The v1 server stores richText as a serialized JSON string, so stringify on
3
+ * write and parse on read. A localized richText is a per-locale map, serialized
4
+ * and parsed per entry (not as one blob).
5
+ */ function isPlainObject(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
8
+ function richTextLeafToWire(value) {
9
+ if (value === null || value === undefined || typeof value === 'string') {
10
+ return value;
11
+ }
12
+ return JSON.stringify(value);
13
+ }
14
+ export function encodeRichTextForWrite(field, value) {
15
+ if (field.type !== 'richText' || value === null || value === undefined) {
16
+ return value;
17
+ }
18
+ if ('localized' in field && field.localized && isPlainObject(value)) {
19
+ const result = {};
20
+ for (const [locale, localeValue] of Object.entries(value)){
21
+ result[locale] = richTextLeafToWire(localeValue);
22
+ }
23
+ return result;
24
+ }
25
+ return richTextLeafToWire(value);
26
+ }
27
+ function richTextLeafFromWire(value, onError) {
28
+ if (typeof value !== 'string') {
29
+ return value;
30
+ }
31
+ try {
32
+ return JSON.parse(value);
33
+ } catch (error) {
34
+ onError(error);
35
+ return value;
36
+ }
37
+ }
38
+ export function decodeRichTextFromRead(field, value, payload, fieldPath, collectionSlug) {
39
+ if (field.type !== 'richText' || value === null || value === undefined) {
40
+ return value;
41
+ }
42
+ const onError = (error)=>{
43
+ payload.logger.warn({
44
+ err: error instanceof Error ? error : new Error(String(error)),
45
+ msg: `Failed to parse richtext field '${fieldPath}' in collection '${collectionSlug}'`
46
+ });
47
+ };
48
+ if ('localized' in field && field.localized && isPlainObject(value)) {
49
+ const result = {};
50
+ for (const [locale, localeValue] of Object.entries(value)){
51
+ result[locale] = richTextLeafFromWire(localeValue, onError);
52
+ }
53
+ return result;
54
+ }
55
+ return richTextLeafFromWire(value, onError);
56
+ }
@@ -0,0 +1,56 @@
1
+ import type { Config } from 'payload';
2
+ /**
3
+ * Wire form of the v1 `DocumentSchema` sent on every request so the server can
4
+ * classify each queried path against the index tables. Paths are
5
+ * period-delimited (e.g. `author.name`).
6
+ */
7
+ export interface DocumentSchemaWire {
8
+ collections: Record<string, WireCollectionSchema>;
9
+ version: 1;
10
+ }
11
+ interface WireCollectionSchema {
12
+ hints: WireHint[];
13
+ name: string;
14
+ paths: Record<string, WireFieldSchema>;
15
+ }
16
+ type WireHint = {
17
+ paths: string[];
18
+ type: 'unique';
19
+ };
20
+ type WireFieldSchema = {
21
+ collection: string | string[];
22
+ on: string;
23
+ type: 'join';
24
+ } | {
25
+ collection: string | string[];
26
+ hasMany: boolean;
27
+ localized: boolean;
28
+ type: 'relationship' | 'upload';
29
+ unique: boolean;
30
+ } | {
31
+ format: 'lexical';
32
+ localized: boolean;
33
+ type: 'richText';
34
+ } | {
35
+ hasMany: boolean;
36
+ localized: boolean;
37
+ type: 'group';
38
+ } | {
39
+ hasMany: boolean;
40
+ localized: boolean;
41
+ type: 'number' | 'select' | 'text';
42
+ unique: boolean;
43
+ } | {
44
+ localized: boolean;
45
+ type: 'array' | 'blocks';
46
+ } | {
47
+ localized: boolean;
48
+ type: 'checkbox' | 'date' | 'json' | 'radio';
49
+ } | {
50
+ localized: boolean;
51
+ type: 'code' | 'email' | 'point' | 'textarea';
52
+ unique: boolean;
53
+ };
54
+ export declare function buildDocumentSchema(config: Config): DocumentSchemaWire;
55
+ export declare function buildVersionDocumentSchema(schema: DocumentSchemaWire, targetCollectionSlug: string): DocumentSchemaWire;
56
+ export {};
@@ -0,0 +1,183 @@
1
+ export function buildDocumentSchema(config) {
2
+ const collections = {};
3
+ const blocksBySlug = new Map();
4
+ for (const block of config.blocks ?? []){
5
+ blocksBySlug.set(block.slug, block);
6
+ }
7
+ for (const collection of config.collections ?? []){
8
+ collections[collection.slug] = buildCollectionSchema(collection.slug, collection.fields, blocksBySlug);
9
+ }
10
+ for (const global of config.globals ?? []){
11
+ const slug = `_global-${global.slug}`;
12
+ collections[slug] = buildCollectionSchema(slug, global.fields, blocksBySlug);
13
+ }
14
+ return {
15
+ collections,
16
+ version: 1
17
+ };
18
+ }
19
+ export function buildVersionDocumentSchema(schema, targetCollectionSlug) {
20
+ const target = schema.collections[targetCollectionSlug];
21
+ if (!target) {
22
+ return schema;
23
+ }
24
+ const prefixedPaths = {};
25
+ for (const [path, fieldSchema] of Object.entries(target.paths)){
26
+ prefixedPaths[`version.${path}`] = fieldSchema;
27
+ }
28
+ prefixedPaths['version._payloadAutosave'] = {
29
+ type: 'checkbox',
30
+ localized: false
31
+ };
32
+ return {
33
+ ...schema,
34
+ collections: {
35
+ ...schema.collections,
36
+ [targetCollectionSlug]: {
37
+ ...target,
38
+ paths: prefixedPaths
39
+ }
40
+ }
41
+ };
42
+ }
43
+ function buildCollectionSchema(name, fields, blocksBySlug) {
44
+ const paths = {};
45
+ collectPaths(fields, '', false, paths, blocksBySlug);
46
+ return {
47
+ name,
48
+ hints: [],
49
+ paths
50
+ };
51
+ }
52
+ function collectPaths(fields, prefix, absorbed, paths, blocksBySlug) {
53
+ for (const field of fields){
54
+ if (field.type === 'row' || field.type === 'collapsible' || field.type === 'group' && (!('name' in field) || !field.name)) {
55
+ collectPaths(field.fields, prefix, absorbed, paths, blocksBySlug);
56
+ continue;
57
+ }
58
+ if (field.type === 'tabs') {
59
+ for (const tab of field.tabs){
60
+ const tabOwnLocalized = 'localized' in tab && tab.localized === true;
61
+ if ('name' in tab && tab.name) {
62
+ paths[`${prefix}${tab.name}`] = {
63
+ type: 'group',
64
+ hasMany: false,
65
+ localized: tabOwnLocalized && !absorbed
66
+ };
67
+ collectPaths(tab.fields, `${prefix}${tab.name}.`, absorbed || tabOwnLocalized, paths, blocksBySlug);
68
+ } else {
69
+ collectPaths(tab.fields, prefix, absorbed, paths, blocksBySlug);
70
+ }
71
+ }
72
+ continue;
73
+ }
74
+ if (field.type === 'ui' || !('name' in field) || !field.name) {
75
+ continue;
76
+ }
77
+ collectNamedField(field, prefix, absorbed, paths, blocksBySlug);
78
+ }
79
+ }
80
+ function collectNamedField(field, prefix, absorbed, paths, blocksBySlug) {
81
+ if (!('name' in field) || !field.name) {
82
+ return;
83
+ }
84
+ const path = `${prefix}${field.name}`;
85
+ const localized = 'localized' in field && field.localized === true && !absorbed;
86
+ const childAbsorbed = absorbed || 'localized' in field && field.localized === true;
87
+ const hasMany = 'hasMany' in field ? Boolean(field.hasMany) : false;
88
+ const unique = 'unique' in field ? Boolean(field.unique) : false;
89
+ switch(field.type){
90
+ case 'array':
91
+ paths[path] = {
92
+ type: 'array',
93
+ localized
94
+ };
95
+ collectPaths(field.fields, `${path}.`, childAbsorbed, paths, blocksBySlug);
96
+ return;
97
+ case 'blocks':
98
+ {
99
+ paths[path] = {
100
+ type: 'blocks',
101
+ localized
102
+ };
103
+ paths[`${path}.blockType`] = {
104
+ type: 'text',
105
+ hasMany: false,
106
+ localized: false,
107
+ unique: false
108
+ };
109
+ const blockFields = [];
110
+ for (const block of field.blocks){
111
+ const resolved = typeof block === 'string' ? blocksBySlug.get(block) : block;
112
+ if (resolved) {
113
+ blockFields.push(...resolved.fields);
114
+ }
115
+ }
116
+ collectPaths(blockFields, `${path}.`, childAbsorbed, paths, blocksBySlug);
117
+ return;
118
+ }
119
+ case 'checkbox':
120
+ case 'date':
121
+ case 'json':
122
+ case 'radio':
123
+ paths[path] = {
124
+ type: field.type,
125
+ localized
126
+ };
127
+ return;
128
+ case 'code':
129
+ case 'email':
130
+ case 'point':
131
+ case 'textarea':
132
+ paths[path] = {
133
+ type: field.type,
134
+ localized,
135
+ unique
136
+ };
137
+ return;
138
+ case 'group':
139
+ paths[path] = {
140
+ type: 'group',
141
+ hasMany,
142
+ localized
143
+ };
144
+ collectPaths(field.fields, `${path}.`, childAbsorbed, paths, blocksBySlug);
145
+ return;
146
+ case 'join':
147
+ paths[path] = {
148
+ type: 'join',
149
+ collection: field.collection,
150
+ on: field.on
151
+ };
152
+ return;
153
+ case 'number':
154
+ case 'select':
155
+ case 'text':
156
+ paths[path] = {
157
+ type: field.type,
158
+ hasMany,
159
+ localized,
160
+ unique
161
+ };
162
+ return;
163
+ case 'relationship':
164
+ case 'upload':
165
+ paths[path] = {
166
+ type: field.type,
167
+ collection: field.relationTo,
168
+ hasMany,
169
+ localized,
170
+ unique
171
+ };
172
+ return;
173
+ case 'richText':
174
+ paths[path] = {
175
+ type: 'richText',
176
+ format: 'lexical',
177
+ localized
178
+ };
179
+ return;
180
+ default:
181
+ return;
182
+ }
183
+ }
@@ -138,6 +138,14 @@ export const defaultVerify = ({ collection, strategyName, userInfoCookieName = D
138
138
  user.collection = collection.slug;
139
139
  user._strategy = strategyName;
140
140
  user.exp = token.exp;
141
+ // userGroups/makePermission are virtual (never persisted). Surface the live
142
+ // JWT claims on the authenticated user for this request so access control
143
+ // and self-reads see current values. Claims are snake_case on the wire; the
144
+ // collection fields are camelCase. The field afterRead hooks
145
+ // (see build-config.ts) read these back off req.user.
146
+ const userWithFigmaClaims = user;
147
+ userWithFigmaClaims.userGroups = Array.isArray(token.user_groups) ? token.user_groups : undefined;
148
+ userWithFigmaClaims.makePermission = token.make_permission === 'edit' || token.make_permission === 'view' ? token.make_permission : undefined;
141
149
  return {
142
150
  responseHeaders,
143
151
  user
@@ -163,9 +163,8 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
163
163
  strategy
164
164
  });
165
165
  debugLogger.info({
166
- msg: 'Token Response',
167
- path,
168
- tokenRes
166
+ msg: 'Received token response',
167
+ path
169
168
  });
170
169
  const { access_token, error, error_description, expires_in: refreshTokenExpires } = tokenRes;
171
170
  if (error) {
@@ -180,8 +179,7 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
180
179
  const errMsg = 'No access_token was returned from the identity provider.';
181
180
  req.payload.logger.error({
182
181
  msg: errMsg,
183
- path,
184
- tokenRes
182
+ path
185
183
  });
186
184
  throw new APIError(errMsg);
187
185
  }
@@ -206,8 +204,8 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
206
204
  accessToken: access_token,
207
205
  collection,
208
206
  collectionOptions,
209
- contentSystemId: config.custom.figma.contentSystemId,
210
207
  debugLogger,
208
+ environmentId: config.custom.figma.environmentId,
211
209
  pluginOptions,
212
210
  refreshTokenExpiresIn: refreshTokenExpires,
213
211
  req,
@@ -1,6 +1,6 @@
1
1
  import * as jose from 'jose';
2
2
  import { APIError, parseCookies } from 'payload';
3
- import { JWTValidationError, validateProjectToken } from '../../auth/jwt-validator.js';
3
+ import { JWTValidationError, validatePayloadAdminToken } from '../../auth/jwt-validator.js';
4
4
  import { getInfraEnvironment } from '../../constants.js';
5
5
  import { createDebugLogger } from '../utilities/createDebugLogger.js';
6
6
  import { getTokenExp } from '../utilities/getTokenExp.js';
@@ -45,16 +45,15 @@ export class Strategy {
45
45
  let oauthToken = cookies.get(this.cookieName);
46
46
  const refreshToken = cookies.get(`${this.cookieName}-refresh`);
47
47
  debugLogger.info({
48
- msg: 'Retrieved tokens from cookies',
49
- oauthToken,
50
- refreshToken
48
+ hasOauthToken: !!oauthToken,
49
+ hasRefreshToken: !!refreshToken,
50
+ msg: 'Retrieved tokens from cookies'
51
51
  });
52
52
  if (!this.collectionOptions.disableJWTFromHeader) {
53
53
  // Parse Authorization header if exists
54
54
  const jwtFromHeader = headers.get('authorization');
55
55
  if (typeof jwtFromHeader === 'string' && jwtFromHeader.startsWith('Bearer ')) {
56
56
  debugLogger.info({
57
- jwtFromHeader,
58
57
  msg: 'Found bearer token in authorization header'
59
58
  });
60
59
  oauthToken = jwtFromHeader.replace('Bearer ', '');
@@ -67,8 +66,7 @@ export class Strategy {
67
66
  // full page load when canSetHeaders=true.
68
67
  if (refreshToken) {
69
68
  debugLogger.info({
70
- msg: 'No access token found, but refresh token exists. Attempting to refresh...',
71
- refreshToken
69
+ msg: 'No access token found, but refresh token exists. Attempting to refresh...'
72
70
  });
73
71
  const refreshed = await refreshTokens({
74
72
  payload,
@@ -103,9 +101,7 @@ export class Strategy {
103
101
  };
104
102
  } else {
105
103
  debugLogger.info({
106
- msg: 'Token refresh failed',
107
- refreshed,
108
- refreshToken
104
+ msg: 'Token refresh failed'
109
105
  });
110
106
  }
111
107
  }
@@ -156,9 +152,7 @@ export class Strategy {
156
152
  strategy: this
157
153
  });
158
154
  debugLogger.info({
159
- msg: 'Token expired. Refreshing...',
160
- refreshed,
161
- refreshToken
155
+ msg: 'Token expired. Refreshing...'
162
156
  });
163
157
  if (refreshed) {
164
158
  const verifyResult = await this.verify({
@@ -185,9 +179,7 @@ export class Strategy {
185
179
  };
186
180
  } else {
187
181
  debugLogger.info({
188
- msg: 'Token refresh failed',
189
- refreshed,
190
- refreshToken
182
+ msg: 'Token refresh failed'
191
183
  });
192
184
  }
193
185
  }
@@ -282,11 +274,11 @@ export class Strategy {
282
274
  }
283
275
  async jwtVerify({ payload, token }) {
284
276
  try {
285
- const audience = payload.config.custom.figma.contentSystemId;
277
+ const audience = payload.config.custom.figma.environmentId;
286
278
  if (!audience) {
287
- throw new Error('Cannot validate token audience: contentSystemId is not configured');
279
+ throw new Error('Cannot validate token audience: environmentId is not configured');
288
280
  }
289
- const verified = await validateProjectToken({
281
+ const verified = await validatePayloadAdminToken({
290
282
  audience,
291
283
  environment: getInfraEnvironment(),
292
284
  token
@@ -5,8 +5,8 @@ interface Args {
5
5
  accessToken: string;
6
6
  collection: CollectionConfig;
7
7
  collectionOptions: CollectionOptions;
8
- contentSystemId: string;
9
8
  debugLogger: DebugLogger;
9
+ environmentId: string;
10
10
  pluginOptions: PluginOptions;
11
11
  /** From the IdP token response (`expires_in`); used as a refresh-cookie expiry fallback. */
12
12
  refreshTokenExpiresIn?: number;
@@ -15,8 +15,8 @@ interface Args {
15
15
  }
16
16
  /**
17
17
  * Given a valid OAuth access_token: fetch user info (non-fatal), mint a
18
- * project token, and write the main + refresh + user-info cookies to
19
- * `req.responseHeaders`. Throws `APIError` if the project token mint fails.
18
+ * Payload Admin token, and write the main + refresh + user-info cookies to
19
+ * `req.responseHeaders`. Throws `APIError` if the Admin token mint fails.
20
20
  */
21
- export declare const establishSession: ({ accessToken, collection, collectionOptions, contentSystemId, debugLogger, pluginOptions, refreshTokenExpiresIn, req, strategy, }: Args) => Promise<void>;
21
+ export declare const establishSession: ({ accessToken, collection, collectionOptions, debugLogger, environmentId, pluginOptions, refreshTokenExpiresIn, req, strategy, }: Args) => Promise<void>;
22
22
  export {};
@@ -1,13 +1,13 @@
1
1
  import { APIError, generateCookie } from 'payload';
2
- import { getProjectToken, getUserInfo } from '../../api/figma-api.js';
2
+ import { getPayloadAdminToken, getUserInfo } from '../../api/figma-api.js';
3
3
  import { createCookieOptions } from './createCookieOptions.js';
4
4
  import { getCookieExpiration } from './getCookieExpiration.js';
5
5
  import { withPartitionedCookie } from './withPartitionedCookie.js';
6
6
  /**
7
7
  * Given a valid OAuth access_token: fetch user info (non-fatal), mint a
8
- * project token, and write the main + refresh + user-info cookies to
9
- * `req.responseHeaders`. Throws `APIError` if the project token mint fails.
10
- */ export const establishSession = async ({ accessToken, collection, collectionOptions, contentSystemId, debugLogger, pluginOptions, refreshTokenExpiresIn, req, strategy })=>{
8
+ * Payload Admin token, and write the main + refresh + user-info cookies to
9
+ * `req.responseHeaders`. Throws `APIError` if the Admin token mint fails.
10
+ */ export const establishSession = async ({ accessToken, collection, collectionOptions, debugLogger, environmentId, pluginOptions, refreshTokenExpiresIn, req, strategy })=>{
11
11
  const oauthCredential = {
12
12
  type: 'oauth',
13
13
  token: accessToken
@@ -17,7 +17,6 @@ import { withPartitionedCookie } from './withPartitionedCookie.js';
17
17
  try {
18
18
  figmaUserInfo = await getUserInfo(oauthCredential);
19
19
  debugLogger.info({
20
- figmaUserInfo,
21
20
  msg: 'Fetched Figma user info'
22
21
  });
23
22
  } catch (err) {
@@ -26,9 +25,9 @@ import { withPartitionedCookie } from './withPartitionedCookie.js';
26
25
  msg: 'Failed to fetch Figma user info'
27
26
  });
28
27
  }
29
- const projectToken = await getProjectToken(oauthCredential, contentSystemId);
30
- if (!projectToken.token) {
31
- const errMsg = 'No project token returned from Figma API';
28
+ const adminToken = await getPayloadAdminToken(oauthCredential, environmentId);
29
+ if (!adminToken.token) {
30
+ const errMsg = 'No Payload Admin token returned from Figma API';
32
31
  req.payload.logger.error({
33
32
  msg: errMsg
34
33
  });
@@ -54,13 +53,13 @@ import { withPartitionedCookie } from './withPartitionedCookie.js';
54
53
  });
55
54
  req.responseHeaders.append('Set-Cookie', withPartitionedCookie(refreshCookie));
56
55
  }
57
- const tokenExpires = cookieOptions?.expires ? getCookieExpiration(cookieOptions.expires) : new Date(projectToken.expiresAt);
56
+ const tokenExpires = cookieOptions?.expires ? getCookieExpiration(cookieOptions.expires) : new Date(adminToken.expiresAt);
58
57
  const tokenCookie = generateCookie({
59
58
  ...cookieOptions,
60
59
  name: strategy.cookieName,
61
60
  expires: tokenExpires,
62
61
  returnCookieAsObject: false,
63
- value: projectToken.token
62
+ value: adminToken.token
64
63
  });
65
64
  req.responseHeaders.append('Set-Cookie', withPartitionedCookie(tokenCookie));
66
65
  // Short-lived handoff cookie consumed by defaultVerify on the next request.
@@ -1,6 +1,6 @@
1
1
  import { generateCookie } from 'payload';
2
- import { getProjectToken } from '../../api/figma-api.js';
3
- import { validateProjectToken } from '../../auth/jwt-validator.js';
2
+ import { getPayloadAdminToken } from '../../api/figma-api.js';
3
+ import { validatePayloadAdminToken } from '../../auth/jwt-validator.js';
4
4
  import { getInfraEnvironment } from '../../constants.js';
5
5
  import { createCookieOptions } from './createCookieOptions.js';
6
6
  import { createDebugLogger } from './createDebugLogger.js';
@@ -16,15 +16,18 @@ export const refreshTokens = async ({ payload, refreshToken, strategy })=>{
16
16
  // but refresh token is still valid
17
17
  // so we will refresh both tokens
18
18
  try {
19
- // Get another project token
20
- const { expiresAt, token } = await getProjectToken({
19
+ // Get another Payload Admin token.
20
+ const environmentId = payload.config.custom.figma.environmentId;
21
+ if (!environmentId) {
22
+ throw new Error('Cannot refresh Payload Admin token: environmentId is not configured');
23
+ }
24
+ const { expiresAt, token } = await getPayloadAdminToken({
21
25
  type: 'oauth',
22
26
  token: refreshToken
23
- }, payload.config.custom.figma.contentSystemId);
27
+ }, environmentId);
24
28
  debugLogger.info({
25
29
  expiresAt,
26
- msg: 'Received new tokens from Figma API',
27
- token
30
+ msg: 'Received new Payload Admin token from Figma API'
28
31
  });
29
32
  // On error, no user
30
33
  if (!token) {
@@ -35,8 +38,7 @@ export const refreshTokens = async ({ payload, refreshToken, strategy })=>{
35
38
  }
36
39
  if (token) {
37
40
  debugLogger.info({
38
- msg: 'Successfully refreshed project token',
39
- token
41
+ msg: 'Successfully refreshed Payload Admin token'
40
42
  });
41
43
  const expires_in = new Date(expiresAt);
42
44
  const cookieOptions = createCookieOptions({
@@ -52,12 +54,8 @@ export const refreshTokens = async ({ payload, refreshToken, strategy })=>{
52
54
  value: token
53
55
  });
54
56
  headers.append('Set-Cookie', withPartitionedCookie(tokenCookie));
55
- const audience = payload.config.custom.figma.contentSystemId;
56
- if (!audience) {
57
- throw new Error('Cannot validate token audience: contentSystemId is not configured');
58
- }
59
- const decodedOauthToken = await validateProjectToken({
60
- audience,
57
+ const decodedOauthToken = await validatePayloadAdminToken({
58
+ audience: environmentId,
61
59
  environment: getInfraEnvironment(),
62
60
  token
63
61
  });
@@ -47,6 +47,7 @@ export type FigmaConfig = {
47
47
  } & {
48
48
  figma: {
49
49
  contentSystemId?: string;
50
+ environmentId?: string;
50
51
  storage?: boolean;
51
52
  useContentSystem?: boolean;
52
53
  };