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.
Files changed (74) hide show
  1. package/README.md +52 -57
  2. package/dist/components/Avatar.d.ts +3 -0
  3. package/dist/components/Avatar.d.ts.map +1 -0
  4. package/dist/components/{Avatar/index.js → Avatar.js} +13 -13
  5. package/dist/components/Avatar.js.map +1 -0
  6. package/dist/components/LoginButton.d.ts +3 -0
  7. package/dist/components/LoginButton.d.ts.map +1 -0
  8. package/dist/components/LoginButton.js +19 -0
  9. package/dist/components/LoginButton.js.map +1 -0
  10. package/dist/components/index.d.ts +2 -3
  11. package/dist/components/index.d.ts.map +1 -1
  12. package/dist/components/index.js +2 -3
  13. package/dist/components/index.js.map +1 -1
  14. package/dist/handlers/authorize.d.ts +3 -0
  15. package/dist/handlers/authorize.d.ts.map +1 -0
  16. package/dist/handlers/authorize.js +28 -0
  17. package/dist/handlers/authorize.js.map +1 -0
  18. package/dist/handlers/callback.d.ts +3 -0
  19. package/dist/handlers/callback.d.ts.map +1 -0
  20. package/dist/handlers/callback.js +50 -0
  21. package/dist/handlers/callback.js.map +1 -0
  22. package/dist/handlers/index.d.ts +3 -0
  23. package/dist/handlers/index.d.ts.map +1 -0
  24. package/dist/handlers/index.js +4 -0
  25. package/dist/handlers/index.js.map +1 -0
  26. package/dist/hooks/index.d.ts +2 -0
  27. package/dist/hooks/index.d.ts.map +1 -0
  28. package/dist/hooks/index.js +3 -0
  29. package/dist/hooks/index.js.map +1 -0
  30. package/dist/hooks/user.d.ts +6 -0
  31. package/dist/hooks/user.d.ts.map +1 -0
  32. package/dist/hooks/user.js +12 -0
  33. package/dist/hooks/user.js.map +1 -0
  34. package/dist/index.d.ts +3 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +132 -135
  37. package/dist/index.js.map +1 -1
  38. package/dist/strategy.d.ts.map +1 -1
  39. package/dist/strategy.js +27 -20
  40. package/dist/strategy.js.map +1 -1
  41. package/dist/translations.d.ts +2 -2
  42. package/dist/translations.js +4 -4
  43. package/dist/translations.js.map +1 -1
  44. package/dist/types.d.ts +14 -26
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/types.js.map +1 -1
  47. package/dist/utils/index.d.ts +2 -0
  48. package/dist/utils/index.d.ts.map +1 -0
  49. package/dist/utils/index.js +3 -0
  50. package/dist/utils/index.js.map +1 -0
  51. package/dist/utils/user.d.ts +5 -0
  52. package/dist/utils/user.d.ts.map +1 -0
  53. package/dist/utils/user.js +15 -0
  54. package/dist/utils/user.js.map +1 -0
  55. package/package.json +17 -12
  56. package/dist/components/Avatar/index.d.ts +0 -5
  57. package/dist/components/Avatar/index.d.ts.map +0 -1
  58. package/dist/components/Avatar/index.js.map +0 -1
  59. package/dist/components/LoginButton/button.d.ts +0 -6
  60. package/dist/components/LoginButton/button.d.ts.map +0 -1
  61. package/dist/components/LoginButton/button.js +0 -19
  62. package/dist/components/LoginButton/button.js.map +0 -1
  63. package/dist/components/LoginButton/index.d.ts +0 -6
  64. package/dist/components/LoginButton/index.d.ts.map +0 -1
  65. package/dist/components/LoginButton/index.js +0 -8
  66. package/dist/components/LoginButton/index.js.map +0 -1
  67. package/dist/components/Session/index.d.ts +0 -4
  68. package/dist/components/Session/index.d.ts.map +0 -1
  69. package/dist/components/Session/index.js +0 -6
  70. package/dist/components/Session/index.js.map +0 -1
  71. package/dist/options.d.ts +0 -3
  72. package/dist/options.d.ts.map +0 -1
  73. package/dist/options.js +0 -54
  74. package/dist/options.js.map +0 -1
package/README.md CHANGED
@@ -2,72 +2,63 @@
2
2
 
