payload-zitadel-plugin 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -59
- package/dist/components/Avatar.d.ts +3 -0
- package/dist/components/Avatar.d.ts.map +1 -0
- package/dist/components/{Avatar/index.js → Avatar.js} +13 -13
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/LoginButton.d.ts +3 -0
- package/dist/components/LoginButton.d.ts.map +1 -0
- package/dist/components/LoginButton.js +19 -0
- package/dist/components/LoginButton.js.map +1 -0
- package/dist/components/index.d.ts +2 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -3
- package/dist/components/index.js.map +1 -1
- package/dist/handlers/authorize.d.ts +3 -0
- package/dist/handlers/authorize.d.ts.map +1 -0
- package/dist/handlers/authorize.js +28 -0
- package/dist/handlers/authorize.js.map +1 -0
- package/dist/handlers/callback.d.ts +3 -0
- package/dist/handlers/callback.d.ts.map +1 -0
- package/dist/handlers/callback.js +50 -0
- package/dist/handlers/callback.js.map +1 -0
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +4 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/user.d.ts +6 -0
- package/dist/hooks/user.d.ts.map +1 -0
- package/dist/hooks/user.js +12 -0
- package/dist/hooks/user.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +132 -135
- package/dist/index.js.map +1 -1
- package/dist/strategy.d.ts.map +1 -1
- package/dist/strategy.js +27 -20
- package/dist/strategy.js.map +1 -1
- package/dist/translations.d.ts +2 -2
- package/dist/translations.js +4 -4
- package/dist/translations.js.map +1 -1
- package/dist/types.d.ts +14 -26
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/user.d.ts +5 -0
- package/dist/utils/user.d.ts.map +1 -0
- package/dist/utils/user.js +15 -0
- package/dist/utils/user.js.map +1 -0
- package/package.json +17 -12
- package/dist/components/Avatar/index.d.ts +0 -5
- package/dist/components/Avatar/index.d.ts.map +0 -1
- package/dist/components/Avatar/index.js.map +0 -1
- package/dist/components/LoginButton/button.d.ts +0 -6
- package/dist/components/LoginButton/button.d.ts.map +0 -1
- package/dist/components/LoginButton/button.js +0 -19
- package/dist/components/LoginButton/button.js.map +0 -1
- package/dist/components/LoginButton/index.d.ts +0 -6
- package/dist/components/LoginButton/index.d.ts.map +0 -1
- package/dist/components/LoginButton/index.js +0 -8
- package/dist/components/LoginButton/index.js.map +0 -1
- package/dist/components/Session/index.d.ts +0 -4
- package/dist/components/Session/index.d.ts.map +0 -1
- package/dist/components/Session/index.js +0 -6
- package/dist/components/Session/index.js.map +0 -1
- package/dist/options.d.ts +0 -3
- package/dist/options.d.ts.map +0 -1
- package/dist/options.js +0 -54
- package/dist/options.js.map +0 -1
package/README.md
CHANGED
@@ -2,72 +2,63 @@
|
|
2
2
|
|
3
3
|
[](https://npmjs.org/package/payload-zitadel-plugin)
|
4
4
|
|
5
|
-
plugin for [Payload CMS](https://payloadcms.com), which enables authentication via Zitadel IdP.
|
6
|
-
|
5
|
+
plugin for [Payload CMS](https://payloadcms.com), which enables authentication via Zitadel IdP.
|
6
|
+
|
7
|
+
The default use case is to fully replace PayloadCMS Auth with Zitadel.
|
8
|
+
Thus the user collection in PayloadCMS becomes just a shadow of the information in Zitadel.
|
7
9
|
|
8
10
|
:boom: :boom: :boom: works :100: with PayloadCMS version :three: :boom: :boom: :boom:
|
9
11
|
|
10
12
|
## Install
|
11
13
|
|
12
14
|
```shell
|
13
|
-
pnpm add payload-zitadel-plugin@0.
|
15
|
+
pnpm add payload-zitadel-plugin@0.2.0
|
14
16
|
```
|
15
17
|
|
16
18
|
## Configuration
|
17
19
|
|
18
20
|
Initialize the plugin in Payload Config File. Change the parameters to connect to your Zitadel Instance.
|
19
21
|
|
20
|
-
#### zitadel-plugin.ts
|
21
|
-
|
22
|
-
```typescript
|
23
|
-
import {ZitadelPluginProvider} from 'payload-zitadel-plugin'
|
24
|
-
|
25
|
-
export const {zitadelPlugin, nextauthHandler} = ZitadelPluginProvider({
|
26
|
-
// in Zitadel create a new App->Web->PKCE
|
27
|
-
issuerUrl: process.env.ZITADEL_URL,
|
28
|
-
clientId: process.env.ZITADEL_CLIENT_ID,
|
29
|
-
|
30
|
-
// interpolation text for the Login Button - "sign in with ..."
|
31
|
-
// externalProviderName: 'ZITADEL',
|
32
|
-
|
33
|
-
// set to true if you do not want to use the IdP Profile as the Avatar
|
34
|
-
// disableAvatar: true
|
35
|
-
|
36
|
-
// set to true if you want to use your own custom login button
|
37
|
-
// disableDefaultLoginButton: true
|
38
|
-
|
39
|
-
// set to true if you want users to only be able to sign in via Zitadel
|
40
|
-
// disableLocalStrategy: true,
|
41
|
-
|
42
|
-
// if you want to specify the users collection slug
|
43
|
-
// authSlug: 'users',
|
44
|
-
|
45
|
-
// if you want to specify the field name for the IdP Id in the users collection
|
46
|
-
// associatedIdFieldName: 'idp_id'
|
47
|
-
|
48
|
-
// change the internal name, only if you know what you are doing!!!
|
49
|
-
// internalProviderName = 'zitadel',
|
50
|
-
|
51
|
-
// following properties are only needed if you want to authenticate clients for the API
|
52
|
-
// if you are just using the CMS you can ignore all of them
|
53
|
-
// in Zitadel create a new App->API->JWT
|
54
|
-
// enableAPI: true,
|
55
|
-
// apiClientId: process.env.ZITADEL_API_CLIENT_ID,
|
56
|
-
// apiKeyId: process.env.ZITADEL_API_KEY_ID,
|
57
|
-
// apiKey: process.env.ZITADEL_API_KEY
|
58
|
-
})
|
59
|
-
|
60
|
-
```
|
61
|
-
|
62
22
|
#### payload.config.ts
|
63
23
|
|
64
24
|
```typescript
|
65
25
|
import {buildConfig} from 'payload/config'
|
26
|
+
import {ZitadelPlugin} from 'payload-zitadel-plugin'
|
27
|
+
|
66
28
|
|
67
29
|
export default buildConfig({
|
68
30
|
...,
|
69
31
|
plugins: [
|
70
|
-
|
32
|
+
ZitadelPlugin({
|
33
|
+
// URL of your Zitadel instance
|
34
|
+
issuerUrl: process.env.ZITADEL_URL,
|
35
|
+
|
36
|
+
// in Zitadel create a new App->Web->PKCE, then copy the Client ID
|
37
|
+
clientId: process.env.ZITADEL_CLIENT_ID,
|
38
|
+
|
39
|
+
// interpolation text for the Login Button - "sign in with ..."
|
40
|
+
label: 'Zitadel',
|
41
|
+
|
42
|
+
// set the name of the CustomStrategy in PayloadCMS - usually not necessary
|
43
|
+
// strategyName: 'zitadel'
|
44
|
+
|
45
|
+
// set to true if you do not want to use the Zitadel Profile Picture as the Avatar
|
46
|
+
// disableAvatar: true
|
47
|
+
|
48
|
+
// set to true if you want to use your own custom login button
|
49
|
+
// disableDefaultLoginButton: true
|
50
|
+
|
51
|
+
// if you want to specify the field name for the Zitadel User Id in the users collection
|
52
|
+
// associatedIdFieldName: 'idp_id'
|
53
|
+
|
54
|
+
// following properties are only needed if you want to authenticate clients for the API
|
55
|
+
// if you are just using the CMS you can ignore all of them
|
56
|
+
// in Zitadel create a new App->API->JWT
|
57
|
+
// enableAPI: true,
|
58
|
+
// apiClientId: process.env.ZITADEL_API_CLIENT_ID,
|
59
|
+
// apiKeyId: process.env.ZITADEL_API_KEY_ID,
|
60
|
+
// apiKey: process.env.ZITADEL_API_KEY
|
61
|
+
})
|
71
62
|
],
|
72
63
|
...
|
73
64
|
})
|
@@ -110,21 +101,9 @@ const nextConfig = {
|
|
110
101
|
export default withPayload(nextConfig)
|
111
102
|
```
|
112
103
|
|
113
|
-
### create route
|
114
|
-
|
115
|
-
Unfortunately you need to manually create the following NextAuth.js route in your Next.js App (using App Router):
|
116
|
-
|
117
|
-
### (nextauth)/api/auth/[...nextauth]/route.ts
|
118
|
-
|
119
|
-
```typescript
|
120
|
-
import {nextauthHandler} from '@/config/zitadel-plugin'
|
121
|
-
|
122
|
-
export {nextauthHandler as GET, nextauthHandler as POST}
|
123
|
-
```
|
124
|
-
|
125
104
|
### add profile picture url to accepted Next.js assets
|
126
105
|
|
127
|
-
If you want to use the Zitadel profile picture as the avatar in PayloadCMS (`disableAvatar != true`),
|
106
|
+
If you want to use the Zitadel profile picture as the avatar in PayloadCMS (`disableAvatar != true`),
|
128
107
|
you have to manually add the asset URL to the Next.js config file.
|
129
108
|
|
130
109
|
#### next.config.js
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/components/Avatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,eAAO,MAAM,MAAM,yBA6BlB,CAAA"}
|
@@ -1,14 +1,13 @@
|
|
1
1
|
'use client';
|
2
2
|
import * as React from 'react';
|
3
|
-
// https://github.com/vercel/next.js/issues/46078
|
4
|
-
// import Image from 'next/image.js'
|
5
|
-
import { useSession } from 'next-auth/react';
|
6
3
|
import { DefaultAccountIcon } from '@payloadcms/ui/graphics/Account/Default';
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
import { Image } from 'next/dist/client/image-component.js';
|
5
|
+
import { useAuth } from '@payloadcms/ui';
|
6
|
+
export const Avatar = ()=>{
|
7
|
+
const { user } = useAuth();
|
8
|
+
return user?.image ? /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement("style", null, `
|
11
9
|
.avatar {
|
10
|
+
position: relative;
|
12
11
|
height: 2rem;
|
13
12
|
width: 2rem;
|
14
13
|
}
|
@@ -18,17 +17,18 @@ export const Avatar = ({ active })=>{
|
|
18
17
|
}
|
19
18
|
|
20
19
|
.avatar img {
|
21
|
-
object-fit: fill;
|
22
20
|
border-radius: 100%;
|
23
21
|
}
|
24
22
|
`), /*#__PURE__*/ React.createElement("div", {
|
25
23
|
className: "avatar"
|
26
|
-
}, /*#__PURE__*/ React.createElement(
|
27
|
-
src:
|
28
|
-
alt: "Profile Picture"
|
24
|
+
}, /*#__PURE__*/ React.createElement(Image, {
|
25
|
+
src: user.image,
|
26
|
+
alt: "Profile Picture",
|
27
|
+
fill: true,
|
28
|
+
sizes: "2rem 2rem"
|
29
29
|
}))) : /*#__PURE__*/ React.createElement(DefaultAccountIcon, {
|
30
|
-
active:
|
30
|
+
active: false
|
31
31
|
});
|
32
32
|
};
|
33
33
|
|
34
|
-
//# sourceMappingURL=
|
34
|
+
//# sourceMappingURL=Avatar.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/components/Avatar.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {DefaultAccountIcon} from '@payloadcms/ui/graphics/Account/Default'\nimport {Image} from 'next/dist/client/image-component.js'\nimport {useAuth} from '@payloadcms/ui'\n\n\nexport const Avatar = () => {\n\n const {user} = useAuth()\n\n return (user?.image ?\n <>\n <style>\n {`\n .avatar {\n position: relative;\n height: 2rem;\n width: 2rem;\n }\n \n .avatar:hover {\n filter: brightness(.8);\n }\n \n .avatar img {\n border-radius: 100%;\n }\n `}\n </style>\n <div className=\"avatar\">\n <Image src={user.image} alt=\"Profile Picture\" fill sizes=\"2rem 2rem\"/>\n </div>\n </> :\n <DefaultAccountIcon active={false}/>\n )\n}"],"names":["React","DefaultAccountIcon","Image","useAuth","Avatar","user","image","style","div","className","src","alt","fill","sizes","active"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAO;AAC9B,SAAQC,kBAAkB,QAAO,0CAAyC;AAC1E,SAAQC,KAAK,QAAO,sCAAqC;AACzD,SAAQC,OAAO,QAAO,iBAAgB;AAGtC,OAAO,MAAMC,SAAS;IAElB,MAAM,EAACC,IAAI,EAAC,GAAGF;IAEf,OAAQE,MAAMC,sBACN,wDACI,oBAACC,eACI,CAAC;;;;;;;;;;;;;;wBAcE,CAAC,iBAET,oBAACC;QAAIC,WAAU;qBACX,oBAACP;QAAMQ,KAAKL,KAAKC,KAAK;QAAEK,KAAI;QAAkBC,MAAAA;QAAKC,OAAM;yBAGjE,oBAACZ;QAAmBa,QAAQ;;AAExC,EAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"LoginButton.d.ts","sourceRoot":"","sources":["../../src/components/LoginButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,eAAO,MAAM,WAAW,yBAcvB,CAAA"}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
'use client';
|
2
|
+
import React from 'react';
|
3
|
+
import { Button, useConfig, useTranslation } from '@payloadcms/ui';
|
4
|
+
export const LoginButton = ()=>{
|
5
|
+
const { t } = useTranslation();
|
6
|
+
const { admin: { custom: { zitadel: { label } } } } = useConfig();
|
7
|
+
return /*#__PURE__*/ React.createElement("div", {
|
8
|
+
style: {
|
9
|
+
display: 'flex',
|
10
|
+
justifyContent: 'center'
|
11
|
+
}
|
12
|
+
}, /*#__PURE__*/ React.createElement(Button, {
|
13
|
+
onClick: ()=>open('http://localhost/api/users/authorize', '_self')
|
14
|
+
}, t('oidcPlugin:signIn', {
|
15
|
+
label
|
16
|
+
})));
|
17
|
+
};
|
18
|
+
|
19
|
+
//# sourceMappingURL=LoginButton.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/components/LoginButton.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport {NestedKeysStripped} from '@payloadcms/translations'\nimport {Button, useConfig, useTranslation} from '@payloadcms/ui'\nimport {translations} from '../translations.js'\n\nexport const LoginButton = () => {\n\n const {t} = useTranslation<typeof translations.en, NestedKeysStripped<typeof translations.en>>()\n\n const {admin: {custom: {zitadel: {label}}}} = useConfig()\n\n return (\n <div style={{display: 'flex', justifyContent: 'center'}}>\n <Button onClick={() => open('http://localhost/api/users/authorize', '_self')}>\n {t('oidcPlugin:signIn', {label})}\n </Button>\n </div>\n )\n\n}"],"names":["React","Button","useConfig","useTranslation","LoginButton","t","admin","custom","zitadel","label","div","style","display","justifyContent","onClick","open"],"mappings":"AAAA;AAEA,OAAOA,WAAW,QAAO;AAEzB,SAAQC,MAAM,EAAEC,SAAS,EAAEC,cAAc,QAAO,iBAAgB;AAGhE,OAAO,MAAMC,cAAc;IAEvB,MAAM,EAACC,CAAC,EAAC,GAAGF;IAEZ,MAAM,EAACG,OAAO,EAACC,QAAQ,EAACC,SAAS,EAACC,KAAK,EAAC,EAAC,EAAC,EAAC,GAAGP;IAE9C,qBACI,oBAACQ;QAAIC,OAAO;YAACC,SAAS;YAAQC,gBAAgB;QAAQ;qBAClD,oBAACZ;QAAOa,SAAS,IAAMC,KAAK,wCAAwC;OAC/DV,EAAE,qBAAqB;QAACI;IAAK;AAK9C,EAAC"}
|
@@ -1,4 +1,3 @@
|
|
1
|
-
export { Avatar } from './Avatar
|
2
|
-
export { LoginButton } from './LoginButton
|
3
|
-
export { Session } from './Session/index.js';
|
1
|
+
export { Avatar } from './Avatar.js';
|
2
|
+
export { LoginButton } from './LoginButton.js';
|
4
3
|
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,aAAa,CAAA;AAClC,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA"}
|
package/dist/components/index.js
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
export { Avatar } from './Avatar
|
2
|
-
export { LoginButton } from './LoginButton
|
3
|
-
export { Session } from './Session/index.js';
|
1
|
+
export { Avatar } from './Avatar.js';
|
2
|
+
export { LoginButton } from './LoginButton.js';
|
4
3
|
|
5
4
|
//# sourceMappingURL=index.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/components/index.ts"],"sourcesContent":["export {Avatar} from './Avatar
|
1
|
+
{"version":3,"sources":["../../src/components/index.ts"],"sourcesContent":["export {Avatar} from './Avatar.js'\nexport {LoginButton} from './LoginButton.js'"],"names":["Avatar","LoginButton"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAa;AAClC,SAAQC,WAAW,QAAO,mBAAkB"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/handlers/authorize.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAA;AAwBtC,eAAO,MAAM,SAAS,EAAE,cAuBJ,CAAA"}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
'use server';
|
2
|
+
import { cookies } from 'next/headers.js';
|
3
|
+
import process from 'node:process';
|
4
|
+
import { NextResponse } from 'next/server.js';
|
5
|
+
const genCodeChallenge = async ()=>{
|
6
|
+
const code_verifier = Buffer.from(crypto.getRandomValues(new Uint8Array(24))).toString('base64url');
|
7
|
+
cookies().set({
|
8
|
+
name: 'pkce_code_verifier',
|
9
|
+
value: code_verifier,
|
10
|
+
httpOnly: true,
|
11
|
+
sameSite: 'lax',
|
12
|
+
path: '/',
|
13
|
+
maxAge: 300,
|
14
|
+
secure: process.env.NODE_ENV == 'production'
|
15
|
+
});
|
16
|
+
return Buffer.from(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(code_verifier))).toString('base64url');
|
17
|
+
};
|
18
|
+
export const authorize = async ({ payload: { config: { admin: { custom: { zitadel: { issuerURL, clientId, redirectURL } } } } } })=>NextResponse.redirect(`${issuerURL}/oauth/v2/authorize?${new URLSearchParams({
|
19
|
+
client_id: clientId,
|
20
|
+
redirect_uri: redirectURL,
|
21
|
+
response_type: 'code',
|
22
|
+
scope: 'openid email profile',
|
23
|
+
state: '',
|
24
|
+
code_challenge: await genCodeChallenge(),
|
25
|
+
code_challenge_method: 'S256'
|
26
|
+
}).toString()}`);
|
27
|
+
|
28
|
+
//# sourceMappingURL=authorize.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/handlers/authorize.ts"],"sourcesContent":["'use server'\n\nimport {cookies} from 'next/headers.js'\nimport process from 'node:process'\nimport {PayloadHandler} from 'payload'\nimport {NextResponse} from 'next/server.js'\n\nconst genCodeChallenge = async () => {\n\n const code_verifier = Buffer.from(crypto.getRandomValues(new Uint8Array(24)))\n .toString('base64url')\n\n cookies().set({\n name: 'pkce_code_verifier',\n value: code_verifier,\n httpOnly: true,\n sameSite: 'lax',\n path: '/',\n maxAge: 300,\n secure: process.env.NODE_ENV == 'production'\n })\n\n return Buffer.from(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(code_verifier)))\n .toString('base64url')\n\n}\n\n\nexport const authorize: PayloadHandler = async ({\n payload: {\n config: {\n admin: {\n custom: {\n zitadel: {\n issuerURL,\n clientId,\n redirectURL\n }\n }\n }\n }\n }\n }) =>\n NextResponse.redirect(`${issuerURL}/oauth/v2/authorize?${new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectURL,\n response_type: 'code',\n scope: 'openid email profile',\n state: '',\n code_challenge: await genCodeChallenge(),\n code_challenge_method: 'S256'\n }).toString()}`)"],"names":["cookies","process","NextResponse","genCodeChallenge","code_verifier","Buffer","from","crypto","getRandomValues","Uint8Array","toString","set","name","value","httpOnly","sameSite","path","maxAge","secure","env","NODE_ENV","subtle","digest","TextEncoder","encode","authorize","payload","config","admin","custom","zitadel","issuerURL","clientId","redirectURL","redirect","URLSearchParams","client_id","redirect_uri","response_type","scope","state","code_challenge","code_challenge_method"],"mappings":"AAAA;AAEA,SAAQA,OAAO,QAAO,kBAAiB;AACvC,OAAOC,aAAa,eAAc;AAElC,SAAQC,YAAY,QAAO,iBAAgB;AAE3C,MAAMC,mBAAmB;IAErB,MAAMC,gBAAgBC,OAAOC,IAAI,CAACC,OAAOC,eAAe,CAAC,IAAIC,WAAW,MACnEC,QAAQ,CAAC;IAEdV,UAAUW,GAAG,CAAC;QACVC,MAAM;QACNC,OAAOT;QACPU,UAAU;QACVC,UAAU;QACVC,MAAM;QACNC,QAAQ;QACRC,QAAQjB,QAAQkB,GAAG,CAACC,QAAQ,IAAI;IACpC;IAEA,OAAOf,OAAOC,IAAI,CAAC,MAAMC,OAAOc,MAAM,CAACC,MAAM,CAAC,WAAW,IAAIC,cAAcC,MAAM,CAACpB,iBAC7EM,QAAQ,CAAC;AAElB;AAGA,OAAO,MAAMe,YAA4B,OAAO,EACIC,SAAS,EACLC,QAAQ,EACJC,OAAO,EACHC,QAAQ,EACJC,SAAS,EACLC,SAAS,EACTC,QAAQ,EACRC,WAAW,EACd,EACJ,EACJ,EACJ,EACJ,EACJ,GAC7C/B,aAAagC,QAAQ,CAAC,CAAC,EAAEH,UAAU,oBAAoB,EAAE,IAAII,gBAAgB;QACzEC,WAAWJ;QACXK,cAAcJ;QACdK,eAAe;QACfC,OAAO;QACPC,OAAO;QACPC,gBAAgB,MAAMtC;QACtBuC,uBAAuB;IAC3B,GAAGhC,QAAQ,GAAG,CAAC,EAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/handlers/callback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAA;AAMtC,eAAO,MAAM,QAAQ,EAAE,cAyDtB,CAAA"}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { cookies } from 'next/headers.js';
|
2
|
+
import process from 'node:process';
|
3
|
+
import jwt from 'jsonwebtoken';
|
4
|
+
export const callback = async ({ payload, query: { code } })=>{
|
5
|
+
const { secret, admin: { custom: { zitadel: { issuerURL, clientId, redirectURL } } } } = payload.config;
|
6
|
+
const cookieStore = cookies();
|
7
|
+
const code_verifier = cookieStore.get('pkce_code_verifier')?.value;
|
8
|
+
if (code_verifier) {
|
9
|
+
const response = await fetch(new URL(`${issuerURL}/oauth/v2/token`), {
|
10
|
+
method: 'POST',
|
11
|
+
body: new URLSearchParams({
|
12
|
+
grant_type: 'authorization_code',
|
13
|
+
code: code,
|
14
|
+
redirect_uri: redirectURL,
|
15
|
+
client_id: clientId,
|
16
|
+
code_verifier
|
17
|
+
})
|
18
|
+
});
|
19
|
+
if (response.ok) {
|
20
|
+
const { id_token } = await response.json();
|
21
|
+
if (id_token) {
|
22
|
+
cookieStore.set({
|
23
|
+
name: 'id_token',
|
24
|
+
value: jwt.sign(jwt.decode(id_token), secret),
|
25
|
+
httpOnly: true,
|
26
|
+
path: '/',
|
27
|
+
sameSite: 'strict',
|
28
|
+
maxAge: 900,
|
29
|
+
secure: process.env.NODE_ENV == 'production'
|
30
|
+
});
|
31
|
+
cookieStore.delete('pkce_code_verifier');
|
32
|
+
return Response.redirect(new URL(redirectURL).origin);
|
33
|
+
}
|
34
|
+
return Response.json({
|
35
|
+
status: 'error',
|
36
|
+
message: 'token could not be retrieved from the response'
|
37
|
+
});
|
38
|
+
}
|
39
|
+
return Response.json({
|
40
|
+
status: 'error',
|
41
|
+
message: 'error while communicating with token endpoint'
|
42
|
+
});
|
43
|
+
}
|
44
|
+
return Response.json({
|
45
|
+
status: 'error',
|
46
|
+
message: 'code verifier not found (associated http-only cookie is empty)'
|
47
|
+
});
|
48
|
+
};
|
49
|
+
|
50
|
+
//# sourceMappingURL=callback.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/handlers/callback.ts"],"sourcesContent":["import {PayloadHandler} from 'payload'\nimport {cookies} from 'next/headers.js'\nimport process from 'node:process'\nimport jwt from 'jsonwebtoken'\nimport {ZitadelIdToken} from '../types.js'\n\nexport const callback: PayloadHandler = async ({payload, query: {code}}) => {\n\n const {secret, admin: {custom: {zitadel: {issuerURL, clientId, redirectURL}}}} = payload.config\n\n const cookieStore = cookies()\n\n const code_verifier = cookieStore.get('pkce_code_verifier')?.value\n\n if (code_verifier) {\n\n const response = await fetch(new URL(`${issuerURL}/oauth/v2/token`), {\n method: 'POST',\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code as string,\n redirect_uri: redirectURL,\n client_id: clientId,\n code_verifier\n })\n })\n\n if (response.ok) {\n const {id_token} = await response.json()\n\n if (id_token) {\n cookieStore.set({\n name: 'id_token',\n value: jwt.sign(jwt.decode(id_token) as ZitadelIdToken, secret),\n httpOnly: true,\n path: '/',\n sameSite: 'strict',\n maxAge: 900,\n secure: process.env.NODE_ENV == 'production'\n })\n cookieStore.delete('pkce_code_verifier')\n\n return Response.redirect(new URL(redirectURL).origin)\n }\n\n return Response.json({\n status: 'error',\n message: 'token could not be retrieved from the response'\n })\n }\n\n return Response.json({\n status: 'error',\n message: 'error while communicating with token endpoint'\n })\n\n }\n\n return Response.json({\n status: 'error',\n message: 'code verifier not found (associated http-only cookie is empty)'\n })\n\n}"],"names":["cookies","process","jwt","callback","payload","query","code","secret","admin","custom","zitadel","issuerURL","clientId","redirectURL","config","cookieStore","code_verifier","get","value","response","fetch","URL","method","body","URLSearchParams","grant_type","redirect_uri","client_id","ok","id_token","json","set","name","sign","decode","httpOnly","path","sameSite","maxAge","secure","env","NODE_ENV","delete","Response","redirect","origin","status","message"],"mappings":"AACA,SAAQA,OAAO,QAAO,kBAAiB;AACvC,OAAOC,aAAa,eAAc;AAClC,OAAOC,SAAS,eAAc;AAG9B,OAAO,MAAMC,WAA2B,OAAO,EAACC,OAAO,EAAEC,OAAO,EAACC,IAAI,EAAC,EAAC;IAEnE,MAAM,EAACC,MAAM,EAAEC,OAAO,EAACC,QAAQ,EAACC,SAAS,EAACC,SAAS,EAAEC,QAAQ,EAAEC,WAAW,EAAC,EAAC,EAAC,EAAC,GAAGT,QAAQU,MAAM;IAE/F,MAAMC,cAAcf;IAEpB,MAAMgB,gBAAgBD,YAAYE,GAAG,CAAC,uBAAuBC;IAE7D,IAAIF,eAAe;QAEf,MAAMG,WAAW,MAAMC,MAAM,IAAIC,IAAI,CAAC,EAAEV,UAAU,eAAe,CAAC,GAAG;YACjEW,QAAQ;YACRC,MAAM,IAAIC,gBAAgB;gBACtBC,YAAY;gBACZnB,MAAMA;gBACNoB,cAAcb;gBACdc,WAAWf;gBACXI;YACJ;QACJ;QAEA,IAAIG,SAASS,EAAE,EAAE;YACb,MAAM,EAACC,QAAQ,EAAC,GAAG,MAAMV,SAASW,IAAI;YAEtC,IAAID,UAAU;gBACVd,YAAYgB,GAAG,CAAC;oBACZC,MAAM;oBACNd,OAAOhB,IAAI+B,IAAI,CAAC/B,IAAIgC,MAAM,CAACL,WAA6BtB;oBACxD4B,UAAU;oBACVC,MAAM;oBACNC,UAAU;oBACVC,QAAQ;oBACRC,QAAQtC,QAAQuC,GAAG,CAACC,QAAQ,IAAI;gBACpC;gBACA1B,YAAY2B,MAAM,CAAC;gBAEnB,OAAOC,SAASC,QAAQ,CAAC,IAAIvB,IAAIR,aAAagC,MAAM;YACxD;YAEA,OAAOF,SAASb,IAAI,CAAC;gBACjBgB,QAAQ;gBACRC,SAAS;YACb;QACJ;QAEA,OAAOJ,SAASb,IAAI,CAAC;YACjBgB,QAAQ;YACRC,SAAS;QACb;IAEJ;IAEA,OAAOJ,SAASb,IAAI,CAAC;QACjBgB,QAAQ;QACRC,SAAS;IACb;AAEJ,EAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAA"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/handlers/index.ts"],"sourcesContent":["export {authorize} from './authorize.js'\nexport {callback} from './callback.js'"],"names":["authorize","callback"],"mappings":"AAAA,SAAQA,SAAS,QAAO,iBAAgB;AACxC,SAAQC,QAAQ,QAAO,gBAAe"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAA"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/index.ts"],"sourcesContent":["export {useCurrentUser} from './user.js'"],"names":["useCurrentUser"],"mappings":"AAAA,SAAQA,cAAc,QAAO,YAAW"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/hooks/user.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;CAQ1B,CAAA"}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
'use client';
|
2
|
+
import { usePayloadAPI } from '@payloadcms/ui';
|
3
|
+
export const useCurrentUser = ()=>{
|
4
|
+
const { data: { user }, isError, isLoading } = usePayloadAPI('/api/users/me')[0];
|
5
|
+
return {
|
6
|
+
user,
|
7
|
+
isError,
|
8
|
+
isLoading
|
9
|
+
};
|
10
|
+
};
|
11
|
+
|
12
|
+
//# sourceMappingURL=user.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/user.ts"],"sourcesContent":["'use client'\n\nimport {usePayloadAPI} from '@payloadcms/ui'\n\nexport const useCurrentUser = () => {\n const {data: {user}, isError, isLoading} = usePayloadAPI('/api/users/me')[0]\n\n return {\n user,\n isError,\n isLoading\n }\n}"],"names":["usePayloadAPI","useCurrentUser","data","user","isError","isLoading"],"mappings":"AAAA;AAEA,SAAQA,aAAa,QAAO,iBAAgB;AAE5C,OAAO,MAAMC,iBAAiB;IAC1B,MAAM,EAACC,MAAM,EAACC,IAAI,EAAC,EAAEC,OAAO,EAAEC,SAAS,EAAC,GAAGL,cAAc,gBAAgB,CAAC,EAAE;IAE5E,OAAO;QACHG;QACAC;QACAC;IACJ;AACJ,EAAC"}
|
package/dist/index.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
export
|
1
|
+
import { ZitadelPluginType } from './types.js';
|
2
|
+
export { getCurrentUser } from './utils/index.js';
|
3
|
+
export declare const ZitadelPlugin: ZitadelPluginType;
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAA;AAM5C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,eAAO,MAAM,aAAa,EAAE,iBA8J3B,CAAA"}
|