@payloadcms/figma 0.0.1-alpha.45 → 0.0.1-alpha.47
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/api/figma-api.d.ts +20 -0
- package/dist/api/figma-api.d.ts.map +1 -1
- package/dist/api/figma-api.js +35 -1
- package/dist/api/figma-api.js.map +1 -1
- package/dist/commands/debug.js +1 -1
- package/dist/commands/debug.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/db-content-api/index.js +1 -1
- package/dist/db-content-api/index.js.map +1 -1
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts +2 -17
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts.map +1 -1
- package/dist/db-content-api/utilities/meta/buildMeta.js +1 -4
- package/dist/db-content-api/utilities/meta/buildMeta.js.map +1 -1
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts +2 -8
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts.map +1 -1
- package/dist/db-content-api/utilities/meta/buildPathTypes.js +11 -41
- package/dist/db-content-api/utilities/meta/buildPathTypes.js.map +1 -1
- package/dist/db-content-api/utilities/meta/index.d.ts +5 -0
- package/dist/db-content-api/utilities/meta/index.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/index.js +5 -0
- package/dist/db-content-api/utilities/meta/index.js.map +1 -0
- package/dist/endpoints/schema.d.ts +3 -0
- package/dist/endpoints/schema.d.ts.map +1 -0
- package/dist/endpoints/schema.js +29 -0
- package/dist/endpoints/schema.js.map +1 -0
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +2 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/oauth/components/FigmaAvatar/index.d.ts +4 -0
- package/dist/oauth/components/FigmaAvatar/index.d.ts.map +1 -0
- package/dist/oauth/components/FigmaAvatar/index.js +27 -0
- package/dist/oauth/components/FigmaAvatar/index.js.map +1 -0
- package/dist/oauth/components/FigmaAvatar/index.scss +12 -0
- package/dist/oauth/components/FigmaAvatarCell/index.d.ts +5 -0
- package/dist/oauth/components/FigmaAvatarCell/index.d.ts.map +1 -0
- package/dist/oauth/components/FigmaAvatarCell/index.js +25 -0
- package/dist/oauth/components/FigmaAvatarCell/index.js.map +1 -0
- package/dist/oauth/components/FigmaAvatarCell/index.scss +5 -0
- package/dist/oauth/components/FigmaAvatarPlaceholder/index.d.ts +9 -0
- package/dist/oauth/components/FigmaAvatarPlaceholder/index.d.ts.map +1 -0
- package/dist/oauth/components/FigmaAvatarPlaceholder/index.js +37 -0
- package/dist/oauth/components/FigmaAvatarPlaceholder/index.js.map +1 -0
- package/dist/oauth/components/FigmaAvatarPlaceholder/index.scss +28 -0
- package/dist/oauth/defaults.d.ts.map +1 -1
- package/dist/oauth/defaults.js +38 -5
- package/dist/oauth/defaults.js.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js +31 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
- package/dist/plugin/build-config.d.ts +3 -3
- package/dist/plugin/build-config.d.ts.map +1 -1
- package/dist/plugin/build-config.js +48 -4
- package/dist/plugin/build-config.js.map +1 -1
- package/dist/templates/.env.example +1 -1
- package/dist/utils/payload-config-ast.js +5 -5
- package/dist/utils/payload-config-ast.js.map +1 -1
- package/dist/utils/project.js +1 -1
- package/dist/utils/project.js.map +1 -1
- package/package.json +1 -1
- package/dist/db-content-api/temp-utilities/types.d.ts +0 -22
- package/dist/db-content-api/temp-utilities/types.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/types.js +0 -15
- package/dist/db-content-api/temp-utilities/types.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/oauth/components/FigmaAvatar/index.tsx"],"sourcesContent":["'use client'\nimport type React from 'react'\n\nimport { useAuth } from '@payloadcms/ui'\nimport { useState } from 'react'\n\nimport { FigmaAvatarPlaceholder, isValidImageUrl } from '../FigmaAvatarPlaceholder/index.js'\nimport './index.scss'\n\nconst baseClass = 'figma-avatar'\n\ntype UserWithFigmaFields = {\n figmaImageUrl?: string\n}\n\nexport const FigmaAvatar: React.FC = () => {\n const { user } = useAuth()\n const [hasError, setHasError] = useState(false)\n\n const figmaImageUrl = (user as UserWithFigmaFields)?.figmaImageUrl\n\n if (!figmaImageUrl || hasError || !isValidImageUrl(figmaImageUrl)) {\n return <FigmaAvatarPlaceholder size={25} />\n }\n\n return (\n <img\n alt=\"Profile\"\n className={`${baseClass}__image`}\n height={25}\n onError={() => setHasError(true)}\n src={figmaImageUrl}\n width={25}\n />\n )\n}\n"],"names":["useAuth","useState","FigmaAvatarPlaceholder","isValidImageUrl","baseClass","FigmaAvatar","user","hasError","setHasError","figmaImageUrl","size","img","alt","className","height","onError","src","width"],"mappings":"AAAA;;AAGA,SAASA,OAAO,QAAQ,iBAAgB;AACxC,SAASC,QAAQ,QAAQ,QAAO;AAEhC,SAASC,sBAAsB,EAAEC,eAAe,QAAQ,qCAAoC;AAC5F,OAAO,eAAc;AAErB,MAAMC,YAAY;AAMlB,OAAO,MAAMC,cAAwB;IACnC,MAAM,EAAEC,IAAI,EAAE,GAAGN;IACjB,MAAM,CAACO,UAAUC,YAAY,GAAGP,SAAS;IAEzC,MAAMQ,gBAAiBH,MAA8BG;IAErD,IAAI,CAACA,iBAAiBF,YAAY,CAACJ,gBAAgBM,gBAAgB;QACjE,qBAAO,KAACP;YAAuBQ,MAAM;;IACvC;IAEA,qBACE,KAACC;QACCC,KAAI;QACJC,WAAW,GAAGT,UAAU,OAAO,CAAC;QAChCU,QAAQ;QACRC,SAAS,IAAMP,YAAY;QAC3BQ,KAAKP;QACLQ,OAAO;;AAGb,EAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/oauth/components/FigmaAvatarCell/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AACxD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,cAAc,CAAA;AAIrB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAkB/D,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { FigmaAvatarPlaceholder, isValidImageUrl } from '../FigmaAvatarPlaceholder/index.js';
|
|
5
|
+
import './index.scss';
|
|
6
|
+
const baseClass = 'figma-avatar-cell';
|
|
7
|
+
export const FigmaAvatarCell = ({ cellData })=>{
|
|
8
|
+
const [hasError, setHasError] = useState(false);
|
|
9
|
+
const figmaImageUrl = cellData;
|
|
10
|
+
if (!figmaImageUrl || hasError || !isValidImageUrl(figmaImageUrl)) {
|
|
11
|
+
return /*#__PURE__*/ _jsx(FigmaAvatarPlaceholder, {
|
|
12
|
+
size: 20
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return /*#__PURE__*/ _jsx("img", {
|
|
16
|
+
alt: "Avatar",
|
|
17
|
+
className: `${baseClass}__image`,
|
|
18
|
+
height: 20,
|
|
19
|
+
onError: ()=>setHasError(true),
|
|
20
|
+
src: figmaImageUrl,
|
|
21
|
+
width: 20
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/oauth/components/FigmaAvatarCell/index.tsx"],"sourcesContent":["'use client'\nimport type { DefaultCellComponentProps } from 'payload'\nimport type React from 'react'\n\nimport { useState } from 'react'\n\nimport { FigmaAvatarPlaceholder, isValidImageUrl } from '../FigmaAvatarPlaceholder/index.js'\nimport './index.scss'\n\nconst baseClass = 'figma-avatar-cell'\n\nexport const FigmaAvatarCell: React.FC<DefaultCellComponentProps> = ({ cellData }) => {\n const [hasError, setHasError] = useState(false)\n const figmaImageUrl = cellData as string | undefined\n\n if (!figmaImageUrl || hasError || !isValidImageUrl(figmaImageUrl)) {\n return <FigmaAvatarPlaceholder size={20} />\n }\n\n return (\n <img\n alt=\"Avatar\"\n className={`${baseClass}__image`}\n height={20}\n onError={() => setHasError(true)}\n src={figmaImageUrl}\n width={20}\n />\n )\n}\n"],"names":["useState","FigmaAvatarPlaceholder","isValidImageUrl","baseClass","FigmaAvatarCell","cellData","hasError","setHasError","figmaImageUrl","size","img","alt","className","height","onError","src","width"],"mappings":"AAAA;;AAIA,SAASA,QAAQ,QAAQ,QAAO;AAEhC,SAASC,sBAAsB,EAAEC,eAAe,QAAQ,qCAAoC;AAC5F,OAAO,eAAc;AAErB,MAAMC,YAAY;AAElB,OAAO,MAAMC,kBAAuD,CAAC,EAAEC,QAAQ,EAAE;IAC/E,MAAM,CAACC,UAAUC,YAAY,GAAGP,SAAS;IACzC,MAAMQ,gBAAgBH;IAEtB,IAAI,CAACG,iBAAiBF,YAAY,CAACJ,gBAAgBM,gBAAgB;QACjE,qBAAO,KAACP;YAAuBQ,MAAM;;IACvC;IAEA,qBACE,KAACC;QACCC,KAAI;QACJC,WAAW,GAAGT,UAAU,OAAO,CAAC;QAChCU,QAAQ;QACRC,SAAS,IAAMP,YAAY;QAC3BQ,KAAKP;QACLQ,OAAO;;AAGb,EAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import './index.scss';
|
|
3
|
+
type FigmaAvatarPlaceholderProps = {
|
|
4
|
+
size?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const FigmaAvatarPlaceholder: React.FC<FigmaAvatarPlaceholderProps>;
|
|
7
|
+
export declare function isValidImageUrl(url: string): boolean;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/oauth/components/FigmaAvatarPlaceholder/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,cAAc,CAAA;AAIrB,KAAK,2BAA2B,GAAG;IACjC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAMxE,CAAA;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOpD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import './index.scss';
|
|
3
|
+
const baseClass = 'figma-avatar-placeholder';
|
|
4
|
+
export const FigmaAvatarPlaceholder = ({ size = 25 })=>/*#__PURE__*/ _jsxs("svg", {
|
|
5
|
+
className: baseClass,
|
|
6
|
+
height: size,
|
|
7
|
+
viewBox: "0 0 25 25",
|
|
8
|
+
width: size,
|
|
9
|
+
children: [
|
|
10
|
+
/*#__PURE__*/ _jsx("circle", {
|
|
11
|
+
className: `${baseClass}__bg`,
|
|
12
|
+
cx: "12.5",
|
|
13
|
+
cy: "12.5",
|
|
14
|
+
r: "11.5"
|
|
15
|
+
}),
|
|
16
|
+
/*#__PURE__*/ _jsx("circle", {
|
|
17
|
+
className: `${baseClass}__head`,
|
|
18
|
+
cx: "12.5",
|
|
19
|
+
cy: "10",
|
|
20
|
+
r: "4"
|
|
21
|
+
}),
|
|
22
|
+
/*#__PURE__*/ _jsx("path", {
|
|
23
|
+
className: `${baseClass}__body`,
|
|
24
|
+
d: "M12.5 15c-4 0-7 2-7 4.5v1h14v-1c0-2.5-3-4.5-7-4.5z"
|
|
25
|
+
})
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
export function isValidImageUrl(url) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = new URL(url);
|
|
31
|
+
return parsed.protocol === 'https:' || parsed.protocol === 'http:';
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/oauth/components/FigmaAvatarPlaceholder/index.tsx"],"sourcesContent":["import type React from 'react'\n\nimport './index.scss'\n\nconst baseClass = 'figma-avatar-placeholder'\n\ntype FigmaAvatarPlaceholderProps = {\n size?: number\n}\n\nexport const FigmaAvatarPlaceholder: React.FC<FigmaAvatarPlaceholderProps> = ({ size = 25 }) => (\n <svg className={baseClass} height={size} viewBox=\"0 0 25 25\" width={size}>\n <circle className={`${baseClass}__bg`} cx=\"12.5\" cy=\"12.5\" r=\"11.5\" />\n <circle className={`${baseClass}__head`} cx=\"12.5\" cy=\"10\" r=\"4\" />\n <path className={`${baseClass}__body`} d=\"M12.5 15c-4 0-7 2-7 4.5v1h14v-1c0-2.5-3-4.5-7-4.5z\" />\n </svg>\n)\n\nexport function isValidImageUrl(url: string): boolean {\n try {\n const parsed = new URL(url)\n return parsed.protocol === 'https:' || parsed.protocol === 'http:'\n } catch {\n return false\n }\n}\n"],"names":["baseClass","FigmaAvatarPlaceholder","size","svg","className","height","viewBox","width","circle","cx","cy","r","path","d","isValidImageUrl","url","parsed","URL","protocol"],"mappings":";AAEA,OAAO,eAAc;AAErB,MAAMA,YAAY;AAMlB,OAAO,MAAMC,yBAAgE,CAAC,EAAEC,OAAO,EAAE,EAAE,iBACzF,MAACC;QAAIC,WAAWJ;QAAWK,QAAQH;QAAMI,SAAQ;QAAYC,OAAOL;;0BAClE,KAACM;gBAAOJ,WAAW,GAAGJ,UAAU,IAAI,CAAC;gBAAES,IAAG;gBAAOC,IAAG;gBAAOC,GAAE;;0BAC7D,KAACH;gBAAOJ,WAAW,GAAGJ,UAAU,MAAM,CAAC;gBAAES,IAAG;gBAAOC,IAAG;gBAAKC,GAAE;;0BAC7D,KAACC;gBAAKR,WAAW,GAAGJ,UAAU,MAAM,CAAC;gBAAEa,GAAE;;;OAE5C;AAED,OAAO,SAASC,gBAAgBC,GAAW;IACzC,IAAI;QACF,MAAMC,SAAS,IAAIC,IAAIF;QACvB,OAAOC,OAAOE,QAAQ,KAAK,YAAYF,OAAOE,QAAQ,KAAK;IAC7D,EAAE,OAAM;QACN,OAAO;IACT;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@layer payload-default {
|
|
2
|
+
.figma-avatar-placeholder {
|
|
3
|
+
&__bg {
|
|
4
|
+
fill: var(--theme-elevation-50);
|
|
5
|
+
stroke: var(--theme-elevation-200);
|
|
6
|
+
stroke-width: 1px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&__head,
|
|
10
|
+
&__body {
|
|
11
|
+
fill: var(--theme-elevation-200);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&:hover {
|
|
15
|
+
.figma-avatar-placeholder {
|
|
16
|
+
&__bg {
|
|
17
|
+
fill: var(--theme-elevation-200);
|
|
18
|
+
stroke: var(--theme-elevation-600);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&__head,
|
|
22
|
+
&__body {
|
|
23
|
+
fill: var(--theme-elevation-600);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/oauth/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/oauth/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAa,MAAM,SAAS,CAAA;AAKjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AA4BhD,eAAO,MAAM,YAAY,UAAiC,CAAA;AAE1D,eAAO,MAAM,oBAAoB,EAAE,SAQlC,CAAA;AAED,eAAO,MAAM,aAAa,iDAKrB;IACD,UAAU,EAAE,gBAAgB,CAAA;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,UAAU,GAAG,SAAS,CAAA;CACtC,KAAG,cAkIH,CAAA"}
|
package/dist/oauth/defaults.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
import { parseCookies } from 'payload';
|
|
1
2
|
import { v4 as uuid } from 'uuid';
|
|
2
3
|
import { hasUserTokenPropsChanged } from './utilities/hasUserTokenPropsChanged.js';
|
|
3
4
|
import { isDuplicateKeyError } from './utilities/isDuplicateKeyError.js';
|
|
5
|
+
function getFigmaUserInfo(headers) {
|
|
6
|
+
try {
|
|
7
|
+
const cookies = parseCookies(headers);
|
|
8
|
+
const encoded = cookies.get('figma-user-info');
|
|
9
|
+
if (!encoded) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return JSON.parse(Buffer.from(encoded, 'base64').toString('utf-8'));
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
export const defaultScope = [
|
|
5
18
|
'openid',
|
|
6
19
|
'profile',
|
|
@@ -15,7 +28,7 @@ export const defaultUsernameField = {
|
|
|
15
28
|
},
|
|
16
29
|
unique: true
|
|
17
30
|
};
|
|
18
|
-
export const defaultVerify = ({ collection, strategyName, usernameField })=>async ({ payload, token })=>{
|
|
31
|
+
export const defaultVerify = ({ collection, strategyName, usernameField })=>async ({ headers, payload, token })=>{
|
|
19
32
|
let tokenUsername = 'preferred_username';
|
|
20
33
|
if (usernameField.name !== 'preferredUsername') {
|
|
21
34
|
tokenUsername = usernameField.name;
|
|
@@ -25,6 +38,13 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
25
38
|
user: null
|
|
26
39
|
};
|
|
27
40
|
}
|
|
41
|
+
const figmaUserInfo = getFigmaUserInfo(headers);
|
|
42
|
+
let responseHeaders;
|
|
43
|
+
// Clear the cookie after reading
|
|
44
|
+
if (figmaUserInfo) {
|
|
45
|
+
responseHeaders = new Headers();
|
|
46
|
+
responseHeaders.append('Set-Cookie', 'figma-user-info=; Max-Age=0; Path=/;');
|
|
47
|
+
}
|
|
28
48
|
let user = null;
|
|
29
49
|
const depth = typeof collection.auth === 'object' ? collection.auth.depth : undefined;
|
|
30
50
|
try {
|
|
@@ -41,13 +61,19 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
41
61
|
});
|
|
42
62
|
if (results.docs.length > 0) {
|
|
43
63
|
user = results.docs[0];
|
|
44
|
-
//
|
|
45
|
-
|
|
64
|
+
// Check if update needed (token changed OR figma info missing/changed)
|
|
65
|
+
const userWithFigma = user;
|
|
66
|
+
const needsFigmaUpdate = figmaUserInfo && (userWithFigma.figmaHandle !== figmaUserInfo.handle || userWithFigma.figmaImageUrl !== figmaUserInfo.img_url);
|
|
67
|
+
if (hasUserTokenPropsChanged(user, token) || needsFigmaUpdate) {
|
|
46
68
|
user = await payload.update({
|
|
47
69
|
id: user.id,
|
|
48
70
|
collection: collection.slug,
|
|
49
71
|
data: {
|
|
50
|
-
...token
|
|
72
|
+
...token,
|
|
73
|
+
...figmaUserInfo && {
|
|
74
|
+
figmaHandle: figmaUserInfo.handle,
|
|
75
|
+
figmaImageUrl: figmaUserInfo.img_url
|
|
76
|
+
}
|
|
51
77
|
},
|
|
52
78
|
depth
|
|
53
79
|
});
|
|
@@ -56,7 +82,11 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
56
82
|
const newUser = {
|
|
57
83
|
email: token.preferredUsername || token.email,
|
|
58
84
|
[usernameField.name]: token[tokenUsername],
|
|
59
|
-
...token
|
|
85
|
+
...token,
|
|
86
|
+
...figmaUserInfo && {
|
|
87
|
+
figmaHandle: figmaUserInfo.handle,
|
|
88
|
+
figmaImageUrl: figmaUserInfo.img_url
|
|
89
|
+
}
|
|
60
90
|
};
|
|
61
91
|
if (typeof collection.auth === 'object' && !collection.auth.disableLocalStrategy) {
|
|
62
92
|
newUser.password = uuid();
|
|
@@ -96,6 +126,7 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
96
126
|
msg: 'Error verifying user from defaultVerify.'
|
|
97
127
|
});
|
|
98
128
|
return {
|
|
129
|
+
responseHeaders,
|
|
99
130
|
user: null
|
|
100
131
|
};
|
|
101
132
|
}
|
|
@@ -104,10 +135,12 @@ export const defaultVerify = ({ collection, strategyName, usernameField })=>asyn
|
|
|
104
135
|
user._strategy = strategyName;
|
|
105
136
|
user.exp = token.exp;
|
|
106
137
|
return {
|
|
138
|
+
responseHeaders,
|
|
107
139
|
user
|
|
108
140
|
};
|
|
109
141
|
}
|
|
110
142
|
return {
|
|
143
|
+
responseHeaders,
|
|
111
144
|
user: null
|
|
112
145
|
};
|
|
113
146
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/oauth/defaults.ts"],"sourcesContent":["import type { CollectionConfig, EmailField, TextField,
|
|
1
|
+
{"version":3,"sources":["../../src/oauth/defaults.ts"],"sourcesContent":["import type { CollectionConfig, EmailField, TextField, TypedUser } from 'payload'\n\nimport { parseCookies } from 'payload'\nimport { v4 as uuid } from 'uuid'\n\nimport type { VerifyFunction } from './types.js'\n\nimport { hasUserTokenPropsChanged } from './utilities/hasUserTokenPropsChanged.js'\nimport { isDuplicateKeyError } from './utilities/isDuplicateKeyError.js'\n\ntype FigmaUserInfoCookie = {\n handle: string\n img_url: string\n}\n\ntype UserWithFigmaFields = {\n figmaHandle?: string\n figmaImageUrl?: string\n} & TypedUser\n\nfunction getFigmaUserInfo(headers: Headers): FigmaUserInfoCookie | null {\n try {\n const cookies = parseCookies(headers)\n const encoded = cookies.get('figma-user-info')\n if (!encoded) {\n return null\n }\n return JSON.parse(Buffer.from(encoded, 'base64').toString('utf-8'))\n } catch {\n return null\n }\n}\n\nexport const defaultScope = ['openid', 'profile', 'email']\n\nexport const defaultUsernameField: TextField = {\n name: 'preferredUsername',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n unique: true,\n}\n\nexport const defaultVerify =\n ({\n collection,\n strategyName,\n usernameField,\n }: {\n collection: CollectionConfig\n strategyName: string\n usernameField: EmailField | TextField\n }): VerifyFunction =>\n async ({ headers, payload, token }) => {\n let tokenUsername = 'preferred_username'\n\n if (usernameField.name !== 'preferredUsername') {\n tokenUsername = usernameField.name\n }\n\n if (!token[tokenUsername]) {\n return {\n user: null,\n }\n }\n\n const figmaUserInfo = getFigmaUserInfo(headers)\n let responseHeaders: Headers | undefined\n\n // Clear the cookie after reading\n if (figmaUserInfo) {\n responseHeaders = new Headers()\n responseHeaders.append('Set-Cookie', 'figma-user-info=; Max-Age=0; Path=/;')\n }\n\n let user: null | TypedUser | UserWithFigmaFields = null\n const depth = typeof collection.auth === 'object' ? collection.auth.depth : undefined\n\n try {\n const results = await payload.find({\n collection: collection.slug,\n depth,\n limit: 1,\n pagination: false,\n where: {\n [usernameField.name]: { equals: token[tokenUsername] },\n },\n })\n\n if (results.docs.length > 0) {\n user = results.docs[0] as UserWithFigmaFields\n\n // Check if update needed (token changed OR figma info missing/changed)\n const userWithFigma = user\n const needsFigmaUpdate =\n figmaUserInfo &&\n (userWithFigma.figmaHandle !== figmaUserInfo.handle ||\n userWithFigma.figmaImageUrl !== figmaUserInfo.img_url)\n\n if (hasUserTokenPropsChanged(user, token) || needsFigmaUpdate) {\n user = (await payload.update({\n id: user.id,\n collection: collection.slug,\n data: {\n ...token,\n ...(figmaUserInfo && {\n figmaHandle: figmaUserInfo.handle,\n figmaImageUrl: figmaUserInfo.img_url,\n }),\n },\n depth,\n })) as TypedUser\n }\n } else {\n const newUser: Record<string, unknown> = {\n email: token.preferredUsername || token.email,\n [usernameField.name]: token[tokenUsername],\n ...token,\n ...(figmaUserInfo && {\n figmaHandle: figmaUserInfo.handle,\n figmaImageUrl: figmaUserInfo.img_url,\n }),\n }\n\n if (typeof collection.auth === 'object' && !collection.auth.disableLocalStrategy) {\n newUser.password = uuid()\n }\n\n try {\n user = (await payload.create({\n collection: collection.slug,\n data: newUser,\n depth,\n overrideAccess: true,\n })) as TypedUser\n } catch (createErr: unknown) {\n // Race condition: if unique constraint violation, user was created by concurrent request\n if (isDuplicateKeyError(createErr)) {\n const retryResults = await payload.find({\n collection: collection.slug,\n depth,\n limit: 1,\n pagination: false,\n where: {\n [usernameField.name]: { equals: token[tokenUsername] },\n },\n })\n\n if (retryResults.docs.length > 0) {\n user = retryResults.docs[0] as TypedUser\n }\n } else {\n throw createErr\n }\n }\n }\n } catch (err: unknown) {\n payload.logger.error({\n err,\n msg: 'Error verifying user from defaultVerify.',\n })\n return {\n responseHeaders,\n user: null,\n }\n }\n\n if (user) {\n user.collection = collection.slug\n user._strategy = strategyName\n user.exp = token.exp\n\n return {\n responseHeaders,\n user,\n }\n }\n\n return {\n responseHeaders,\n user: null,\n }\n }\n"],"names":["parseCookies","v4","uuid","hasUserTokenPropsChanged","isDuplicateKeyError","getFigmaUserInfo","headers","cookies","encoded","get","JSON","parse","Buffer","from","toString","defaultScope","defaultUsernameField","name","type","admin","position","readOnly","unique","defaultVerify","collection","strategyName","usernameField","payload","token","tokenUsername","user","figmaUserInfo","responseHeaders","Headers","append","depth","auth","undefined","results","find","slug","limit","pagination","where","equals","docs","length","userWithFigma","needsFigmaUpdate","figmaHandle","handle","figmaImageUrl","img_url","update","id","data","newUser","email","preferredUsername","disableLocalStrategy","password","create","overrideAccess","createErr","retryResults","err","logger","error","msg","_strategy","exp"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLoginEndpoint.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"getLoginEndpoint.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAMzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAY,MAAM,aAAa,CAAA;AAQ7E,UAAU,IAAI;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,aAAa,CAAA;IAC5B,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAID,eAAO,MAAM,gBAAgB,8EAM1B,IAAI,KAAG,QAyRR,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { APIError, generateCookie, parseCookies } from 'payload';
|
|
2
2
|
import { formatAdminURL, getSafeRedirect } from 'payload/shared';
|
|
3
|
-
import { getProjectToken } from '../../api/figma-api.js';
|
|
3
|
+
import { getProjectToken, getUserInfo } from '../../api/figma-api.js';
|
|
4
4
|
import { createCookieOptions } from '../utilities/createCookieOptions.js';
|
|
5
5
|
import { createDebugLogger } from '../utilities/createDebugLogger.js';
|
|
6
6
|
import { extractOrigin } from '../utilities/extractOrigin.js';
|
|
@@ -167,6 +167,21 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
|
|
|
167
167
|
});
|
|
168
168
|
return Response.redirect(failedRedirect);
|
|
169
169
|
}
|
|
170
|
+
// Fetch Figma user info for handle and profile image
|
|
171
|
+
let figmaUserInfo = null;
|
|
172
|
+
try {
|
|
173
|
+
figmaUserInfo = await getUserInfo(access_token);
|
|
174
|
+
debugLogger.info({
|
|
175
|
+
figmaUserInfo,
|
|
176
|
+
msg: 'Fetched Figma user info'
|
|
177
|
+
});
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// Non-fatal: user will just not have handle/image
|
|
180
|
+
debugLogger.error({
|
|
181
|
+
err,
|
|
182
|
+
msg: 'Failed to fetch Figma user info'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
170
185
|
if (!access_token) {
|
|
171
186
|
const errMsg = 'No access_token was returned from the identity provider.';
|
|
172
187
|
req.payload.logger.error({
|
|
@@ -231,6 +246,21 @@ export const getLoginEndpoint = ({ collection, collectionOptions, endpointSlug,
|
|
|
231
246
|
value: token
|
|
232
247
|
});
|
|
233
248
|
req.responseHeaders.append('Set-Cookie', tokenCookie);
|
|
249
|
+
// Store user info in short-lived cookie for defaultVerify to consume
|
|
250
|
+
if (figmaUserInfo) {
|
|
251
|
+
const userInfoValue = Buffer.from(JSON.stringify({
|
|
252
|
+
handle: figmaUserInfo.handle,
|
|
253
|
+
img_url: figmaUserInfo.img_url
|
|
254
|
+
})).toString('base64');
|
|
255
|
+
const userInfoCookie = generateCookie({
|
|
256
|
+
...cookieOptions,
|
|
257
|
+
name: 'figma-user-info',
|
|
258
|
+
expires: new Date(Date.now() + 60_000),
|
|
259
|
+
returnCookieAsObject: false,
|
|
260
|
+
value: userInfoValue
|
|
261
|
+
});
|
|
262
|
+
req.responseHeaders.append('Set-Cookie', userInfoCookie);
|
|
263
|
+
}
|
|
234
264
|
return Response.redirect(redirectToUse);
|
|
235
265
|
} catch (err) {
|
|
236
266
|
req.payload.logger.error({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport { APIError, generateCookie, parseCookies } from 'payload'\nimport { formatAdminURL, getSafeRedirect } from 'payload/shared'\n\nimport type { Strategy } from '../strategy/index.js'\nimport type { CollectionOptions, PluginOptions, StateObj } from '../types.js'\n\nimport { getProjectToken } from '../../api/figma-api.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { extractOrigin } from '../utilities/extractOrigin.js'\nimport { getCookieExpiration } from '../utilities/getCookieExpiration.js'\n\ninterface Args {\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n endpointSlug: string\n pluginOptions: PluginOptions\n strategy: Strategy\n}\n\nconst isAbsoluteURL = (url: string) => /^[a-z][a-z\\d+.-]*:/.test(url)\n\nexport const getLoginEndpoint = ({\n collection,\n collectionOptions,\n endpointSlug,\n pluginOptions,\n strategy,\n}: Args): Endpoint => ({\n handler: async (req) => {\n const { config } = req.payload\n const adminRoute = config.routes?.admin || '/admin'\n\n try {\n await strategy.ensureMeta()\n\n const path = req.pathname\n const debugLogger = createDebugLogger(req.payload, pluginOptions.debug)\n\n let jsonBody: Record<string, unknown> = {}\n\n const contentType = req.headers.get('content-type')\n\n // Type narrow PayloadRequst's Partial<Request>\n if (typeof req.json !== 'function' || typeof req.formData !== 'function') {\n debugLogger.info({\n msg: 'Request does not have json or formData methods',\n path,\n })\n\n return Response.redirect(`${pluginOptions.redirectServerURL || config.serverURL}/login`)\n }\n\n if (collectionOptions.authorizationURLParams?.response_mode === 'query') {\n if (Object.keys(req.query).length > 0) {\n jsonBody = req.query\n }\n } else if (contentType === 'application/json') {\n jsonBody = await req.json()\n } else if (contentType === 'application/x-www-form-urlencoded') {\n const formData = await req.formData()\n jsonBody = Object.fromEntries(formData.entries())\n }\n\n const stateRaw = jsonBody.state as string\n const state: StateObj = JSON.parse(Buffer.from(stateRaw, 'base64').toString('utf-8'))\n\n const redirectServerURL =\n state.serverURL || pluginOptions.redirectServerURL || config.serverURL\n let failedRedirect = formatAdminURL({\n adminRoute,\n path: '/login',\n serverURL: redirectServerURL,\n })\n let redirectToUse = formatAdminURL({\n adminRoute,\n serverURL: redirectServerURL,\n })\n\n if (state.redirect) {\n redirectToUse = state.redirect\n }\n\n if (state.failedRedirect) {\n failedRedirect = state.failedRedirect\n }\n\n failedRedirect = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: failedRedirect })\n redirectToUse = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: redirectToUse })\n\n if (!isAbsoluteURL(failedRedirect)) {\n return Response.json({ error: 'Invalid failed redirect provided' }, { status: 403 })\n }\n if (!isAbsoluteURL(redirectToUse)) {\n return Response.json({ error: 'Invalid redirect provided' }, { status: 403 })\n }\n\n if (Array.isArray(req.payload.config.csrf) && req.payload.config.csrf.length > 0) {\n const failedRedirectOrigin = extractOrigin(failedRedirect)\n const redirectToUseOrigin = extractOrigin(redirectToUse)\n\n if (failedRedirectOrigin && !req.payload.config.csrf.includes(failedRedirectOrigin)) {\n req.payload.logger.error(\n `Failed redirect origin ${failedRedirectOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Failed redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n\n if (redirectToUseOrigin && !req.payload.config.csrf.includes(redirectToUseOrigin)) {\n req.payload.logger.error(\n `Redirect origin ${redirectToUseOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n }\n\n // Verify signature\n if (strategy.name !== req.payload.decrypt(state.sig)) {\n req.payload.logger.error('Signature mismatch. Failing login and redirecting.')\n return Response.redirect(failedRedirect)\n }\n\n if (!strategy.meta?.token_endpoint) {\n debugLogger.error({\n msg: `No metadata was obtained from the identity provider. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n debugLogger.info({ body: jsonBody, msg: 'Received login response body', path })\n\n if (!jsonBody?.code) {\n debugLogger.error({\n msg: `No authorization code present in body. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n // Exchange `code` from body for refresh token\n const { code } = jsonBody\n\n if (typeof code !== 'string') {\n debugLogger.error({\n msg: `Authorization code present in body is not a string. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n const body: Record<string, string> = {\n client_id: strategy.clientID,\n code,\n code_verifier: req.payload.decrypt(state.verifier),\n grant_type: 'authorization_code',\n redirect_uri: formatAdminURL({\n apiRoute: config.routes?.api || '/api',\n path: `/${collection.slug}/${endpointSlug}/login`,\n serverURL: redirectServerURL,\n }),\n }\n\n if (strategy.collectionOptions.clientSecret) {\n body.client_secret = strategy.collectionOptions.clientSecret\n }\n\n debugLogger.info({\n msg: 'Exchanging code for access token',\n path,\n requestBody: body,\n })\n\n const tokenRes = await fetch(strategy.meta?.token_endpoint, {\n body: new URLSearchParams(body),\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n method: 'POST',\n }).then((r) => r.json())\n\n debugLogger.info({ msg: 'Token Response', path, tokenRes })\n\n const { access_token, error, error_description, expires_in: refreshTokenExpires } = tokenRes\n\n if (error) {\n req.payload.logger.error('Error swapping access code with access token:')\n req.payload.logger.error({ err: error, error_description })\n return Response.redirect(failedRedirect)\n }\n\n if (!access_token) {\n const errMsg = 'No access_token was returned from the identity provider.'\n req.payload.logger.error({ msg: errMsg, path, tokenRes })\n throw new APIError(errMsg)\n }\n\n // Swap for project token\n const project_token = await getProjectToken(access_token, config.custom.figma.contentSystemId)\n const refresh_token = access_token\n\n // Store project token as access_token\n\n const token = project_token.token\n const expiresIn = project_token.expiresAt\n\n if (!token) {\n const errMsg = `No ${\n collectionOptions.token || 'access_token'\n } was returned from the identity provider.`\n req.payload.logger.error({ msg: errMsg, path })\n throw new APIError(errMsg)\n }\n\n const cookieOptions = createCookieOptions({ collection, headers: req.headers, pluginOptions })\n\n // get the payloadRedirect cookie value\n const cookies = parseCookies(req.headers)\n\n const payloadRedirect =\n 'payloadRedirect' in cookies ? (cookies.payloadRedirect as string) : ''\n\n if (!req.responseHeaders) {\n req.responseHeaders = new Headers()\n }\n\n if (payloadRedirect) {\n if (isAbsoluteURL(payloadRedirect)) {\n redirectToUse = payloadRedirect\n } else {\n redirectToUse += payloadRedirect\n }\n\n req.responseHeaders.append(\n 'Set-Cookie',\n `payloadRedirect=${redirectToUse}; Max-Age=0; Path=/;`,\n )\n }\n\n if (refresh_token && !collectionOptions.usePayloadJWT) {\n debugLogger.info('Setting refresh token cookie')\n const refreshCookie = generateCookie({\n returnCookieAsObject: false,\n ...cookieOptions,\n name: `${strategy.cookieName}-refresh`,\n value: refresh_token,\n // Default to 1 year if no expiration\n expires: getCookieExpiration(cookieOptions?.expires ?? refreshTokenExpires ?? 31_556_952),\n }) as string\n\n req.responseHeaders.append('Set-Cookie', refreshCookie)\n }\n\n const tokenExpires = cookieOptions?.expires\n ? getCookieExpiration(cookieOptions.expires)\n : new Date(expiresIn) // Project token uses epoch time\n const tokenCookie = generateCookie({\n ...cookieOptions,\n name: strategy.cookieName,\n expires: tokenExpires,\n returnCookieAsObject: false,\n value: token,\n }) as string\n\n req.responseHeaders.append('Set-Cookie', tokenCookie)\n\n return Response.redirect(redirectToUse)\n } catch (err: unknown) {\n req.payload.logger.error({ err, msg: 'Error while attempting to login', path: req.pathname })\n\n return Response.json({ error: 'Error while attempting to login' }, { status: 500 })\n }\n },\n method: collectionOptions.authorizationURLParams?.response_mode === 'query' ? 'get' : 'post',\n path: `/${endpointSlug}/login`,\n})\n"],"names":["APIError","generateCookie","parseCookies","formatAdminURL","getSafeRedirect","getProjectToken","createCookieOptions","createDebugLogger","extractOrigin","getCookieExpiration","isAbsoluteURL","url","test","getLoginEndpoint","collection","collectionOptions","endpointSlug","pluginOptions","strategy","handler","req","config","payload","adminRoute","routes","admin","ensureMeta","path","pathname","debugLogger","debug","jsonBody","contentType","headers","get","json","formData","info","msg","Response","redirect","redirectServerURL","serverURL","authorizationURLParams","response_mode","Object","keys","query","length","fromEntries","entries","stateRaw","state","JSON","parse","Buffer","from","toString","failedRedirect","redirectToUse","allowAbsoluteUrls","redirectTo","error","status","Array","isArray","csrf","failedRedirectOrigin","redirectToUseOrigin","includes","logger","name","decrypt","sig","meta","token_endpoint","body","code","client_id","clientID","code_verifier","verifier","grant_type","redirect_uri","apiRoute","api","slug","clientSecret","client_secret","requestBody","tokenRes","fetch","URLSearchParams","method","then","r","access_token","error_description","expires_in","refreshTokenExpires","err","errMsg","project_token","custom","figma","contentSystemId","refresh_token","token","expiresIn","expiresAt","cookieOptions","cookies","payloadRedirect","responseHeaders","Headers","append","usePayloadJWT","refreshCookie","returnCookieAsObject","cookieName","value","expires","tokenExpires","Date","tokenCookie"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport { APIError, generateCookie, parseCookies } from 'payload'\nimport { formatAdminURL, getSafeRedirect } from 'payload/shared'\n\nimport type { FigmaUserInfo } from '../../api/figma-api.js'\nimport type { Strategy } from '../strategy/index.js'\nimport type { CollectionOptions, PluginOptions, StateObj } from '../types.js'\n\nimport { getProjectToken, getUserInfo } from '../../api/figma-api.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { extractOrigin } from '../utilities/extractOrigin.js'\nimport { getCookieExpiration } from '../utilities/getCookieExpiration.js'\n\ninterface Args {\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n endpointSlug: string\n pluginOptions: PluginOptions\n strategy: Strategy\n}\n\nconst isAbsoluteURL = (url: string) => /^[a-z][a-z\\d+.-]*:/.test(url)\n\nexport const getLoginEndpoint = ({\n collection,\n collectionOptions,\n endpointSlug,\n pluginOptions,\n strategy,\n}: Args): Endpoint => ({\n handler: async (req) => {\n const { config } = req.payload\n const adminRoute = config.routes?.admin || '/admin'\n\n try {\n await strategy.ensureMeta()\n\n const path = req.pathname\n const debugLogger = createDebugLogger(req.payload, pluginOptions.debug)\n\n let jsonBody: Record<string, unknown> = {}\n\n const contentType = req.headers.get('content-type')\n\n // Type narrow PayloadRequst's Partial<Request>\n if (typeof req.json !== 'function' || typeof req.formData !== 'function') {\n debugLogger.info({\n msg: 'Request does not have json or formData methods',\n path,\n })\n\n return Response.redirect(`${pluginOptions.redirectServerURL || config.serverURL}/login`)\n }\n\n if (collectionOptions.authorizationURLParams?.response_mode === 'query') {\n if (Object.keys(req.query).length > 0) {\n jsonBody = req.query\n }\n } else if (contentType === 'application/json') {\n jsonBody = await req.json()\n } else if (contentType === 'application/x-www-form-urlencoded') {\n const formData = await req.formData()\n jsonBody = Object.fromEntries(formData.entries())\n }\n\n const stateRaw = jsonBody.state as string\n const state: StateObj = JSON.parse(Buffer.from(stateRaw, 'base64').toString('utf-8'))\n\n const redirectServerURL =\n state.serverURL || pluginOptions.redirectServerURL || config.serverURL\n let failedRedirect = formatAdminURL({\n adminRoute,\n path: '/login',\n serverURL: redirectServerURL,\n })\n let redirectToUse = formatAdminURL({\n adminRoute,\n serverURL: redirectServerURL,\n })\n\n if (state.redirect) {\n redirectToUse = state.redirect\n }\n\n if (state.failedRedirect) {\n failedRedirect = state.failedRedirect\n }\n\n failedRedirect = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: failedRedirect })\n redirectToUse = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: redirectToUse })\n\n if (!isAbsoluteURL(failedRedirect)) {\n return Response.json({ error: 'Invalid failed redirect provided' }, { status: 403 })\n }\n if (!isAbsoluteURL(redirectToUse)) {\n return Response.json({ error: 'Invalid redirect provided' }, { status: 403 })\n }\n\n if (Array.isArray(req.payload.config.csrf) && req.payload.config.csrf.length > 0) {\n const failedRedirectOrigin = extractOrigin(failedRedirect)\n const redirectToUseOrigin = extractOrigin(redirectToUse)\n\n if (failedRedirectOrigin && !req.payload.config.csrf.includes(failedRedirectOrigin)) {\n req.payload.logger.error(\n `Failed redirect origin ${failedRedirectOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Failed redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n\n if (redirectToUseOrigin && !req.payload.config.csrf.includes(redirectToUseOrigin)) {\n req.payload.logger.error(\n `Redirect origin ${redirectToUseOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n }\n\n // Verify signature\n if (strategy.name !== req.payload.decrypt(state.sig)) {\n req.payload.logger.error('Signature mismatch. Failing login and redirecting.')\n return Response.redirect(failedRedirect)\n }\n\n if (!strategy.meta?.token_endpoint) {\n debugLogger.error({\n msg: `No metadata was obtained from the identity provider. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n debugLogger.info({ body: jsonBody, msg: 'Received login response body', path })\n\n if (!jsonBody?.code) {\n debugLogger.error({\n msg: `No authorization code present in body. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n // Exchange `code` from body for refresh token\n const { code } = jsonBody\n\n if (typeof code !== 'string') {\n debugLogger.error({\n msg: `Authorization code present in body is not a string. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n const body: Record<string, string> = {\n client_id: strategy.clientID,\n code,\n code_verifier: req.payload.decrypt(state.verifier),\n grant_type: 'authorization_code',\n redirect_uri: formatAdminURL({\n apiRoute: config.routes?.api || '/api',\n path: `/${collection.slug}/${endpointSlug}/login`,\n serverURL: redirectServerURL,\n }),\n }\n\n if (strategy.collectionOptions.clientSecret) {\n body.client_secret = strategy.collectionOptions.clientSecret\n }\n\n debugLogger.info({\n msg: 'Exchanging code for access token',\n path,\n requestBody: body,\n })\n\n const tokenRes = await fetch(strategy.meta?.token_endpoint, {\n body: new URLSearchParams(body),\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n method: 'POST',\n }).then((r) => r.json())\n\n debugLogger.info({ msg: 'Token Response', path, tokenRes })\n\n const { access_token, error, error_description, expires_in: refreshTokenExpires } = tokenRes\n\n if (error) {\n req.payload.logger.error('Error swapping access code with access token:')\n req.payload.logger.error({ err: error, error_description })\n return Response.redirect(failedRedirect)\n }\n\n // Fetch Figma user info for handle and profile image\n let figmaUserInfo: FigmaUserInfo | null = null\n try {\n figmaUserInfo = await getUserInfo(access_token)\n debugLogger.info({ figmaUserInfo, msg: 'Fetched Figma user info' })\n } catch (err) {\n // Non-fatal: user will just not have handle/image\n debugLogger.error({ err, msg: 'Failed to fetch Figma user info' })\n }\n\n if (!access_token) {\n const errMsg = 'No access_token was returned from the identity provider.'\n req.payload.logger.error({ msg: errMsg, path, tokenRes })\n throw new APIError(errMsg)\n }\n\n // Swap for project token\n const project_token = await getProjectToken(access_token, config.custom.figma.contentSystemId)\n const refresh_token = access_token\n\n // Store project token as access_token\n\n const token = project_token.token\n const expiresIn = project_token.expiresAt\n\n if (!token) {\n const errMsg = `No ${\n collectionOptions.token || 'access_token'\n } was returned from the identity provider.`\n req.payload.logger.error({ msg: errMsg, path })\n throw new APIError(errMsg)\n }\n\n const cookieOptions = createCookieOptions({ collection, headers: req.headers, pluginOptions })\n\n // get the payloadRedirect cookie value\n const cookies = parseCookies(req.headers)\n\n const payloadRedirect =\n 'payloadRedirect' in cookies ? (cookies.payloadRedirect as string) : ''\n\n if (!req.responseHeaders) {\n req.responseHeaders = new Headers()\n }\n\n if (payloadRedirect) {\n if (isAbsoluteURL(payloadRedirect)) {\n redirectToUse = payloadRedirect\n } else {\n redirectToUse += payloadRedirect\n }\n\n req.responseHeaders.append(\n 'Set-Cookie',\n `payloadRedirect=${redirectToUse}; Max-Age=0; Path=/;`,\n )\n }\n\n if (refresh_token && !collectionOptions.usePayloadJWT) {\n debugLogger.info('Setting refresh token cookie')\n const refreshCookie = generateCookie({\n returnCookieAsObject: false,\n ...cookieOptions,\n name: `${strategy.cookieName}-refresh`,\n value: refresh_token,\n // Default to 1 year if no expiration\n expires: getCookieExpiration(cookieOptions?.expires ?? refreshTokenExpires ?? 31_556_952),\n }) as string\n\n req.responseHeaders.append('Set-Cookie', refreshCookie)\n }\n\n const tokenExpires = cookieOptions?.expires\n ? getCookieExpiration(cookieOptions.expires)\n : new Date(expiresIn) // Project token uses epoch time\n const tokenCookie = generateCookie({\n ...cookieOptions,\n name: strategy.cookieName,\n expires: tokenExpires,\n returnCookieAsObject: false,\n value: token,\n }) as string\n\n req.responseHeaders.append('Set-Cookie', tokenCookie)\n\n // Store user info in short-lived cookie for defaultVerify to consume\n if (figmaUserInfo) {\n const userInfoValue = Buffer.from(\n JSON.stringify({\n handle: figmaUserInfo.handle,\n img_url: figmaUserInfo.img_url,\n }),\n ).toString('base64')\n const userInfoCookie = generateCookie({\n ...cookieOptions,\n name: 'figma-user-info',\n expires: new Date(Date.now() + 60_000), // 60 seconds\n returnCookieAsObject: false,\n value: userInfoValue,\n }) as string\n req.responseHeaders.append('Set-Cookie', userInfoCookie)\n }\n\n return Response.redirect(redirectToUse)\n } catch (err: unknown) {\n req.payload.logger.error({ err, msg: 'Error while attempting to login', path: req.pathname })\n\n return Response.json({ error: 'Error while attempting to login' }, { status: 500 })\n }\n },\n method: collectionOptions.authorizationURLParams?.response_mode === 'query' ? 'get' : 'post',\n path: `/${endpointSlug}/login`,\n})\n"],"names":["APIError","generateCookie","parseCookies","formatAdminURL","getSafeRedirect","getProjectToken","getUserInfo","createCookieOptions","createDebugLogger","extractOrigin","getCookieExpiration","isAbsoluteURL","url","test","getLoginEndpoint","collection","collectionOptions","endpointSlug","pluginOptions","strategy","handler","req","config","payload","adminRoute","routes","admin","ensureMeta","path","pathname","debugLogger","debug","jsonBody","contentType","headers","get","json","formData","info","msg","Response","redirect","redirectServerURL","serverURL","authorizationURLParams","response_mode","Object","keys","query","length","fromEntries","entries","stateRaw","state","JSON","parse","Buffer","from","toString","failedRedirect","redirectToUse","allowAbsoluteUrls","redirectTo","error","status","Array","isArray","csrf","failedRedirectOrigin","redirectToUseOrigin","includes","logger","name","decrypt","sig","meta","token_endpoint","body","code","client_id","clientID","code_verifier","verifier","grant_type","redirect_uri","apiRoute","api","slug","clientSecret","client_secret","requestBody","tokenRes","fetch","URLSearchParams","method","then","r","access_token","error_description","expires_in","refreshTokenExpires","err","figmaUserInfo","errMsg","project_token","custom","figma","contentSystemId","refresh_token","token","expiresIn","expiresAt","cookieOptions","cookies","payloadRedirect","responseHeaders","Headers","append","usePayloadJWT","refreshCookie","returnCookieAsObject","cookieName","value","expires","tokenExpires","Date","tokenCookie","userInfoValue","stringify","handle","img_url","userInfoCookie","now"],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ import type { Config, SanitizedConfig } from 'payload';
|
|
|
11
11
|
* // Minimal config
|
|
12
12
|
* const config: FigmaConfig = {
|
|
13
13
|
* figma: {
|
|
14
|
-
* contentSystemId: process.env.
|
|
14
|
+
* contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!
|
|
15
15
|
* },
|
|
16
16
|
* collections: [...]
|
|
17
17
|
* }
|
|
@@ -20,7 +20,7 @@ import type { Config, SanitizedConfig } from 'payload';
|
|
|
20
20
|
* // With custom editor
|
|
21
21
|
* const config: FigmaConfig = {
|
|
22
22
|
* figma: {
|
|
23
|
-
* contentSystemId: process.env.
|
|
23
|
+
* contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!
|
|
24
24
|
* },
|
|
25
25
|
* collections: [...],
|
|
26
26
|
* editor: lexicalEditor({ features: [...] })
|
|
@@ -30,7 +30,7 @@ import type { Config, SanitizedConfig } from 'payload';
|
|
|
30
30
|
* // Disable Content System (use custom db)
|
|
31
31
|
* const config: FigmaConfig = {
|
|
32
32
|
* figma: {
|
|
33
|
-
* contentSystemId: process.env.
|
|
33
|
+
* contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!,
|
|
34
34
|
* useContentSystem: false
|
|
35
35
|
* },
|
|
36
36
|
* db: mongooseAdapter({ url: process.env.DATABASE_URI }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-config.d.ts","sourceRoot":"","sources":["../../src/plugin/build-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"build-config.d.ts","sourceRoot":"","sources":["../../src/plugin/build-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAS,eAAe,EAAE,MAAM,SAAS,CAAA;AAmB7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;CAC1B,GAAG;IACF,KAAK,EAAE;QACL,eAAe,EAAE,MAAM,CAAA;QACvB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;CACF,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC,GAC1C,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AA6B7B;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CA8IpF"}
|
|
@@ -6,10 +6,36 @@ import { getTokenStore } from '../auth/token-store.js';
|
|
|
6
6
|
import { ENV_CONFIG, getEnvironment } from '../constants.js';
|
|
7
7
|
import { contentAPIAdapter } from '../db-content-api/index.js';
|
|
8
8
|
import { health } from '../endpoints/health.js';
|
|
9
|
+
import { schema } from '../endpoints/schema.js';
|
|
9
10
|
import { oAuth2Plugin } from '../oauth/index.js';
|
|
10
11
|
import { createStorageClient } from '../storage-content-api/client.js';
|
|
11
12
|
import { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js';
|
|
12
13
|
import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* Fields added to users collection for Figma profile info
|
|
16
|
+
*/ const figmaUserFields = [
|
|
17
|
+
{
|
|
18
|
+
name: 'figmaHandle',
|
|
19
|
+
type: 'text',
|
|
20
|
+
admin: {
|
|
21
|
+
readOnly: true
|
|
22
|
+
},
|
|
23
|
+
label: 'Name'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'figmaImageUrl',
|
|
27
|
+
type: 'text',
|
|
28
|
+
admin: {
|
|
29
|
+
components: {
|
|
30
|
+
Cell: '@payloadcms/figma/client#FigmaAvatarCell'
|
|
31
|
+
},
|
|
32
|
+
disableBulkEdit: true,
|
|
33
|
+
hidden: true,
|
|
34
|
+
readOnly: true
|
|
35
|
+
},
|
|
36
|
+
label: 'Profile Picture'
|
|
37
|
+
}
|
|
38
|
+
];
|
|
13
39
|
/**
|
|
14
40
|
* Figma platform wrapper for Payload's buildConfig.
|
|
15
41
|
* Automatically injects:
|
|
@@ -22,7 +48,7 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
22
48
|
*/ export async function buildFigmaConfig(config) {
|
|
23
49
|
// Validate required environment variable
|
|
24
50
|
if (!config.figma.contentSystemId) {
|
|
25
|
-
throw new Error('
|
|
51
|
+
throw new Error('FIGMA_CONTENT_API_CONTENT_SYSTEM_ID is required. ' + 'Ensure your .env file contains: FIGMA_CONTENT_API_CONTENT_SYSTEM_ID=your-content-system-id');
|
|
26
52
|
}
|
|
27
53
|
const env = getEnvironment();
|
|
28
54
|
const url = process.env.FIGMA_CONTENT_API_URL || ENV_CONFIG[env].contentApiUrl;
|
|
@@ -107,9 +133,10 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
107
133
|
...config,
|
|
108
134
|
admin: {
|
|
109
135
|
// Auto-refresh tokens before expiry to prevent unexpected logouts.
|
|
110
|
-
// The project token (access token) expires in ~15 minutes, so the client
|
|
111
|
-
// will automatically refresh at ~14 minutes to keep the session alive.
|
|
112
136
|
autoRefresh: true,
|
|
137
|
+
avatar: {
|
|
138
|
+
Component: '@payloadcms/figma/client#FigmaAvatar'
|
|
139
|
+
},
|
|
113
140
|
...config.admin
|
|
114
141
|
},
|
|
115
142
|
custom: {
|
|
@@ -119,7 +146,8 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
119
146
|
editor: config.editor ?? lexicalEditor(),
|
|
120
147
|
endpoints: [
|
|
121
148
|
...config.endpoints ?? [],
|
|
122
|
-
health
|
|
149
|
+
health,
|
|
150
|
+
schema
|
|
123
151
|
],
|
|
124
152
|
secret: 'MY_SECRET',
|
|
125
153
|
// Add oauth to plugins if not already present
|
|
@@ -152,6 +180,22 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
152
180
|
})
|
|
153
181
|
]
|
|
154
182
|
};
|
|
183
|
+
// Inject figma fields into users collection
|
|
184
|
+
const collections = configWithFigmaDefaults.collections?.map((col)=>{
|
|
185
|
+
if (col.slug === 'users') {
|
|
186
|
+
return {
|
|
187
|
+
...col,
|
|
188
|
+
fields: [
|
|
189
|
+
...col.fields || [],
|
|
190
|
+
...figmaUserFields
|
|
191
|
+
]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return col;
|
|
195
|
+
});
|
|
196
|
+
if (collections) {
|
|
197
|
+
configWithFigmaDefaults.collections = collections;
|
|
198
|
+
}
|
|
155
199
|
// Delegate to Payload's buildConfig
|
|
156
200
|
return await payloadBuildConfig(configWithFigmaDefaults);
|
|
157
201
|
}
|