3
3
  [![NPM](https://nodei.co/npm/payload-zitadel-plugin.png)](https://npmjs.org/package/payload-zitadel-plugin)
4
4
 
5
- plugin for [Payload CMS](https://payloadcms.com), which enables authentication via Zitadel IdP. It
6
- uses [NextAuth.js](https://next-auth.js.org) under the hood.
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.6
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
- #### zitadel-plugin.ts
22
+ #### payload.config.ts
21
23
 
22
24
  ```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
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
- // if you want to specify the users collection slug
43
- // authSlug: 'users',
29
+ export default buildConfig({
30
+ ...,
31
+ plugins: [
32
+ ZitadelPlugin({
33
+ // URL of your Zitadel instance
34
+ issuerUrl: process.env.ZITADEL_URL,
44
35
 
45
- // if you want to specify the field name for the IdP Id in the users collection
46
- // associatedIdFieldName: 'idp_id'
36
+ // in Zitadel create a new App->Web->PKCE, then copy the Client ID
37
+ clientId: process.env.ZITADEL_CLIENT_ID,
47
38
 
48
- // change the internal name, only if you know what you are doing!!!
49
- // internalProviderName = 'zitadel',
39
+ // interpolation text for the Login Button - "sign in with ..."
40
+ label: 'Zitadel',
50
41
 
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
- })
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
- #### payload.config.ts
48
+ // set to true if you want to use your own custom login button
49
+ // disableDefaultLoginButton: true
63
50
 
64
- ```typescript
65
- import {buildConfig} from 'payload/config'
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
- export default buildConfig({
68
- ...,
69
- plugins: [
70
- zitadelPlugin
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
- ### 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
- ### 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,3 @@
1
+ import * as React from 'react';
2
+ export declare const Avatar: () => React.JSX.Element;
3
+ //# sourceMappingURL=Avatar.d.ts.map
@@ -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
- export const Avatar = ({ active })=>{
8
- const session = useSession();
9
- const imageUrl = session?.data?.user?.image;
10
- return imageUrl ? /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement("style", null, `
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("img", {
27
- src: imageUrl,
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: active
30
+ active: false
31
31
  });
32
32
  };
33
33
 
34
- //# sourceMappingURL=index.js.map
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,3 @@
1
+ import React from 'react';
2
+ export declare const LoginButton: () => React.JSX.Element;
3
+ //# sourceMappingURL=LoginButton.d.ts.map
@@ -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/index.js';
2
- export { LoginButton } from './LoginButton/index.js';
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,mBAAmB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA"}
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"}
@@ -1,5 +1,4 @@
1
- export { Avatar } from './Avatar/index.js';
2
- export { LoginButton } from './LoginButton/index.js';
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/index.js'\nexport {LoginButton} from './LoginButton/index.js'\nexport {Session} from './Session/index.js'"],"names":["Avatar","LoginButton","Session"],"rangeMappings":";;","mappings":"AAAA,SAAQA,MAAM,QAAO,oBAAmB;AACxC,SAAQC,WAAW,QAAO,yBAAwB;AAClD,SAAQC,OAAO,QAAO,qBAAoB"}
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,3 @@
1
+ import { PayloadHandler } from 'payload';
2
+ export declare const authorize: PayloadHandler;
3
+ //# sourceMappingURL=authorize.d.ts.map
@@ -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,3 @@
1
+ import { PayloadHandler } from 'payload';
2
+ export declare const callback: PayloadHandler;
3
+ //# sourceMappingURL=callback.d.ts.map
@@ -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,3 @@
1
+ export { authorize } from './authorize.js';
2
+ export { callback } from './callback.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,4 @@
1
+ export { authorize } from './authorize.js';
2
+ export { callback } from './callback.js';
3
+
4
+ //# sourceMappingURL=index.js.map
@@ -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,2 @@
1
+ export { useCurrentUser } from './user.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ export { useCurrentUser } from './user.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -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,6 @@
1
+ export declare const useCurrentUser: () => {
2
+ user: any;
3
+ isError: boolean;
4
+ isLoading: boolean;
5
+ };
6
+ //# sourceMappingURL=user.d.ts.map
@@ -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 { ZitadelPluginProviderType } from './types.js';
2
- export declare const ZitadelPluginProvider: ZitadelPluginProviderType;
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAA0B,yBAAyB,EAAC,MAAM,YAAY,CAAA;AAI7E,eAAO,MAAM,qBAAqB,EAAE,yBAoKnC,CAAA"}
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"}