payload-zitadel-plugin 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -57
- 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.1
|
15
|
+
pnpm add payload-zitadel-plugin@0.2.1
|
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
|
-
####
|
22
|
+
#### payload.config.ts
|
21
23
|
|
22
24
|
```typescript
|
23
|
-
import {
|
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
|
25
|
+
import {buildConfig} from 'payload/config'
|
26
|
+
import {ZitadelPlugin} from 'payload-zitadel-plugin'
|
38
27
|
|
39
|
-
// set to true if you want users to only be able to sign in via Zitadel
|
40
|
-
// disableLocalStrategy: true,
|
41
28
|
|
42
|
-
|
43
|
-
|
29
|
+
export default buildConfig({
|
30
|
+
...,
|
31
|
+
plugins: [
|
32
|
+
ZitadelPlugin({
|
33
|
+
// URL of your Zitadel instance
|
34
|
+
issuerUrl: process.env.ZITADEL_URL,
|
44
35
|
|
45
|
-
|
46
|
-
|
36
|
+
// in Zitadel create a new App->Web->PKCE, then copy the Client ID
|
37
|
+
clientId: process.env.ZITADEL_CLIENT_ID,
|
47
38
|
|
48
|
-
|
49
|
-
|
39
|
+
// interpolation text for the Login Button - "sign in with ..."
|
40
|
+
label: 'Zitadel',
|
50
41
|
|
51
|
-
|
52
|
-
|
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
|
-
})
|
42
|
+
// set the name of the CustomStrategy in PayloadCMS - usually not necessary
|
43
|
+
// strategyName: 'zitadel'
|
59
44
|
|
60
|
-
|
45
|
+
// set to true if you do not want to use the Zitadel Profile Picture as the Avatar
|
46
|
+
// disableAvatar: true
|
61
47
|
|
62
|
-
|
48
|
+
// set to true if you want to use your own custom login button
|
49
|
+
// disableDefaultLoginButton: true
|
63
50
|
|
64
|
-
|
65
|
-
|
51
|
+
// if you want to specify the field name for the Zitadel User Id in the users collection
|
52
|
+
// associatedIdFieldName: 'idp_id'
|
66
53
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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,23 +101,14 @@ const nextConfig = {
|
|
110
101
|
export default withPayload(nextConfig)
|
111
102
|
```
|
112
103
|
|
113
|
-
###
|
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
|
-
### add profile picture url to accepted Next.js assets
|
104
|
+
### further configuration
|
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
|
|
109
|
+
Also if you want to automatically redirect to Zitadel without asking the user to click on the login button,
|
110
|
+
you have to add the redirect manually to the Next.js config file.
|
111
|
+
|
130
112
|
#### next.config.js
|
131
113
|
|
132
114
|
```typescript
|
@@ -134,6 +116,8 @@ import {withPayload} from '@payloadcms/next/withPayload'
|
|
134
116
|
|
135
117
|
/** @type {import('next').NextConfig} */
|
136
118
|
const nextConfig = {
|
119
|
+
// if Avatar enabled:
|
120
|
+
// allow loading assets like profile pictures from Zitadel
|
137
121
|
images: {
|
138
122
|
remotePatterns: [
|
139
123
|
{
|
@@ -143,7 +127,18 @@ const nextConfig = {
|
|
143
127
|
pathname: '/assets/**'
|
144
128
|
}
|
145
129
|
]
|
130
|
+
},
|
131
|
+
// optional: enable auto-redirect to Zitadel login page if no logged in
|
132
|
+
async redirects() {
|
133
|
+
return [
|
134
|
+
{
|
135
|
+
source: '/admin/login',
|
136
|
+
destination: '/api/users/authorize',
|
137
|
+
permanent: true
|
138
|
+
}
|
139
|
+
]
|
146
140
|
}
|
141
|
+
|
147
142
|
}
|
148
143
|
|
149
144
|
export default withPayload(nextConfig)
|
@@ -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"}
|