From f149f1aebc465af934115d68cfb20b758ca38357 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 15 May 2023 09:23:59 +0200 Subject: [PATCH 01/39] v2 alpha service in @zitadel/server --- apps/login/app/(login)/error.tsx | 2 +- apps/login/app/(login)/register/page.tsx | 16 ++-- apps/login/app/layout.tsx | 4 +- apps/login/lib/zitadel.ts | 44 +++++----- apps/login/package.json | 6 +- packages/zitadel-server/src/index.ts | 3 +- .../zitadel-server/src/v2/settings/index.ts | 6 ++ .../src/v2/settings/settings.ts | 46 ++++++++++ pnpm-lock.yaml | 87 ++++++++++--------- turbo.json | 8 +- 10 files changed, 137 insertions(+), 85 deletions(-) create mode 100644 packages/zitadel-server/src/v2/settings/index.ts create mode 100644 packages/zitadel-server/src/v2/settings/settings.ts diff --git a/apps/login/app/(login)/error.tsx b/apps/login/app/(login)/error.tsx index 899ddad10f7..c0885c16dad 100644 --- a/apps/login/app/(login)/error.tsx +++ b/apps/login/app/(login)/error.tsx @@ -12,7 +12,7 @@ export default function Error({ error, reset }: any) { return (
-
+
Error: {error?.message}
diff --git a/apps/login/app/(login)/register/page.tsx b/apps/login/app/(login)/register/page.tsx index c7b224d74c6..1cde08c52f6 100644 --- a/apps/login/app/(login)/register/page.tsx +++ b/apps/login/app/(login)/register/page.tsx @@ -1,23 +1,25 @@ import { - getPasswordComplexityPolicy, - getPrivacyPolicy, + getLegalAndSupportSettings, + getPasswordComplexitySettings, server, } from "#/lib/zitadel"; import RegisterForm from "#/ui/RegisterForm"; export default async function Page() { - const privacyPolicy = await getPrivacyPolicy(server); - const passwordComplexityPolicy = await getPasswordComplexityPolicy(server); + const legal = await getLegalAndSupportSettings(server); + const passwordComplexitySettings = await getPasswordComplexitySettings( + server + ); return (

Register

Create your ZITADEL account.

- {privacyPolicy && passwordComplexityPolicy && ( + {legal && passwordComplexitySettings && ( )}
diff --git a/apps/login/app/layout.tsx b/apps/login/app/layout.tsx index 749e8184c63..93b649a6a97 100644 --- a/apps/login/app/layout.tsx +++ b/apps/login/app/layout.tsx @@ -6,7 +6,7 @@ import Byline from "#/ui/Byline"; import { LayoutProviders } from "#/ui/LayoutProviders"; import { Analytics } from "@vercel/analytics/react"; import ThemeWrapper from "#/ui/ThemeWrapper"; -import { getBranding } from "#/lib/zitadel"; +import { getBrandingSettings } from "#/lib/zitadel"; import { server } from "../lib/zitadel"; import { LabelPolicyColors } from "#/utils/colors"; @@ -25,7 +25,7 @@ export default async function RootLayout({ // later only shown with dev mode enabled const showNav = true; - const branding = await getBranding(server); + const branding = await getBrandingSettings(server); let partialPolicy: LabelPolicyColors | undefined; console.log(branding); if (branding) { diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 50ac2f39b93..ccf3f9adc07 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -2,14 +2,11 @@ import { management, ZitadelServer, ZitadelServerOptions, - getManagement, orgMetadata, getServer, getServers, - LabelPolicy, initializeServer, - PrivacyPolicy, - PasswordComplexityPolicy, + settings, } from "@zitadel/server"; // import { getAuth } from "@zitadel/server/auth"; @@ -26,46 +23,47 @@ if (!getServers().length) { server = initializeServer(zitadelConfig); } -export function getBranding( +export function getBrandingSettings( server: ZitadelServer -): Promise { - const mgmt = getManagement(server); - return mgmt - .getLabelPolicy( +): Promise { + // settings.branding_settings.BrandingSettings + const settingsService = settings.getSettings(server); + return settingsService + .getBrandingSettings( {}, { // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.policy); + .then((resp) => resp.settings); } -export function getPrivacyPolicy( +export function getLegalAndSupportSettings( server: ZitadelServer -): Promise { - const mgmt = getManagement(server); - return mgmt - .getPrivacyPolicy( +): Promise { + const settingsService = settings.getSettings(server); + return settingsService + .getLegalAndSupportSettings( {}, { // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.policy); + .then((resp) => resp.settings); } -export function getPasswordComplexityPolicy( +export function getPasswordComplexitySettings( server: ZitadelServer -): Promise { - const mgmt = getManagement(server); - return mgmt - .getPasswordComplexityPolicy( +): Promise { + const settingsService = settings.getSettings(server); + return settingsService + .getPasswordComplexitySettings( {}, { // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.policy); + .then((resp) => resp.settings); } export type AddHumanUserData = { @@ -78,7 +76,7 @@ export function addHumanUser( server: ZitadelServer, { email, firstName, lastName, password }: AddHumanUserData ): Promise { - const mgmt = getManagement(server); + const mgmt = management.getManagement(server); return mgmt .addHumanUser( { diff --git a/apps/login/package.json b/apps/login/package.json index ec1d3a2cfe2..4d2d4067987 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -27,7 +27,7 @@ "@zitadel/server": "workspace:*", "clsx": "1.2.1", "date-fns": "2.29.3", - "next": "13.3.2-canary.2", + "next": "13.4.2", "next-themes": "^0.2.1", "nice-grpc": "2.0.1", "react": "18.2.0", @@ -45,7 +45,6 @@ "@types/tinycolor2": "1.4.3", "@vercel/git-hooks": "1.0.0", "@zitadel/tsconfig": "workspace:*", - "zitadel-tailwind-config": "workspace:*", "autoprefixer": "10.4.13", "del-cli": "5.0.0", "eslint-config-zitadel": "workspace:*", @@ -56,6 +55,7 @@ "prettier-plugin-tailwindcss": "0.1.13", "tailwindcss": "3.2.4", "ts-proto": "^1.139.0", - "typescript": "4.8.4" + "typescript": "4.8.4", + "zitadel-tailwind-config": "workspace:*" } } diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index eca81cea9b4..174ae537ff5 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -1,6 +1,7 @@ export * from "./server"; export * from "./middleware"; -export * from "./management"; +export * as management from "./management"; +export * as settings from "./v2/settings"; // export * as auth from "./auth"; // export * as management from "./management"; diff --git a/packages/zitadel-server/src/v2/settings/index.ts b/packages/zitadel-server/src/v2/settings/index.ts new file mode 100644 index 00000000000..e96aa88159a --- /dev/null +++ b/packages/zitadel-server/src/v2/settings/index.ts @@ -0,0 +1,6 @@ +export * from "./settings"; +export * from "../../proto/server/zitadel/settings/v2alpha/settings"; +export * as branding from "../../proto/server/zitadel/settings/v2alpha/branding_settings"; +export * as login from "../../proto/server/zitadel/settings/v2alpha/login_settings"; +export * as password from "../../proto/server/zitadel/settings/v2alpha/password_settings"; +export * as legal from "../../proto/server/zitadel/settings/v2alpha/legal_settings"; diff --git a/packages/zitadel-server/src/v2/settings/settings.ts b/packages/zitadel-server/src/v2/settings/settings.ts new file mode 100644 index 00000000000..4c495d907e4 --- /dev/null +++ b/packages/zitadel-server/src/v2/settings/settings.ts @@ -0,0 +1,46 @@ +import { CompatServiceDefinition } from "nice-grpc/lib/service-definitions"; + +import { createChannel, createClientFactory } from "nice-grpc"; +import { + SettingsServiceClient, + SettingsServiceDefinition, +} from "../../proto/server/zitadel/settings/v2alpha/settings_service"; + +import { authMiddleware } from "../../middleware"; +import { ZitadelServer, getServers } from "../../server"; + +const createClient = ( + definition: CompatServiceDefinition, + apiUrl: string, + token: string +) => { + if (!apiUrl) { + throw Error("ZITADEL_API_URL not set"); + } + + const channel = createChannel(process.env.ZITADEL_API_URL ?? ""); + return createClientFactory() + .use(authMiddleware(token)) + .create(definition, channel) as Client; +}; + +export const getSettings = (server?: string | ZitadelServer) => { + console.log("init settings"); + let config; + if (server && typeof server === "string") { + const apps = getServers(); + config = apps.find((a) => a.name === server)?.config; + } else if (server && typeof server === "object") { + config = server.config; + } + + if (!config) { + throw Error("No ZITADEL server found"); + } + + return createClient( + SettingsServiceDefinition as CompatServiceDefinition, + config.apiUrl, + config.token + ); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 133e4a550b2..81a52d0d98c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,7 +41,7 @@ importers: grpc-tools: 1.11.3 lint-staged: 13.0.3 make-dir-cli: 3.0.0 - next: 13.3.2-canary.2 + next: 13.4.2 next-themes: ^0.2.1 nice-grpc: 2.0.1 postcss: 8.4.21 @@ -65,8 +65,8 @@ importers: '@zitadel/server': link:../../packages/zitadel-server clsx: 1.2.1 date-fns: 2.29.3 - next: 13.3.2-canary.2_krg7tz6h6n3fx3eq7tclunioeu - next-themes: 0.2.1_fbdiuaz6irj67j3n36ky3t2nbi + next: 13.4.2_krg7tz6h6n3fx3eq7tclunioeu + next-themes: 0.2.1_cmp7sjki5xcmfyvhcokzzink7a nice-grpc: 2.0.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -667,8 +667,8 @@ packages: resolution: {integrity: sha512-FN50r/E+b8wuqyRjmGaqvqNDuWBWYWQiigfZ50KnSFH0f+AMQQyaZl+Zm2+CIpKk0fL9QxhLxOpTVA3xFHgFow==} dev: false - /@next/env/13.3.2-canary.2: - resolution: {integrity: sha512-/NqWjXLGlNpGkxPAXR8TDWT6ZYsYGwWNfwhpPhtyMtUOU78wwWiT5p/smGd/+h/PFaIeLjrjtqiA7hHqrw0u0A==} + /@next/env/13.4.2: + resolution: {integrity: sha512-Wqvo7lDeS0KGwtwg9TT9wKQ8raelmUxt+TQKWvG/xKfcmDXNOtCuaszcfCF8JzlBG1q0VhpI6CKaRMbVPMDWgw==} dev: false /@next/eslint-plugin-next/13.3.1: @@ -704,8 +704,8 @@ packages: dev: false optional: true - /@next/swc-darwin-arm64/13.3.2-canary.2: - resolution: {integrity: sha512-HdqGogdJAF88hzmVLhSXu/msxlkv2MP395natN1MmGxjqfTNGLSJewWmPf4vdOBIP54lDc6Nap/b2joYWOrCDw==} + /@next/swc-darwin-arm64/13.4.2: + resolution: {integrity: sha512-6BBlqGu3ewgJflv9iLCwO1v1hqlecaIH2AotpKfVUEzUxuuDNJQZ2a4KLb4MBl8T9/vca1YuWhSqtbF6ZuUJJw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -722,8 +722,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64/13.3.2-canary.2: - resolution: {integrity: sha512-u9LPNpaRXjKi6WPDqhrXEYW3UJxyf3J2mva8fmb3CGZHR8BrkItRDcn7VDgSZ0jTHRHpCGqYXlPE+z6+bVYdeg==} + /@next/swc-darwin-x64/13.4.2: + resolution: {integrity: sha512-iZuYr7ZvGLPjPmfhhMl0ISm+z8EiyLBC1bLyFwGBxkWmPXqdJ60mzuTaDSr5WezDwv0fz32HB7JHmRC6JVHSZg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -758,8 +758,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu/13.3.2-canary.2: - resolution: {integrity: sha512-e/aUm7RZoDcvLHrK7sTiRMX3cS+1LVlN2gUKV9PYrrXGftuQGkIwJyZPUm4nsJUX7ozNWXPU50YeHPvt9K0c2Q==} + /@next/swc-linux-arm64-gnu/13.4.2: + resolution: {integrity: sha512-2xVabFtIge6BJTcJrW8YuUnYTuQjh4jEuRuS2mscyNVOj6zUZkom3CQg+egKOoS+zh2rrro66ffSKIS+ztFJTg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -776,8 +776,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl/13.3.2-canary.2: - resolution: {integrity: sha512-wDvtL9LcN0pSao+M/A3qSYVHvPcyH1H9d0v7aIbwd6F/JuTIlTeXgKuxVCYY5OBNC6dXbzOyGSREZ8hLCx9Wjw==} + /@next/swc-linux-arm64-musl/13.4.2: + resolution: {integrity: sha512-wKRCQ27xCUJx5d6IivfjYGq8oVngqIhlhSAJntgXLt7Uo9sRT/3EppMHqUZRfyuNBTbykEre1s5166z+pvRB5A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -794,8 +794,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu/13.3.2-canary.2: - resolution: {integrity: sha512-Z/GTeCcD6YK92rBdrAa5GVLC9TzXkXpGKnlDLJLm/2oY1eBRTVpQT5/vp0vrRcPYjdHXubizquk1Q3eyAtlKTg==} + /@next/swc-linux-x64-gnu/13.4.2: + resolution: {integrity: sha512-NpCa+UVhhuNeaFVUP1Bftm0uqtvLWq2JTm7+Ta48+2Uqj2mNXrDIvyn1DY/ZEfmW/1yvGBRaUAv9zkMkMRixQA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -812,8 +812,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl/13.3.2-canary.2: - resolution: {integrity: sha512-P0KCzP17aoxfq3k+rtgDhOl8BILdgw3pw8w88/qD5WA2xK2R9Rg4lRI6pAQSro0++ToNDgnrXpRuJov7n1OfeQ==} + /@next/swc-linux-x64-musl/13.4.2: + resolution: {integrity: sha512-ZWVC72x0lW4aj44e3khvBrj2oSYj1bD0jESmyah3zG/3DplEy/FOtYkMzbMjHTdDSheso7zH8GIlW6CDQnKhmQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -830,8 +830,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc/13.3.2-canary.2: - resolution: {integrity: sha512-yGpQpU0To4gp/bjhwKHqu3zVJ/Jco+g4Okv95IWnbYUX7sd14kophZGwHiZN4dLErB9Pdd4vvmz8ccJP5h+Ubg==} + /@next/swc-win32-arm64-msvc/13.4.2: + resolution: {integrity: sha512-pLT+OWYpzJig5K4VKhLttlIfBcVZfr2+Xbjra0Tjs83NQSkFS+y7xx+YhCwvpEmXYLIvaggj2ONPyjbiigOvHQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -848,8 +848,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc/13.3.2-canary.2: - resolution: {integrity: sha512-iHtddC48Xdl7RxCdhBWZ6+1hq/eC0duTR4y3yYPELpXpZnIwGjOT5W5N+3nVRXUVLsj6teRf8fEfWBp3WbJ0RQ==} + /@next/swc-win32-ia32-msvc/13.4.2: + resolution: {integrity: sha512-dhpiksQCyGca4WY0fJyzK3FxMDFoqMb0Cn+uDB+9GYjpU2K5//UGPQlCwiK4JHxuhg8oLMag5Nf3/IPSJNG8jw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -866,8 +866,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc/13.3.2-canary.2: - resolution: {integrity: sha512-Ctw3gL8cBMvREpJM09xvC+pPKsG8TVSWxsQPTLvD33qFED0gtU9HSIacJ09eXd8mqtRGebcXaNjY9fVFfGHZ3A==} + /@next/swc-win32-x64-msvc/13.4.2: + resolution: {integrity: sha512-O7bort1Vld00cu8g0jHZq3cbSTUNMohOEvYqsqE10+yfohhdPHzvzO+ziJRz4Dyyr/fYKREwS7gR4JC0soSOMw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -948,8 +948,8 @@ packages: tslib: 2.4.1 dev: false - /@swc/helpers/0.5.0: - resolution: {integrity: sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==} + /@swc/helpers/0.5.1: + resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: tslib: 2.4.1 dev: false @@ -3608,14 +3608,14 @@ packages: /natural-compare/1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - /next-themes/0.2.1_fbdiuaz6irj67j3n36ky3t2nbi: + /next-themes/0.2.1_cmp7sjki5xcmfyvhcokzzink7a: resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} peerDependencies: next: '*' react: '*' react-dom: '*' dependencies: - next: 13.3.2-canary.2_krg7tz6h6n3fx3eq7tclunioeu + next: 13.4.2_krg7tz6h6n3fx3eq7tclunioeu react: 18.2.0 react-dom: 18.2.0_react@18.2.0 dev: false @@ -3667,9 +3667,9 @@ packages: - babel-plugin-macros dev: false - /next/13.3.2-canary.2_krg7tz6h6n3fx3eq7tclunioeu: - resolution: {integrity: sha512-tAJBdhzzQxzomn2Ge3lR3zCVPBnPSfXy6+fTQTDtZHDQe/pH9xJgnMpwvA8kBYEr5yrCcJn0U3kxeo32LRJUjw==} - engines: {node: '>=14.18.0'} + /next/13.4.2_krg7tz6h6n3fx3eq7tclunioeu: + resolution: {integrity: sha512-aNFqLs3a3nTGvLWlO9SUhCuMUHVPSFQC0+tDNGAsDXqx+WJDFSbvc233gOJ5H19SBc7nw36A9LwQepOJ2u/8Kg==} + engines: {node: '>=16.8.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -3688,8 +3688,8 @@ packages: sass: optional: true dependencies: - '@next/env': 13.3.2-canary.2 - '@swc/helpers': 0.5.0 + '@next/env': 13.4.2 + '@swc/helpers': 0.5.1 busboy: 1.6.0 caniuse-lite: 1.0.30001473 postcss: 8.4.14 @@ -3697,16 +3697,17 @@ packages: react-dom: 18.2.0_react@18.2.0 sass: 1.62.0 styled-jsx: 5.1.1_react@18.2.0 + zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.3.2-canary.2 - '@next/swc-darwin-x64': 13.3.2-canary.2 - '@next/swc-linux-arm64-gnu': 13.3.2-canary.2 - '@next/swc-linux-arm64-musl': 13.3.2-canary.2 - '@next/swc-linux-x64-gnu': 13.3.2-canary.2 - '@next/swc-linux-x64-musl': 13.3.2-canary.2 - '@next/swc-win32-arm64-msvc': 13.3.2-canary.2 - '@next/swc-win32-ia32-msvc': 13.3.2-canary.2 - '@next/swc-win32-x64-msvc': 13.3.2-canary.2 + '@next/swc-darwin-arm64': 13.4.2 + '@next/swc-darwin-x64': 13.4.2 + '@next/swc-linux-arm64-gnu': 13.4.2 + '@next/swc-linux-arm64-musl': 13.4.2 + '@next/swc-linux-x64-gnu': 13.4.2 + '@next/swc-linux-x64-musl': 13.4.2 + '@next/swc-win32-arm64-msvc': 13.4.2 + '@next/swc-win32-ia32-msvc': 13.4.2 + '@next/swc-win32-x64-msvc': 13.4.2 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -5420,3 +5421,7 @@ packages: /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + + /zod/3.21.4: + resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false diff --git a/turbo.json b/turbo.json index c2730faff63..82fcf3b6a8d 100644 --- a/turbo.json +++ b/turbo.json @@ -23,11 +23,5 @@ } }, "globalDependencies": ["**/.env.*local"], - "globalEnv": [ - "ZITADEL_API_URL", - "ZITADEL_PROJECT_ID", - "ZITADEL_APP_ID", - "ZITADEL_SERVICE_USER_TOKEN", - "ZITADEL_ORG_ID" - ] + "globalEnv": ["ZITADEL_API_URL", "ZITADEL_SERVICE_USER_TOKEN"] } From 8a190e28c6aa6efe16198f7cfb07a79368a478bb Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 16 May 2023 17:34:52 +0200 Subject: [PATCH 02/39] session service, username, password form --- .../app/(login)/register/success/page.tsx | 2 - apps/login/app/(login)/username/page.tsx | 30 +------ apps/login/app/layout.tsx | 22 ++--- apps/login/app/session/route.ts | 26 ++++++ apps/login/lib/zitadel.ts | 40 +++++++-- apps/login/ui/PasswordForm.tsx | 84 +++++++++++++++++++ apps/login/ui/RegisterForm.tsx | 17 ++-- apps/login/ui/ThemeWrapper.tsx | 5 +- apps/login/ui/UsernameForm.tsx | 82 ++++++++++++++++++ apps/login/utils/colors.ts | 71 ++++++++-------- packages/zitadel-server/package.json | 5 +- packages/zitadel-server/src/index.ts | 45 +++++++--- .../zitadel-server/src/management/index.ts | 2 +- packages/zitadel-server/src/server.ts | 23 +++++ .../zitadel-server/src/v2/session/index.ts | 2 + .../zitadel-server/src/v2/session/session.ts | 29 +++++++ .../zitadel-server/src/v2/settings/index.ts | 4 - .../src/v2/settings/settings.ts | 19 +---- packages/zitadel-server/tsconfig.json | 7 +- packages/zitadel-server/tsup.config.ts | 13 +++ 20 files changed, 395 insertions(+), 133 deletions(-) create mode 100644 apps/login/app/session/route.ts create mode 100644 apps/login/ui/PasswordForm.tsx create mode 100644 apps/login/ui/UsernameForm.tsx create mode 100644 packages/zitadel-server/src/v2/session/index.ts create mode 100644 packages/zitadel-server/src/v2/session/session.ts create mode 100644 packages/zitadel-server/tsup.config.ts diff --git a/apps/login/app/(login)/register/success/page.tsx b/apps/login/app/(login)/register/success/page.tsx index bf133037e92..baa484a004b 100644 --- a/apps/login/app/(login)/register/success/page.tsx +++ b/apps/login/app/(login)/register/success/page.tsx @@ -1,7 +1,5 @@ import { Button, ButtonVariants } from "#/ui/Button"; -import { NextPage, NextPageContext } from "next"; import Link from "next/link"; -import { useSearchParams } from "next/navigation"; type Props = { searchParams: { [key: string]: string | string[] | undefined }; diff --git a/apps/login/app/(login)/username/page.tsx b/apps/login/app/(login)/username/page.tsx index 86280e0351b..a68482ef906 100644 --- a/apps/login/app/(login)/username/page.tsx +++ b/apps/login/app/(login)/username/page.tsx @@ -2,41 +2,15 @@ import { Button, ButtonVariants } from "#/ui/Button"; import IdentityProviders from "#/ui/IdentityProviders"; -import { TextInput } from "#/ui/Input"; -import { useRouter } from "next/navigation"; +import UsernameForm from "#/ui/UsernameForm"; export default function Page() { - const router = useRouter(); - - function submit() { - router.push("/password"); - } return (

Welcome back!

Enter your login data.

-
submit()}> -
- -
- -
- -
-
- - -
-
+
); } diff --git a/apps/login/app/layout.tsx b/apps/login/app/layout.tsx index 93b649a6a97..9ca933f31e6 100644 --- a/apps/login/app/layout.tsx +++ b/apps/login/app/layout.tsx @@ -8,7 +8,7 @@ import { Analytics } from "@vercel/analytics/react"; import ThemeWrapper from "#/ui/ThemeWrapper"; import { getBrandingSettings } from "#/lib/zitadel"; import { server } from "../lib/zitadel"; -import { LabelPolicyColors } from "#/utils/colors"; +import { BrandingSettings } from "@zitadel/server"; const lato = Lato({ weight: ["400", "700", "900"], @@ -25,26 +25,20 @@ export default async function RootLayout({ // later only shown with dev mode enabled const showNav = true; - const branding = await getBrandingSettings(server); - let partialPolicy: LabelPolicyColors | undefined; - console.log(branding); + // const general = await getGeneralSettings(server); + const branding: BrandingSettings = await getBrandingSettings(server); + let partial: Partial | undefined; if (branding) { - partialPolicy = { - backgroundColor: branding?.backgroundColor, - backgroundColorDark: branding?.backgroundColorDark, - primaryColor: branding?.primaryColor, - primaryColorDark: branding?.primaryColorDark, - warnColor: branding?.warnColor, - warnColorDark: branding?.warnColorDark, - fontColor: branding?.fontColor, - fontColorDark: branding?.fontColorDark, + partial = { + lightTheme: branding?.lightTheme, + darkTheme: branding?.darkTheme, }; } return ( - +
{showNav && } diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts new file mode 100644 index 00000000000..0ee1f7a8368 --- /dev/null +++ b/apps/login/app/session/route.ts @@ -0,0 +1,26 @@ +import { createSession, server, setSession } from "#/lib/zitadel"; +import { NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + const body = await request.json(); + if (body) { + const { loginName } = body; + + const session = await createSession(server, loginName); + return NextResponse.json(session); + } else { + return NextResponse.error(); + } +} + +export async function PUT(request: NextRequest) { + const body = await request.json(); + if (body) { + const { loginName } = body; + + const session = await setSession(server, loginName); + return NextResponse.json(session); + } else { + return NextResponse.error(); + } +} diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index ccf3f9adc07..96476e8e407 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -1,14 +1,12 @@ import { - management, ZitadelServer, ZitadelServerOptions, - orgMetadata, - getServer, + management, + settings, getServers, initializeServer, - settings, + session, } from "@zitadel/server"; -// import { getAuth } from "@zitadel/server/auth"; export const zitadelConfig: ZitadelServerOptions = { name: "zitadel login", @@ -38,6 +36,21 @@ export function getBrandingSettings( .then((resp) => resp.settings); } +export function getGeneralSettings( + server: ZitadelServer +): Promise { + // settings.branding_settings.BrandingSettings + const settingsService = settings.getSettings(server); + return settingsService + .getGeneralSettings( + {}, + { + // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") + } + ) + .then((resp) => resp.supportedLanguages); +} + export function getLegalAndSupportSettings( server: ZitadelServer ): Promise { @@ -56,6 +69,7 @@ export function getPasswordComplexitySettings( server: ZitadelServer ): Promise { const settingsService = settings.getSettings(server); + return settingsService .getPasswordComplexitySettings( {}, @@ -66,6 +80,22 @@ export function getPasswordComplexitySettings( .then((resp) => resp.settings); } +export function createSession( + server: ZitadelServer, + loginName: string +): Promise { + const sessionService = session.getSession(server); + return sessionService.createSession({ checks: { user: { loginName } } }, {}); +} + +export function setSession( + server: ZitadelServer, + loginName: string +): Promise { + const sessionService = session.getSession(server); + return sessionService.setSession({ checks: { user: { loginName } } }, {}); +} + export type AddHumanUserData = { firstName: string; lastName: string; diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx new file mode 100644 index 00000000000..ed994e02015 --- /dev/null +++ b/apps/login/ui/PasswordForm.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { useState } from "react"; +import { Button, ButtonVariants } from "./Button"; +import { TextInput } from "./Input"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import { Spinner } from "./Spinner"; + +type Inputs = { + password: string; +}; + +export default function UsernameForm() { + const { register, handleSubmit, formState } = useForm({ + mode: "onBlur", + }); + + const [error, setError] = useState(""); + + const [loading, setLoading] = useState(false); + + const router = useRouter(); + + async function submitUsername(values: Inputs) { + setLoading(true); + const res = await fetch("/session", { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + password: values.password, + }), + }); + + if (!res.ok) { + setLoading(false); + throw new Error("Failed to register user"); + } + + setLoading(false); + return res.json(); + } + + function submitAndLink(value: Inputs): Promise { + return submitUsername(value).then((resp: any) => { + return router.push(`/password`); + }); + } + + const { errors } = formState; + + return ( +
+
+ +
+ +
+ {/* */} + + +
+
+ ); +} diff --git a/apps/login/ui/RegisterForm.tsx b/apps/login/ui/RegisterForm.tsx index a92724a6e0f..85a114010ee 100644 --- a/apps/login/ui/RegisterForm.tsx +++ b/apps/login/ui/RegisterForm.tsx @@ -1,6 +1,9 @@ "use client"; -import { PasswordComplexityPolicy, PrivacyPolicy } from "@zitadel/server"; +import { + LegalAndSupportSettings, + PasswordComplexitySettings, +} from "@zitadel/server"; import PasswordComplexity from "./PasswordComplexity"; import { useState } from "react"; import { Button, ButtonVariants } from "./Button"; @@ -27,8 +30,8 @@ type Inputs = | FieldValues; type Props = { - privacyPolicy: PrivacyPolicy; - passwordComplexityPolicy: PasswordComplexityPolicy; + privacyPolicy: LegalAndSupportSettings; + passwordComplexityPolicy: PasswordComplexitySettings; }; export default function RegisterForm({ @@ -90,10 +93,10 @@ export default function RegisterForm({ const policyIsValid = passwordComplexityPolicy && - (passwordComplexityPolicy.hasLowercase ? hasLowercase : true) && - (passwordComplexityPolicy.hasNumber ? hasNumber : true) && - (passwordComplexityPolicy.hasUppercase ? hasUppercase : true) && - (passwordComplexityPolicy.hasSymbol ? hasSymbol : true) && + (passwordComplexityPolicy.requiresLowercase ? hasLowercase : true) && + (passwordComplexityPolicy.requiresNumber ? hasNumber : true) && + (passwordComplexityPolicy.requiresUppercase ? hasUppercase : true) && + (passwordComplexityPolicy.requiresSymbol ? hasSymbol : true) && hasMinLength; return ( diff --git a/apps/login/ui/ThemeWrapper.tsx b/apps/login/ui/ThemeWrapper.tsx index b71fd6ef2ec..b8caf797def 100644 --- a/apps/login/ui/ThemeWrapper.tsx +++ b/apps/login/ui/ThemeWrapper.tsx @@ -1,10 +1,11 @@ "use client"; -import { setTheme, LabelPolicyColors } from "#/utils/colors"; +import { BrandingSettings } from "@zitadel/server"; +import { setTheme } from "#/utils/colors"; import { useEffect } from "react"; type Props = { - branding: LabelPolicyColors | undefined; + branding: Partial | undefined; children: React.ReactNode; }; diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx new file mode 100644 index 00000000000..fe05c8b8c4e --- /dev/null +++ b/apps/login/ui/UsernameForm.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { useState } from "react"; +import { Button, ButtonVariants } from "./Button"; +import { TextInput } from "./Input"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import { Spinner } from "./Spinner"; + +type Inputs = { + loginName: string; +}; + +export default function UsernameForm() { + const { register, handleSubmit, formState } = useForm({ + mode: "onBlur", + }); + + const [loading, setLoading] = useState(false); + + const router = useRouter(); + + async function submitUsername(values: Inputs) { + setLoading(true); + const res = await fetch("/session", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + loginName: values.loginName, + }), + }); + + if (!res.ok) { + setLoading(false); + throw new Error("Failed to register user"); + } + + setLoading(false); + return res.json(); + } + + function submitAndLink(value: Inputs): Promise { + return submitUsername(value).then((resp: any) => { + return router.push(`/password`); + }); + } + + const { errors } = formState; + + return ( +
+
+ +
+ +
+ {/* */} + + +
+
+ ); +} diff --git a/apps/login/utils/colors.ts b/apps/login/utils/colors.ts index 161d2f203c7..75b1c8c3568 100644 --- a/apps/login/utils/colors.ts +++ b/apps/login/utils/colors.ts @@ -1,5 +1,7 @@ import tinycolor from "tinycolor2"; +import { BrandingSettings } from "@zitadel/server"; + export interface Color { name: string; hex: string; @@ -52,32 +54,36 @@ export type LabelPolicyColors = { primaryColorDark: string; }; -export function setTheme(document: any, policy?: LabelPolicyColors) { - const lP = { - backgroundColor: BACKGROUND, - backgroundColorDark: DARK_BACKGROUND, - primaryColor: PRIMARY, - primaryColorDark: DARK_PRIMARY, - warnColor: WARN, - warnColorDark: DARK_WARN, - fontColor: TEXT, - fontColorDark: DARK_TEXT, - linkColor: TEXT, - linkColorDark: DARK_TEXT, +type BrandingColors = { + lightTheme: { + backgroundColor: string; + fontColor: string; + primaryColor: string; + warnColor: string; }; + darkTheme: { + backgroundColor: string; + fontColor: string; + primaryColor: string; + warnColor: string; + }; +}; - if (policy) { - lP.backgroundColor = policy.backgroundColor; - lP.backgroundColorDark = policy.backgroundColorDark; - lP.primaryColor = policy.primaryColor; - lP.primaryColorDark = policy.primaryColorDark; - lP.warnColor = policy.warnColor; - lP.warnColorDark = policy.warnColorDark; - lP.fontColor = policy.fontColor; - lP.fontColorDark = policy.fontColorDark; - lP.linkColor = policy.fontColor; - lP.linkColorDark = policy.fontColorDark; - } +export function setTheme(document: any, policy?: Partial) { + const lP: BrandingColors = { + lightTheme: { + backgroundColor: policy?.lightTheme?.backgroundColor ?? BACKGROUND, + fontColor: policy?.lightTheme?.fontColor ?? TEXT, + primaryColor: policy?.lightTheme?.primaryColor ?? PRIMARY, + warnColor: policy?.lightTheme?.warnColor ?? WARN, + }, + darkTheme: { + backgroundColor: policy?.darkTheme?.backgroundColor ?? DARK_BACKGROUND, + fontColor: policy?.darkTheme?.fontColor ?? DARK_TEXT, + primaryColor: policy?.darkTheme?.primaryColor ?? DARK_PRIMARY, + warnColor: policy?.darkTheme?.warnColor ?? DARK_WARN, + }, + }; const dark = computeMap(lP, true); const light = computeMap(lP, false); @@ -177,25 +183,24 @@ function getContrast(color: string): string { } } -export function computeMap( - labelpolicy: LabelPolicyColors, - dark: boolean -): ColorMap { +export function computeMap(branding: BrandingColors, dark: boolean): ColorMap { return { background: computeColors( - dark ? labelpolicy.backgroundColorDark : labelpolicy.backgroundColor + dark + ? branding.darkTheme.backgroundColor + : branding.lightTheme.backgroundColor ), primary: computeColors( - dark ? labelpolicy.primaryColorDark : labelpolicy.primaryColor + dark ? branding.darkTheme.primaryColor : branding.lightTheme.primaryColor ), warn: computeColors( - dark ? labelpolicy.warnColorDark : labelpolicy.warnColor + dark ? branding.darkTheme.warnColor : branding.lightTheme.warnColor ), text: computeColors( - dark ? labelpolicy.fontColorDark : labelpolicy.fontColor + dark ? branding.darkTheme.fontColor : branding.lightTheme.fontColor ), link: computeColors( - dark ? labelpolicy.fontColorDark : labelpolicy.fontColor + dark ? branding.darkTheme.fontColor : branding.lightTheme.fontColor ), }; } diff --git a/packages/zitadel-server/package.json b/packages/zitadel-server/package.json index 32598e1fff5..e211e7c9893 100644 --- a/packages/zitadel-server/package.json +++ b/packages/zitadel-server/package.json @@ -4,14 +4,15 @@ "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", + "type": "commonjs", "sideEffects": false, "license": "MIT", "files": [ "dist/**" ], "scripts": { - "build": "tsup src/index.ts src/*/index.ts --format esm,cjs --dts", - "dev": "tsup src/index.ts src/*/index.ts --format esm,cjs --watch --dts", + "build": "tsup --dts", + "dev": "tsup --dts --watch", "lint": "eslint \"src/**/*.ts*\"", "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", "prebuild": "pnpm run generate", diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index 174ae537ff5..84016632487 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -1,13 +1,36 @@ -export * from "./server"; +import * as management from "./management"; +import * as settings from "./v2/settings"; +import * as session from "./v2/session"; + +import * as login from "./proto/server/zitadel/settings/v2alpha/login_settings"; +import * as password from "./proto/server/zitadel/settings/v2alpha/password_settings"; +import * as legal from "./proto/server/zitadel/settings/v2alpha/legal_settings"; + +export { + BrandingSettings, + Theme, +} from "./proto/server/zitadel/settings/v2alpha/branding_settings"; + +export { type LegalAndSupportSettings } from "./proto/server/zitadel/settings/v2alpha/legal_settings"; +export { type PasswordComplexitySettings } from "./proto/server/zitadel/settings/v2alpha/password_settings"; + +import { + getServers, + initializeServer, + ZitadelServer, + ZitadelServerOptions, +} from "./server"; export * from "./middleware"; -export * as management from "./management"; -export * as settings from "./v2/settings"; -// export * as auth from "./auth"; -// export * as management from "./management"; -// export * as admin from "./admin"; -// export * as system from "./system"; - -// export * from "./proto/server/zitadel/management"; -// export * from "./proto/server/zitadel/system"; -// export * from "./proto/server/zitadel/admin"; +export { + getServers, + ZitadelServer, + type ZitadelServerOptions, + initializeServer, + management, + session, + settings, + login, + password, + legal, +}; diff --git a/packages/zitadel-server/src/management/index.ts b/packages/zitadel-server/src/management/index.ts index a5567f3802f..5df1ddd3192 100644 --- a/packages/zitadel-server/src/management/index.ts +++ b/packages/zitadel-server/src/management/index.ts @@ -1,3 +1,3 @@ export * from "./management"; export * as management from "../proto/server/zitadel/management"; -export * from "../proto/server/zitadel/policy"; +export * as policy from "../proto/server/zitadel/policy"; diff --git a/packages/zitadel-server/src/server.ts b/packages/zitadel-server/src/server.ts index 2a6d2da456c..a74e55189ff 100644 --- a/packages/zitadel-server/src/server.ts +++ b/packages/zitadel-server/src/server.ts @@ -1,3 +1,11 @@ +import { createChannel, createClientFactory } from "nice-grpc"; +import { + SettingsServiceClient, + SettingsServiceDefinition, +} from "./proto/server/zitadel/settings/v2alpha/settings_service"; +import { authMiddleware } from "./middleware"; +import { CompatServiceDefinition } from "nice-grpc/lib/service-definitions"; + let apps: ZitadelServer[] = []; export interface ZitadelServerProps { @@ -49,3 +57,18 @@ export function getServer(name?: string): ZitadelServer { } } } + +export const createClient = ( + definition: CompatServiceDefinition, + apiUrl: string, + token: string +) => { + if (!apiUrl) { + throw Error("ZITADEL_API_URL not set"); + } + + const channel = createChannel(process.env.ZITADEL_API_URL ?? ""); + return createClientFactory() + .use(authMiddleware(token)) + .create(definition, channel) as Client; +}; diff --git a/packages/zitadel-server/src/v2/session/index.ts b/packages/zitadel-server/src/v2/session/index.ts new file mode 100644 index 00000000000..cc86a9f3a0d --- /dev/null +++ b/packages/zitadel-server/src/v2/session/index.ts @@ -0,0 +1,2 @@ +export * from "./session"; +export * from "../../proto/server/zitadel/session/v2alpha/session"; diff --git a/packages/zitadel-server/src/v2/session/session.ts b/packages/zitadel-server/src/v2/session/session.ts new file mode 100644 index 00000000000..493cf4091a6 --- /dev/null +++ b/packages/zitadel-server/src/v2/session/session.ts @@ -0,0 +1,29 @@ +import { CompatServiceDefinition } from "nice-grpc/lib/service-definitions"; + +import { + SessionServiceClient, + SessionServiceDefinition, +} from "../../proto/server/zitadel/session/v2alpha/session_service"; + +import { ZitadelServer, createClient, getServers } from "../../server"; + +export const getSession = (server?: string | ZitadelServer) => { + console.log("init session"); + let config; + if (server && typeof server === "string") { + const apps = getServers(); + config = apps.find((a) => a.name === server)?.config; + } else if (server && typeof server === "object") { + config = server.config; + } + + if (!config) { + throw Error("No ZITADEL server found"); + } + + return createClient( + SessionServiceDefinition as CompatServiceDefinition, + config.apiUrl, + config.token + ); +}; diff --git a/packages/zitadel-server/src/v2/settings/index.ts b/packages/zitadel-server/src/v2/settings/index.ts index e96aa88159a..3f58216cf15 100644 --- a/packages/zitadel-server/src/v2/settings/index.ts +++ b/packages/zitadel-server/src/v2/settings/index.ts @@ -1,6 +1,2 @@ export * from "./settings"; export * from "../../proto/server/zitadel/settings/v2alpha/settings"; -export * as branding from "../../proto/server/zitadel/settings/v2alpha/branding_settings"; -export * as login from "../../proto/server/zitadel/settings/v2alpha/login_settings"; -export * as password from "../../proto/server/zitadel/settings/v2alpha/password_settings"; -export * as legal from "../../proto/server/zitadel/settings/v2alpha/legal_settings"; diff --git a/packages/zitadel-server/src/v2/settings/settings.ts b/packages/zitadel-server/src/v2/settings/settings.ts index 4c495d907e4..e3cc156e7e9 100644 --- a/packages/zitadel-server/src/v2/settings/settings.ts +++ b/packages/zitadel-server/src/v2/settings/settings.ts @@ -1,28 +1,11 @@ import { CompatServiceDefinition } from "nice-grpc/lib/service-definitions"; -import { createChannel, createClientFactory } from "nice-grpc"; import { SettingsServiceClient, SettingsServiceDefinition, } from "../../proto/server/zitadel/settings/v2alpha/settings_service"; -import { authMiddleware } from "../../middleware"; -import { ZitadelServer, getServers } from "../../server"; - -const createClient = ( - definition: CompatServiceDefinition, - apiUrl: string, - token: string -) => { - if (!apiUrl) { - throw Error("ZITADEL_API_URL not set"); - } - - const channel = createChannel(process.env.ZITADEL_API_URL ?? ""); - return createClientFactory() - .use(authMiddleware(token)) - .create(definition, channel) as Client; -}; +import { ZitadelServer, createClient, getServers } from "../../server"; export const getSettings = (server?: string | ZitadelServer) => { console.log("init settings"); diff --git a/packages/zitadel-server/tsconfig.json b/packages/zitadel-server/tsconfig.json index d64c667c7f6..3d12dfd6278 100644 --- a/packages/zitadel-server/tsconfig.json +++ b/packages/zitadel-server/tsconfig.json @@ -3,12 +3,7 @@ "include": ["src/**/*.ts"], "compilerOptions": { "baseUrl": ".", - "rootDir": "src", - "paths": { - "#": ["."], - "*": ["./*"], - "#/*": ["./*"] - } + "rootDir": "src" }, "exclude": ["dist", "build", "node_modules"] } diff --git a/packages/zitadel-server/tsup.config.ts b/packages/zitadel-server/tsup.config.ts new file mode 100644 index 00000000000..2341f37cd49 --- /dev/null +++ b/packages/zitadel-server/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + treeshake: true, + splitting: true, + publicDir: true, + entry: ["src/index.ts", "src/**/index.ts"], + format: ["esm", "cjs"], + dts: true, + minify: true, + clean: true, + ...options, +})); From d3562b1f63c032fd7a9f8b06f59c411e345ad95a Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 17 May 2023 13:46:44 +0200 Subject: [PATCH 03/39] cookie and session handling, password UI --- apps/login/app/(login)/password/page.tsx | 61 ++++--- apps/login/app/(login)/username/page.tsx | 3 - apps/login/app/session/route.ts | 91 ++++++++++- apps/login/lib/zitadel.ts | 18 ++- apps/login/next.config.js | 3 +- apps/login/ui/Avatar.tsx | 22 +-- apps/login/ui/PasswordForm.tsx | 15 +- apps/login/ui/UserAvatar.tsx | 24 ++- apps/login/ui/UsernameForm.tsx | 9 +- apps/login/utils/cookies.ts | 152 ++++++++++++++++++ .../zitadel-server/src/v2/session/session.ts | 1 - 11 files changed, 338 insertions(+), 61 deletions(-) create mode 100644 apps/login/utils/cookies.ts diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index c0c86096e8c..c6a36ea1ca3 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -1,32 +1,51 @@ -"use client"; -import { Button, ButtonVariants } from "#/ui/Button"; -import { TextInput } from "#/ui/Input"; +import { getSession, server } from "#/lib/zitadel"; +import PasswordForm from "#/ui/PasswordForm"; import UserAvatar from "#/ui/UserAvatar"; -import { useRouter } from "next/navigation"; +import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; -export default function Page() { - const router = useRouter(); +async function loadSession(loginName: string) { + const recent = await getMostRecentCookieWithLoginname(loginName); + console.log("found recent cookie: ", recent); + + return getSession(server, recent.id, recent.token).then(({ session }) => { + console.log(session); + return session; + }); + // const res = await fetch( + // `http://localhost:3000/session?` + + // new URLSearchParams({ + // loginName: loginName, + // }), + // { + // method: "GET", + // headers: { + // "Content-Type": "application/json", + // }, + // } + // ); + + // if (!res.ok) { + // throw new Error("Failed to load session"); + // } + + // return res.json(); +} + +export default async function Page({ searchParams }: { searchParams: any }) { + const { loginName } = searchParams; + console.log(loginName); + + const sessionFactors = await loadSession(loginName); + console.log(sessionFactors); return (
-

Password

+

{sessionFactors.factors.user.displayName}

Enter your password.

- + -
- -
- -
- - -
+
); } diff --git a/apps/login/app/(login)/username/page.tsx b/apps/login/app/(login)/username/page.tsx index a68482ef906..156949dab9c 100644 --- a/apps/login/app/(login)/username/page.tsx +++ b/apps/login/app/(login)/username/page.tsx @@ -1,7 +1,4 @@ "use client"; - -import { Button, ButtonVariants } from "#/ui/Button"; -import IdentityProviders from "#/ui/IdentityProviders"; import UsernameForm from "#/ui/UsernameForm"; export default function Page() { diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index 0ee1f7a8368..934bdccb1c7 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -1,4 +1,11 @@ -import { createSession, server, setSession } from "#/lib/zitadel"; +import { createSession, getSession, server, setSession } from "#/lib/zitadel"; +import { + SessionCookie, + addSessionToCookie, + getMostRecentCookieWithLoginname, + getMostRecentSessionCookie, + updateSessionCookie, +} from "#/utils/cookies"; import { NextRequest, NextResponse } from "next/server"; export async function POST(request: NextRequest) { @@ -6,21 +13,93 @@ export async function POST(request: NextRequest) { if (body) { const { loginName } = body; - const session = await createSession(server, loginName); - return NextResponse.json(session); + const createdSession = await createSession(server, loginName); + + return getSession( + server, + createdSession.sessionId, + createdSession.sessionToken + ).then(({ session }) => { + console.log(session); + const sessionCookie: SessionCookie = { + id: createdSession.sessionId, + token: createdSession.sessionToken, + changeDate: session.changeDate, + loginName: session.factors.user.loginName, + }; + return addSessionToCookie(sessionCookie).then(() => { + return NextResponse.json({ factors: session.factors }); + }); + }); } else { return NextResponse.error(); } } +/** + * + * @param request password for the most recent session + * @returns the updated most recent Session with the added password + */ export async function PUT(request: NextRequest) { const body = await request.json(); if (body) { - const { loginName } = body; + const { password } = body; - const session = await setSession(server, loginName); - return NextResponse.json(session); + const recent = await getMostRecentSessionCookie(); + console.log("found recent cookie: ", recent); + const session = await setSession(server, recent.id, recent.token, password); + console.log("updatedsession", session); + + const sessionCookie: SessionCookie = { + id: recent.id, + token: session.sessionToken, + changeDate: session.details.changeDate, + loginName: recent.loginName, + }; + + return getSession(server, sessionCookie.id, sessionCookie.token).then( + ({ session }) => { + console.log(session); + const newCookie: SessionCookie = { + id: sessionCookie.id, + token: sessionCookie.token, + changeDate: session.changeDate, + loginName: session.factors.user.loginName, + }; + // return addSessionToCookie(sessionCookie).then(() => { + // return NextResponse.json({ factors: session.factors }); + // }); + return updateSessionCookie(sessionCookie.id, sessionCookie).then(() => { + console.log("updatedRecent:", sessionCookie); + return NextResponse.json({ factors: session.factors }); + }); + } + ); } else { return NextResponse.error(); } } + +// /** +// * +// * @param request loginName of a session +// * @returns the session +// */ +// export async function GET(request: NextRequest) { +// console.log(request); +// if (request) { +// const { loginName } = request.params; + +// const recent = await getMostRecentCookieWithLoginname(loginName); +// console.log("found recent cookie: ", recent); + +// return getSession(server, recent.id, recent.token).then(({ session }) => { +// console.log(session); + +// return NextResponse.json({ factors: session.factors }); +// }); +// } else { +// return NextResponse.error(); +// } +// } diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 96476e8e407..6311d3ef6a6 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -90,10 +90,24 @@ export function createSession( export function setSession( server: ZitadelServer, - loginName: string + sessionId: string, + sessionToken: string, + password: string ): Promise { const sessionService = session.getSession(server); - return sessionService.setSession({ checks: { user: { loginName } } }, {}); + return sessionService.setSession( + { sessionId, sessionToken, checks: { password: { password } } }, + {} + ); +} + +export function getSession( + server: ZitadelServer, + sessionId: string, + sessionToken: string +): Promise { + const sessionService = session.getSession(server); + return sessionService.getSession({ sessionId, sessionToken }, {}); } export type AddHumanUserData = { diff --git a/apps/login/next.config.js b/apps/login/next.config.js index 2f60c3de82e..47304cc9a2d 100755 --- a/apps/login/next.config.js +++ b/apps/login/next.config.js @@ -3,8 +3,7 @@ const nextConfig = { reactStrictMode: true, // Recommended for the `pages` directory, default in `app`. swcMinify: true, experimental: { - // Required: - appDir: true, + serverActions: true, }, images: { remotePatterns: [ diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 7275da2b00f..25778cdc5eb 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -1,4 +1,4 @@ -import { Color, ColorShade, getColorHash } from "#/utils/colors"; +import { ColorShade, getColorHash } from "#/utils/colors"; import { useTheme } from "next-themes"; import { FC } from "react"; @@ -23,7 +23,7 @@ export const Avatar: FC = ({ imageUrl, shadow, }) => { - const { resolvedTheme } = useTheme(); + // const { resolvedTheme } = useTheme(); let credentials = ""; if (name) { @@ -46,15 +46,15 @@ export const Avatar: FC = ({ const color: ColorShade = getColorHash(loginName); - const avatarStyleDark = { - backgroundColor: color[900], - color: color[200], - }; + // const avatarStyleDark = { + // backgroundColor: color[900], + // color: color[200], + // }; - const avatarStyleLight = { - backgroundColor: color[200], - color: color[900], - }; + // const avatarStyleLight = { + // backgroundColor: color[200], + // color: color[900], + // }; return (
= ({ ? "w-[32px] h-[32px] font-bold" : "" }`} - style={resolvedTheme === "light" ? avatarStyleLight : avatarStyleDark} + // style={resolvedTheme === "light" ? avatarStyleLight : avatarStyleDark} > {imageUrl ? ( ({ mode: "onBlur", }); @@ -22,7 +22,7 @@ export default function UsernameForm() { const router = useRouter(); - async function submitUsername(values: Inputs) { + async function submitPassword(values: Inputs) { setLoading(true); const res = await fetch("/session", { method: "PUT", @@ -43,9 +43,10 @@ export default function UsernameForm() { return res.json(); } - function submitAndLink(value: Inputs): Promise { - return submitUsername(value).then((resp: any) => { - return router.push(`/password`); + function submitPasswordAndContinue(value: Inputs): Promise { + console.log(value); + return submitPassword(value).then((resp: any) => { + return router.push(`/success`); }); } @@ -58,7 +59,7 @@ export default function UsernameForm() { type="password" autoComplete="password" {...register("password", { required: "This field is required" })} - label="Loginname" + label="Password" // error={errors.username?.message as string} />
@@ -73,7 +74,7 @@ export default function UsernameForm() { className="self-end" variant={ButtonVariants.Primary} disabled={loading || !formState.isValid} - onClick={handleSubmit(submitAndLink)} + onClick={handleSubmit(submitPasswordAndContinue)} > {loading && } continue diff --git a/apps/login/ui/UserAvatar.tsx b/apps/login/ui/UserAvatar.tsx index fbf3b3886e5..d64679367f5 100644 --- a/apps/login/ui/UserAvatar.tsx +++ b/apps/login/ui/UserAvatar.tsx @@ -1,16 +1,32 @@ import { Avatar, AvatarSize } from "#/ui/Avatar"; +import { ChevronDownIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; type Props = { - name: string; + loginName: string; + showDropdown: boolean; }; -export default function UserAvatar({ name }: Props) { +export default function UserAvatar({ loginName, showDropdown }: Props) { return (
- +
- {name} + {loginName} + + {showDropdown && ( + + + + )}
); } diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx index fe05c8b8c4e..4c2d9cf1b55 100644 --- a/apps/login/ui/UsernameForm.tsx +++ b/apps/login/ui/UsernameForm.tsx @@ -41,9 +41,10 @@ export default function UsernameForm() { return res.json(); } - function submitAndLink(value: Inputs): Promise { - return submitUsername(value).then((resp: any) => { - return router.push(`/password`); + function submitUsernameAndContinue(value: Inputs): Promise { + return submitUsername(value).then(({ factors }) => { + console.log(factors); + return router.push(`/password?loginName=${factors.user.loginName}`); }); } @@ -71,7 +72,7 @@ export default function UsernameForm() { className="self-end" variant={ButtonVariants.Primary} disabled={loading || !formState.isValid} - onClick={handleSubmit(submitAndLink)} + onClick={handleSubmit(submitUsernameAndContinue)} > {loading && } continue diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts new file mode 100644 index 00000000000..3dad52bb67e --- /dev/null +++ b/apps/login/utils/cookies.ts @@ -0,0 +1,152 @@ +"use server"; + +import { cookies } from "next/headers"; + +export type SessionCookie = { + id: string; + token: string; + loginName: string; + changeDate: string; +}; + +async function set(sessions: SessionCookie[]) { + const cookiesList = cookies(); + // @ts-ignore + cookiesList.set({ + name: "sessions", + value: JSON.stringify(sessions), + httpOnly: true, + path: "/", + }); +} + +export async function addSessionToCookie(session: SessionCookie): Promise { + const cookiesList = cookies(); + // const hasSessions = cookiesList.has("sessions"); + // if (hasSessions) { + const stringifiedCookie = cookiesList.get("sessions"); + + const currentSessions: SessionCookie[] = stringifiedCookie?.value + ? JSON.parse(stringifiedCookie?.value) + : []; + + // @ts-ignore + return cookiesList.set({ + name: "sessions", + value: JSON.stringify([...currentSessions, session]), + httpOnly: true, + path: "/", + }); + // } else { + // return set([session]); + // } +} + +export async function updateSessionCookie( + id: string, + session: SessionCookie +): Promise { + const cookiesList = cookies(); + // const hasSessions = cookiesList.has("sessions"); + // if (hasSessions) { + const stringifiedCookie = cookiesList.get("sessions"); + + const sessions: SessionCookie[] = stringifiedCookie?.value + ? JSON.parse(stringifiedCookie?.value) + : [session]; + + const foundIndex = sessions.findIndex((session) => session.id === id); + sessions[foundIndex] = session; + + // @ts-ignore + return cookiesList.set({ + name: "sessions", + value: JSON.stringify(sessions), + httpOnly: true, + path: "/", + }); + // } else { + // return Promise.reject(); + // } +} + +export async function removeSessionFromCookie( + session: SessionCookie +): Promise { + const cookiesList = cookies(); + // const hasSessions = cookiesList.has("sessions"); + // if (hasSessions) { + const stringifiedCookie = cookiesList.get("sessions"); + + const sessions: SessionCookie[] = stringifiedCookie?.value + ? JSON.parse(stringifiedCookie?.value) + : [session]; + + const filteredSessions = sessions.filter( + (session) => session.id !== session.id + ); + + // @ts-ignore + return cookiesList.set({ + name: "sessions", + value: JSON.stringify(filteredSessions), + httpOnly: true, + path: "/", + }); + // } else { + // return Promise.reject(); + // } +} + +export async function getMostRecentSessionCookie(): Promise { + const cookiesList = cookies(); + // const hasSessions = cookiesList.has("sessions"); + // if (hasSessions) { + const stringifiedCookie = cookiesList.get("sessions"); + + if (stringifiedCookie?.value) { + const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); + + console.log(sessions); + const latest = sessions.reduce((prev, current) => { + return new Date(prev.changeDate).getTime() > + new Date(current.changeDate).getTime() + ? prev + : current; + }); + + return latest; + } else { + return Promise.reject(); + } + // } else { + // return Promise.reject(); + // } +} + +export async function getMostRecentCookieWithLoginname( + loginName: string +): Promise { + const cookiesList = cookies(); + + const stringifiedCookie = cookiesList.get("sessions"); + + if (stringifiedCookie?.value) { + const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); + + const latest = sessions + .filter((cookie) => cookie.loginName === loginName) + .reduce((prev, current) => { + return new Date(prev.changeDate).getTime() > + new Date(current.changeDate).getTime() + ? prev + : current; + }); + + return latest; + } else { + return Promise.reject(); + } +} + +export async function clearSessions() {} diff --git a/packages/zitadel-server/src/v2/session/session.ts b/packages/zitadel-server/src/v2/session/session.ts index 493cf4091a6..4569fa4db8d 100644 --- a/packages/zitadel-server/src/v2/session/session.ts +++ b/packages/zitadel-server/src/v2/session/session.ts @@ -8,7 +8,6 @@ import { import { ZitadelServer, createClient, getServers } from "../../server"; export const getSession = (server?: string | ZitadelServer) => { - console.log("init session"); let config; if (server && typeof server === "string") { const apps = getServers(); From 202e0b7635ba96f9d6bdf0376e1226d9389e21ad Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 17 May 2023 15:25:25 +0200 Subject: [PATCH 04/39] accounts page, list all sessions --- apps/login/app/(login)/accounts/page.tsx | 92 ++++++++++++++++++++++++ apps/login/app/(login)/password/page.tsx | 31 ++------ apps/login/lib/zitadel.ts | 11 +++ apps/login/package.json | 1 + apps/login/ui/Avatar.tsx | 10 ++- apps/login/ui/PasswordForm.tsx | 2 +- apps/login/ui/UserAvatar.tsx | 9 ++- apps/login/utils/cookies.ts | 26 +++++-- pnpm-lock.yaml | 6 ++ 9 files changed, 152 insertions(+), 36 deletions(-) create mode 100644 apps/login/app/(login)/accounts/page.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx new file mode 100644 index 00000000000..07f330926d2 --- /dev/null +++ b/apps/login/app/(login)/accounts/page.tsx @@ -0,0 +1,92 @@ +import { listSessions, server } from "#/lib/zitadel"; +import { Avatar, AvatarSize } from "#/ui/Avatar"; +import { getAllSessionIds } from "#/utils/cookies"; +import { + ChevronRightIcon, + ExclamationTriangleIcon, + XCircleIcon, +} from "@heroicons/react/24/outline"; +import moment from "moment"; +import Link from "next/link"; + +async function loadSessions() { + const ids = await getAllSessionIds().catch((error) => { + console.log("err", error); + }); + + if (ids && ids.length) { + return listSessions( + server, + ids.filter((id: string | undefined) => !!id) + ).then((sessions) => { + console.log("ss", sessions.sessions); + return sessions; + }); + } else { + return []; + } +} + +export default async function Page() { + const { sessions } = await loadSessions(); + + return ( +
+

Accounts

+

Use your ZITADEL Account

+ +
+ {sessions ? ( + sessions.map((session: any) => { + return ( + +
+ +
+ +
+ {session.factors.user.displayName} + + {session.factors.user.loginName} + + {session.factors.password?.verifiedAt && ( + + {moment( + new Date(session.factors.password.verifiedAt) + ).fromNow()} + + )} +
+ + +
+ {session.factors.password?.verifiedAt ? ( +
+ ) : ( +
+ )} + + +
+ + ); + }) + ) : ( +
+ + No Sessions available! +
+ )} +
+
+ ); +} diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index c6a36ea1ca3..277fea63e33 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -5,45 +5,28 @@ import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; async function loadSession(loginName: string) { const recent = await getMostRecentCookieWithLoginname(loginName); - console.log("found recent cookie: ", recent); return getSession(server, recent.id, recent.token).then(({ session }) => { - console.log(session); + console.log("ss", session); return session; }); - // const res = await fetch( - // `http://localhost:3000/session?` + - // new URLSearchParams({ - // loginName: loginName, - // }), - // { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // } - // ); - - // if (!res.ok) { - // throw new Error("Failed to load session"); - // } - - // return res.json(); } export default async function Page({ searchParams }: { searchParams: any }) { const { loginName } = searchParams; - console.log(loginName); const sessionFactors = await loadSession(loginName); - console.log(sessionFactors); return (
-

{sessionFactors.factors.user.displayName}

+

{sessionFactors.factors?.user?.displayName ?? "Password"}

Enter your password.

- +
diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 6311d3ef6a6..440dbbf22de 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -110,6 +110,17 @@ export function getSession( return sessionService.getSession({ sessionId, sessionToken }, {}); } +export function listSessions( + server: ZitadelServer, + ids: string[] +): Promise { + const sessionService = session.getSession(server); + const query = { offset: 0, limit: 100, asc: true }; + console.log(ids); + const queries = [{ idsQuery: { ids } }]; + return sessionService.listSessions({ queries: queries }, {}); +} + export type AddHumanUserData = { firstName: string; lastName: string; diff --git a/apps/login/package.json b/apps/login/package.json index 4d2d4067987..68d92b2991e 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -27,6 +27,7 @@ "@zitadel/server": "workspace:*", "clsx": "1.2.1", "date-fns": "2.29.3", + "moment": "^2.29.4", "next": "13.4.2", "next-themes": "^0.2.1", "nice-grpc": "2.0.1", diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 25778cdc5eb..8f69f31e0b8 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -26,10 +26,16 @@ export const Avatar: FC = ({ // const { resolvedTheme } = useTheme(); let credentials = ""; + console.log(name, loginName); if (name) { const split = name.split(" "); - const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : ""); - credentials = initials; + if (split) { + const initials = + split[0].charAt(0) + (split[1] ? split[1].charAt(0) : ""); + credentials = initials; + } else { + return name.charAt(0); + } } else { const username = loginName.split("@")[0]; let separator = "_"; diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx index e57ba884852..e46114ff609 100644 --- a/apps/login/ui/PasswordForm.tsx +++ b/apps/login/ui/PasswordForm.tsx @@ -46,7 +46,7 @@ export default function PasswordForm() { function submitPasswordAndContinue(value: Inputs): Promise { console.log(value); return submitPassword(value).then((resp: any) => { - return router.push(`/success`); + return router.push(`/accounts`); }); } diff --git a/apps/login/ui/UserAvatar.tsx b/apps/login/ui/UserAvatar.tsx index d64679367f5..332de5feadd 100644 --- a/apps/login/ui/UserAvatar.tsx +++ b/apps/login/ui/UserAvatar.tsx @@ -4,16 +4,21 @@ import Link from "next/link"; type Props = { loginName: string; + displayName?: string; showDropdown: boolean; }; -export default function UserAvatar({ loginName, showDropdown }: Props) { +export default function UserAvatar({ + loginName, + displayName, + showDropdown, +}: Props) { return (
diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 3dad52bb67e..45579193efe 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -100,8 +100,6 @@ export async function removeSessionFromCookie( export async function getMostRecentSessionCookie(): Promise { const cookiesList = cookies(); - // const hasSessions = cookiesList.has("sessions"); - // if (hasSessions) { const stringifiedCookie = cookiesList.get("sessions"); if (stringifiedCookie?.value) { @@ -119,13 +117,27 @@ export async function getMostRecentSessionCookie(): Promise { } else { return Promise.reject(); } - // } else { - // return Promise.reject(); - // } } +export async function getAllSessionIds(): Promise { + const cookiesList = cookies(); + const stringifiedCookie = cookiesList.get("sessions"); + + if (stringifiedCookie?.value) { + const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); + return sessions.map((session) => session.id); + } else { + return Promise.reject(); + } +} + +/** + * Returns most recent session filtered by optinal loginName + * @param loginName + * @returns most recent session + */ export async function getMostRecentCookieWithLoginname( - loginName: string + loginName?: string ): Promise { const cookiesList = cookies(); @@ -135,7 +147,7 @@ export async function getMostRecentCookieWithLoginname( const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); const latest = sessions - .filter((cookie) => cookie.loginName === loginName) + .filter((cookie) => (loginName ? cookie.loginName === loginName : true)) .reduce((prev, current) => { return new Date(prev.changeDate).getTime() > new Date(current.changeDate).getTime() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81a52d0d98c..ce8e8b92306 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,7 @@ importers: grpc-tools: 1.11.3 lint-staged: 13.0.3 make-dir-cli: 3.0.0 + moment: ^2.29.4 next: 13.4.2 next-themes: ^0.2.1 nice-grpc: 2.0.1 @@ -65,6 +66,7 @@ importers: '@zitadel/server': link:../../packages/zitadel-server clsx: 1.2.1 date-fns: 2.29.3 + moment: 2.29.4 next: 13.4.2_krg7tz6h6n3fx3eq7tclunioeu next-themes: 0.2.1_cmp7sjki5xcmfyvhcokzzink7a nice-grpc: 2.0.1 @@ -3581,6 +3583,10 @@ packages: hasBin: true dev: true + /moment/2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false From 99f66af64464602b0d136e7682365f2403957cdb Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 17 May 2023 17:04:56 +0200 Subject: [PATCH 05/39] verify email --- apps/login/app/(login)/accounts/page.tsx | 1 - apps/login/app/(login)/register/page.tsx | 4 +- apps/login/app/(login)/verify/page.tsx | 24 ++++++ apps/login/app/email/verify/route.ts | 15 ++++ apps/login/lib/demos.ts | 66 ++++++++------ apps/login/lib/zitadel.ts | 26 ++++-- apps/login/ui/PrivacyPolicyCheckboxes.tsx | 22 ++--- apps/login/ui/RegisterForm.tsx | 8 +- apps/login/ui/VerifyEmailForm.tsx | 90 ++++++++++++++++++++ packages/zitadel-server/src/index.ts | 4 +- packages/zitadel-server/src/v2/user/index.ts | 2 + packages/zitadel-server/src/v2/user/user.ts | 28 ++++++ 12 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 apps/login/app/(login)/verify/page.tsx create mode 100644 apps/login/app/email/verify/route.ts create mode 100644 apps/login/ui/VerifyEmailForm.tsx create mode 100644 packages/zitadel-server/src/v2/user/index.ts create mode 100644 packages/zitadel-server/src/v2/user/user.ts diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index 07f330926d2..8bc121198ce 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -2,7 +2,6 @@ import { listSessions, server } from "#/lib/zitadel"; import { Avatar, AvatarSize } from "#/ui/Avatar"; import { getAllSessionIds } from "#/utils/cookies"; import { - ChevronRightIcon, ExclamationTriangleIcon, XCircleIcon, } from "@heroicons/react/24/outline"; diff --git a/apps/login/app/(login)/register/page.tsx b/apps/login/app/(login)/register/page.tsx index 1cde08c52f6..1f7da03dbd5 100644 --- a/apps/login/app/(login)/register/page.tsx +++ b/apps/login/app/(login)/register/page.tsx @@ -11,6 +11,8 @@ export default async function Page() { server ); + console.log(legal); + return (

Register

@@ -18,7 +20,7 @@ export default async function Page() { {legal && passwordComplexitySettings && ( )} diff --git a/apps/login/app/(login)/verify/page.tsx b/apps/login/app/(login)/verify/page.tsx new file mode 100644 index 00000000000..e9ca6f80123 --- /dev/null +++ b/apps/login/app/(login)/verify/page.tsx @@ -0,0 +1,24 @@ +import VerifyEmailForm from "#/ui/VerifyEmailForm"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; + +export default async function Page({ searchParams }: { searchParams: any }) { + const { userID, code, orgID, loginname, passwordset } = searchParams; + + return ( +
+

Verify user

+

+ Enter the Code provided in the verification email. +

+ + {userID ? ( + + ) : ( +
+ + No userId provided! +
+ )} +
+ ); +} diff --git a/apps/login/app/email/verify/route.ts b/apps/login/app/email/verify/route.ts new file mode 100644 index 00000000000..e92be751d07 --- /dev/null +++ b/apps/login/app/email/verify/route.ts @@ -0,0 +1,15 @@ +import { server, verifyEmail } from "#/lib/zitadel"; +import { NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + const body = await request.json(); + if (body) { + const { userId, code } = body; + + return verifyEmail(server, userId, code).then((resp) => { + return NextResponse.json(resp); + }); + } else { + return NextResponse.error(); + } +} diff --git a/apps/login/lib/demos.ts b/apps/login/lib/demos.ts index 7825a8c346c..0bcacc0a991 100644 --- a/apps/login/lib/demos.ts +++ b/apps/login/lib/demos.ts @@ -19,35 +19,40 @@ export const demos: { name: string; items: Item[] }[] = [ description: "The page to request a users password", }, { - name: "Set Password", - slug: "password/set", - description: "The page to set a users password", - }, - { - name: "MFA", - slug: "mfa", - description: "The page to request a users mfa method", - }, - { - name: "MFA Set", - slug: "mfa/set", - description: "The page to set a users mfa method", - }, - { - name: "MFA Create", - slug: "mfa/create", - description: "The page to create a users mfa method", - }, - { - name: "Passwordless", - slug: "passwordless", - description: "The page to login a user with his passwordless device", - }, - { - name: "Passwordless Create", - slug: "passwordless/create", - description: "The page to add a users passwordless device", + name: "Accounts", + slug: "accounts", + description: "List active and inactive sessions", }, + // { + // name: "Set Password", + // slug: "password/set", + // description: "The page to set a users password", + // }, + // { + // name: "MFA", + // slug: "mfa", + // description: "The page to request a users mfa method", + // }, + // { + // name: "MFA Set", + // slug: "mfa/set", + // description: "The page to set a users mfa method", + // }, + // { + // name: "MFA Create", + // slug: "mfa/create", + // description: "The page to create a users mfa method", + // }, + // { + // name: "Passwordless", + // slug: "passwordless", + // description: "The page to login a user with his passwordless device", + // }, + // { + // name: "Passwordless Create", + // slug: "passwordless/create", + // description: "The page to add a users passwordless device", + // }, ], }, { @@ -58,6 +63,11 @@ export const demos: { name: string; items: Item[] }[] = [ slug: "register", description: "Create your ZITADEL account", }, + { + name: "Verify email", + slug: "verify", + description: "Verify your account with an email code", + }, ], }, ]; diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 440dbbf22de..99b47878136 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -1,7 +1,7 @@ import { ZitadelServer, ZitadelServerOptions, - management, + user, settings, getServers, initializeServer, @@ -127,18 +127,19 @@ export type AddHumanUserData = { email: string; password: string; }; + export function addHumanUser( server: ZitadelServer, { email, firstName, lastName, password }: AddHumanUserData ): Promise { - const mgmt = management.getManagement(server); + const mgmt = user.getUser(server); return mgmt .addHumanUser( { - email: { email, isEmailVerified: false }, - userName: email, + email: { email, isVerified: false }, + username: email, profile: { firstName, lastName }, - initialPassword: password, + password: { password }, }, { // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") @@ -150,4 +151,19 @@ export function addHumanUser( }); } +export function verifyEmail( + server: ZitadelServer, + userId: string, + verificationCode: string +): Promise { + const mgmt = user.getUser(server); + return mgmt.verifyEmail( + { + userId, + verificationCode, + }, + {} + ); +} + export { server }; diff --git a/apps/login/ui/PrivacyPolicyCheckboxes.tsx b/apps/login/ui/PrivacyPolicyCheckboxes.tsx index 69b3babbeb9..8addd227a19 100644 --- a/apps/login/ui/PrivacyPolicyCheckboxes.tsx +++ b/apps/login/ui/PrivacyPolicyCheckboxes.tsx @@ -2,10 +2,10 @@ import React, { useState } from "react"; import Link from "next/link"; import { Checkbox } from "./Checkbox"; -import { PrivacyPolicy } from "@zitadel/server"; +import { LegalAndSupportSettings } from "@zitadel/server"; type Props = { - privacyPolicy: PrivacyPolicy; + legal: LegalAndSupportSettings; onChange: (allAccepted: boolean) => void; }; @@ -14,7 +14,7 @@ type AcceptanceState = { privacyPolicyAccepted: boolean; }; -export function PrivacyPolicyCheckboxes({ privacyPolicy, onChange }: Props) { +export function PrivacyPolicyCheckboxes({ legal, onChange }: Props) { const [acceptanceState, setAcceptanceState] = useState({ tosAccepted: false, privacyPolicyAccepted: false, @@ -24,9 +24,9 @@ export function PrivacyPolicyCheckboxes({ privacyPolicy, onChange }: Props) { <>

To register you must agree to the terms and conditions - {privacyPolicy?.helpLink && ( + {legal?.helpLink && ( - + )}

- {privacyPolicy?.tosLink && ( + {legal?.tosLink && (

Agree  - + Terms of Service

)} - {privacyPolicy?.privacyLink && ( + {legal?.privacyPolicyLink && (
Agree  diff --git a/apps/login/ui/RegisterForm.tsx b/apps/login/ui/RegisterForm.tsx index 85a114010ee..c7641176fa8 100644 --- a/apps/login/ui/RegisterForm.tsx +++ b/apps/login/ui/RegisterForm.tsx @@ -30,12 +30,12 @@ type Inputs = | FieldValues; type Props = { - privacyPolicy: LegalAndSupportSettings; + legal: LegalAndSupportSettings; passwordComplexityPolicy: PasswordComplexitySettings; }; export default function RegisterForm({ - privacyPolicy, + legal, passwordComplexityPolicy, }: Props) { const { register, handleSubmit, watch, formState } = useForm({ @@ -166,9 +166,9 @@ export default function RegisterForm({ /> )} - {privacyPolicy && ( + {legal && ( )} diff --git a/apps/login/ui/VerifyEmailForm.tsx b/apps/login/ui/VerifyEmailForm.tsx new file mode 100644 index 00000000000..a802fe1fa2f --- /dev/null +++ b/apps/login/ui/VerifyEmailForm.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState } from "react"; +import { Button, ButtonVariants } from "./Button"; +import { TextInput } from "./Input"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import { Spinner } from "./Spinner"; + +type Inputs = { + code: string; +}; + +type Props = { + userId: string; +}; + +export default function VerifyEmailForm({ userId }: Props) { + const { register, handleSubmit, formState } = useForm({ + mode: "onBlur", + }); + + const [error, setError] = useState(""); + + const [loading, setLoading] = useState(false); + + const router = useRouter(); + + async function submitCode(values: Inputs) { + setLoading(true); + const res = await fetch("/email/verify", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + code: values.code, + userId, + }), + }); + + if (!res.ok) { + setLoading(false); + throw new Error("Failed to verify email"); + } + + setLoading(false); + return res.json(); + } + + function submitCodeAndContinue(value: Inputs): Promise { + console.log(value); + return submitCode(value).then((resp: any) => { + return router.push(`/accounts`); + }); + } + + const { errors } = formState; + + return ( +
+
+ +
+ +
+ {/* */} + + +
+
+ ); +} diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index 84016632487..d8768eba762 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -1,6 +1,6 @@ -import * as management from "./management"; import * as settings from "./v2/settings"; import * as session from "./v2/session"; +import * as user from "./v2/user"; import * as login from "./proto/server/zitadel/settings/v2alpha/login_settings"; import * as password from "./proto/server/zitadel/settings/v2alpha/password_settings"; @@ -27,7 +27,7 @@ export { ZitadelServer, type ZitadelServerOptions, initializeServer, - management, + user, session, settings, login, diff --git a/packages/zitadel-server/src/v2/user/index.ts b/packages/zitadel-server/src/v2/user/index.ts new file mode 100644 index 00000000000..df27d619d2e --- /dev/null +++ b/packages/zitadel-server/src/v2/user/index.ts @@ -0,0 +1,2 @@ +export * from "./user"; +export * from "../../proto/server/zitadel/user/v2alpha/user"; diff --git a/packages/zitadel-server/src/v2/user/user.ts b/packages/zitadel-server/src/v2/user/user.ts new file mode 100644 index 00000000000..1c86a0df496 --- /dev/null +++ b/packages/zitadel-server/src/v2/user/user.ts @@ -0,0 +1,28 @@ +import { CompatServiceDefinition } from "nice-grpc/lib/service-definitions"; + +import { + UserServiceClient, + UserServiceDefinition, +} from "../../proto/server/zitadel/user/v2alpha/user_service"; + +import { ZitadelServer, createClient, getServers } from "../../server"; + +export const getUser = (server?: string | ZitadelServer) => { + let config; + if (server && typeof server === "string") { + const apps = getServers(); + config = apps.find((a) => a.name === server)?.config; + } else if (server && typeof server === "object") { + config = server.config; + } + + if (!config) { + throw Error("No ZITADEL server found"); + } + + return createClient( + UserServiceDefinition as CompatServiceDefinition, + config.apiUrl, + config.token + ); +}; From d08abbfaa935d3c48c78826d2982b9b0732041ee Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 19 May 2023 10:13:05 +0200 Subject: [PATCH 06/39] fix search params --- apps/login/app/(login)/password/page.tsx | 15 ++++++---- apps/login/app/session/route.ts | 1 - .../{email/verify => verifyemail}/route.ts | 0 apps/login/lib/zitadel.ts | 2 +- apps/login/ui/RegisterForm.tsx | 2 +- apps/login/ui/UsernameForm.tsx | 5 +++- apps/login/ui/VerifyEmailForm.tsx | 2 +- apps/login/utils/cookies.ts | 29 +++++++++++++------ 8 files changed, 37 insertions(+), 19 deletions(-) rename apps/login/app/{email/verify => verifyemail}/route.ts (100%) diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index 277fea63e33..c365c2dd0ff 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -4,17 +4,22 @@ import UserAvatar from "#/ui/UserAvatar"; import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; async function loadSession(loginName: string) { - const recent = await getMostRecentCookieWithLoginname(loginName); + try { + const recent = await getMostRecentCookieWithLoginname(`${loginName}`); - return getSession(server, recent.id, recent.token).then(({ session }) => { - console.log("ss", session); - return session; - }); + return getSession(server, recent.id, recent.token).then(({ session }) => { + console.log("ss", session); + return session; + }); + } catch (error) { + throw new Error("Session could not be loaded!"); + } } export default async function Page({ searchParams }: { searchParams: any }) { const { loginName } = searchParams; + console.log(loginName); const sessionFactors = await loadSession(loginName); return ( diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index 934bdccb1c7..6a718ee4e7c 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -2,7 +2,6 @@ import { createSession, getSession, server, setSession } from "#/lib/zitadel"; import { SessionCookie, addSessionToCookie, - getMostRecentCookieWithLoginname, getMostRecentSessionCookie, updateSessionCookie, } from "#/utils/cookies"; diff --git a/apps/login/app/email/verify/route.ts b/apps/login/app/verifyemail/route.ts similarity index 100% rename from apps/login/app/email/verify/route.ts rename to apps/login/app/verifyemail/route.ts diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 99b47878136..9691882b53d 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -136,7 +136,7 @@ export function addHumanUser( return mgmt .addHumanUser( { - email: { email, isVerified: false }, + email: { email }, username: email, profile: { firstName, lastName }, password: { password }, diff --git a/apps/login/ui/RegisterForm.tsx b/apps/login/ui/RegisterForm.tsx index c7641176fa8..61471bc2682 100644 --- a/apps/login/ui/RegisterForm.tsx +++ b/apps/login/ui/RegisterForm.tsx @@ -72,7 +72,7 @@ export default function RegisterForm({ function submitAndLink(value: Inputs): Promise { return submitRegister(value).then((resp: any) => { - return router.push(`/register/success?userid=${resp.userId}`); + return router.push(`/verify?userID=${resp.userId}`); }); } diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx index 4c2d9cf1b55..aad5ff9d238 100644 --- a/apps/login/ui/UsernameForm.tsx +++ b/apps/login/ui/UsernameForm.tsx @@ -44,7 +44,10 @@ export default function UsernameForm() { function submitUsernameAndContinue(value: Inputs): Promise { return submitUsername(value).then(({ factors }) => { console.log(factors); - return router.push(`/password?loginName=${factors.user.loginName}`); + return router.push( + `/password?` + + new URLSearchParams({ loginName: `${factors.user.loginName}` }) + ); }); } diff --git a/apps/login/ui/VerifyEmailForm.tsx b/apps/login/ui/VerifyEmailForm.tsx index a802fe1fa2f..79c8d1ed832 100644 --- a/apps/login/ui/VerifyEmailForm.tsx +++ b/apps/login/ui/VerifyEmailForm.tsx @@ -28,7 +28,7 @@ export default function VerifyEmailForm({ userId }: Props) { async function submitCode(values: Inputs) { setLoading(true); - const res = await fetch("/email/verify", { + const res = await fetch("/verifyemail", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 45579193efe..22f15b87746 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -146,16 +146,27 @@ export async function getMostRecentCookieWithLoginname( if (stringifiedCookie?.value) { const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); - const latest = sessions - .filter((cookie) => (loginName ? cookie.loginName === loginName : true)) - .reduce((prev, current) => { - return new Date(prev.changeDate).getTime() > - new Date(current.changeDate).getTime() - ? prev - : current; - }); + console.log("sess", sessions); + const filtered = sessions.filter((cookie) => { + console.log("filtered", `${cookie.loginName}`, loginName?.toString()); + return !!loginName ? cookie.loginName === loginName : true; + }); - return latest; + const latest = + filtered && filtered.length + ? filtered.reduce((prev, current) => { + return new Date(prev.changeDate).getTime() > + new Date(current.changeDate).getTime() + ? prev + : current; + }) + : undefined; + + if (latest) { + return latest; + } else { + return Promise.reject(); + } } else { return Promise.reject(); } From 7ba6e0f09948c3ea1ab46a2e413564894565d3ff Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 19 May 2023 13:02:09 +0200 Subject: [PATCH 07/39] signedin page, client avatar --- apps/login/app/(login)/accounts/page.tsx | 25 +++++--- apps/login/app/(login)/password/page.tsx | 3 - apps/login/app/(login)/signedin/page.tsx | 34 +++++++++++ apps/login/ui/Avatar.tsx | 75 ++++++++++++------------ apps/login/ui/UserAvatar.tsx | 4 +- apps/login/utils/cookies.ts | 2 +- 6 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 apps/login/app/(login)/signedin/page.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index 8bc121198ce..79e47be1f93 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -1,5 +1,5 @@ import { listSessions, server } from "#/lib/zitadel"; -import { Avatar, AvatarSize } from "#/ui/Avatar"; +import { Avatar } from "#/ui/Avatar"; import { getAllSessionIds } from "#/utils/cookies"; import { ExclamationTriangleIcon, @@ -18,7 +18,6 @@ async function loadSessions() { server, ids.filter((id: string | undefined) => !!id) ).then((sessions) => { - console.log("ss", sessions.sessions); return sessions; }); } else { @@ -37,16 +36,26 @@ export default async function Page() {
{sessions ? ( sessions.map((session: any) => { + const validPassword = session.factors.password?.verifiedAt; + console.log(session); return (
@@ -57,18 +66,16 @@ export default async function Page() { {session.factors.user.loginName} - {session.factors.password?.verifiedAt && ( + {validPassword && ( - {moment( - new Date(session.factors.password.verifiedAt) - ).fromNow()} + {moment(new Date(validPassword)).fromNow()} )}
- {session.factors.password?.verifiedAt ? ( + {validPassword ? (
) : (
diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index c365c2dd0ff..dff1b7eca0e 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -8,7 +8,6 @@ async function loadSession(loginName: string) { const recent = await getMostRecentCookieWithLoginname(`${loginName}`); return getSession(server, recent.id, recent.token).then(({ session }) => { - console.log("ss", session); return session; }); } catch (error) { @@ -18,8 +17,6 @@ async function loadSession(loginName: string) { export default async function Page({ searchParams }: { searchParams: any }) { const { loginName } = searchParams; - - console.log(loginName); const sessionFactors = await loadSession(loginName); return ( diff --git a/apps/login/app/(login)/signedin/page.tsx b/apps/login/app/(login)/signedin/page.tsx new file mode 100644 index 00000000000..3a6a76171b0 --- /dev/null +++ b/apps/login/app/(login)/signedin/page.tsx @@ -0,0 +1,34 @@ +import { getSession, server } from "#/lib/zitadel"; +import PasswordForm from "#/ui/PasswordForm"; +import UserAvatar from "#/ui/UserAvatar"; +import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; + +async function loadSession(loginName: string) { + try { + const recent = await getMostRecentCookieWithLoginname(`${loginName}`); + + return getSession(server, recent.id, recent.token).then(({ session }) => { + return session; + }); + } catch (error) { + throw new Error("Session could not be loaded!"); + } +} + +export default async function Page({ searchParams }: { searchParams: any }) { + const { loginName } = searchParams; + const sessionFactors = await loadSession(loginName); + + return ( +
+

{`Welcome ${sessionFactors.factors?.user?.displayName}`}

+

You are signed in.

+ + +
+ ); +} diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 8f69f31e0b8..294033f4d9f 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -1,32 +1,18 @@ +"use client"; + import { ColorShade, getColorHash } from "#/utils/colors"; import { useTheme } from "next-themes"; -import { FC } from "react"; - -export enum AvatarSize { - SMALL = "small", - BASE = "base", - LARGE = "large", -} interface AvatarProps { name: string | null | undefined; loginName: string; imageUrl?: string; - size?: AvatarSize; + size?: "small" | "base" | "large"; shadow?: boolean; } -export const Avatar: FC = ({ - size = AvatarSize.BASE, - name, - loginName, - imageUrl, - shadow, -}) => { - // const { resolvedTheme } = useTheme(); +function getCredentials(name: string, loginName: string) { let credentials = ""; - - console.log(name, loginName); if (name) { const split = name.split(" "); if (split) { @@ -34,7 +20,7 @@ export const Avatar: FC = ({ split[0].charAt(0) + (split[1] ? split[1].charAt(0) : ""); credentials = initials; } else { - return name.charAt(0); + credentials = name.charAt(0); } } else { const username = loginName.split("@")[0]; @@ -50,32 +36,51 @@ export const Avatar: FC = ({ credentials = initials; } + return credentials; +} + +export function Avatar({ + size = "base", + name, + loginName, + imageUrl, + shadow, +}: AvatarProps) { + const { resolvedTheme } = useTheme(); + const credentials = getCredentials(name ?? loginName, loginName); + const color: ColorShade = getColorHash(loginName); - // const avatarStyleDark = { - // backgroundColor: color[900], - // color: color[200], - // }; + const avatarStyleDark = { + backgroundColor: color[900], + color: color[200], + }; - // const avatarStyleLight = { - // backgroundColor: color[200], - // color: color[900], - // }; + const avatarStyleLight = { + backgroundColor: color[200], + color: color[900], + }; + + // const [mounted, setMounted] = useState(false); + // // useEffect only runs on the client, so now we can safely show the UI + // useEffect(() => { + // setMounted(true); + // }, []); return (
{imageUrl ? ( = ({ /> ) : ( {credentials} )}
); -}; +} diff --git a/apps/login/ui/UserAvatar.tsx b/apps/login/ui/UserAvatar.tsx index 332de5feadd..33205b899bd 100644 --- a/apps/login/ui/UserAvatar.tsx +++ b/apps/login/ui/UserAvatar.tsx @@ -1,4 +1,4 @@ -import { Avatar, AvatarSize } from "#/ui/Avatar"; +import { Avatar } from "#/ui/Avatar"; import { ChevronDownIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; @@ -17,7 +17,7 @@ export default function UserAvatar({
diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 22f15b87746..6b7504f1eaa 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -88,7 +88,7 @@ export async function removeSessionFromCookie( // @ts-ignore return cookiesList.set({ - name: "sessions", + name: "__Secure-sessions", value: JSON.stringify(filteredSessions), httpOnly: true, path: "/", From db7ba251ed6f4f8a8adf1b7f7c2d5a43e4957ff9 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 22 May 2023 09:21:03 +0200 Subject: [PATCH 08/39] latest turborepo --- package.json | 2 +- pnpm-lock.yaml | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 7747ec10e41..d3de0176f1f 100755 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "eslint": "^7.32.0", "eslint-config-zitadel": "workspace:*", "prettier": "^2.5.1", - "turbo": "latest" + "turbo": "^1.9.8" }, "packageManager": "pnpm@7.15.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce8e8b92306..f41e64c5670 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,13 +8,13 @@ importers: eslint: ^7.32.0 eslint-config-zitadel: workspace:* prettier: ^2.5.1 - turbo: latest + turbo: ^1.9.8 devDependencies: '@changesets/cli': 2.25.2 eslint: 7.32.0 eslint-config-zitadel: link:packages/eslint-config-zitadel prettier: 2.8.0 - turbo: 1.9.3 + turbo: 1.9.8 apps/login: specifiers: @@ -5079,65 +5079,65 @@ packages: yargs: 17.6.2 dev: true - /turbo-darwin-64/1.9.3: - resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==} + /turbo-darwin-64/1.9.8: + resolution: {integrity: sha512-PkTdBjPfgpj/Dob/6SjkzP0BBP80/KmFjLEocXVEECCLJE6tHKbWLRdvc79B0N6SufdYdZ1uvvoU3KPtBokSPw==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64/1.9.3: - resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==} + /turbo-darwin-arm64/1.9.8: + resolution: {integrity: sha512-sLwqOx3XV57QCEoJM9GnDDnnqidG8wf29ytxssBaWHBdeJTjupyrmzTUrX+tyKo3Q+CjWvbPLyqVqxT4g5NuXQ==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64/1.9.3: - resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==} + /turbo-linux-64/1.9.8: + resolution: {integrity: sha512-AMg6VT6sW7aOD1uOs5suxglXfTYz9T0uVyKGKokDweGOYTWmuTMGU5afUT1tYRUwQ+kVPJI+83Atl5Ob0oBsgw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64/1.9.3: - resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==} + /turbo-linux-arm64/1.9.8: + resolution: {integrity: sha512-tLnxFv+OIklwTjiOZ8XMeEeRDAf150Ry4BCivNwgTVFAqQGEqkFP6KGBy56hb5RRF1frPQpoPGipJNVm7c8m1w==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64/1.9.3: - resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==} + /turbo-windows-64/1.9.8: + resolution: {integrity: sha512-r3pCjvXTMR7kq2E3iqwFlN1R7pFO/TOsuUjMhOSPP7HwuuUIinAckU4I9foM3q7ZCQd1XXScBUt3niDyHijAqQ==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64/1.9.3: - resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==} + /turbo-windows-arm64/1.9.8: + resolution: {integrity: sha512-CWzRbX2TM5IfHBC6uWM659qUOEDC4h0nn16ocG8yIq1IF3uZMzKRBHgGOT5m1BHom+R08V0NcjTmPRoqpiI0dg==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo/1.9.3: - resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==} + /turbo/1.9.8: + resolution: {integrity: sha512-dTouGZBm4a2fE0OPafcTQERCp4i3ZOow0Pr0JlOyxKmzJy0JRwXypH013kbZoK6k1ET5tS/g9rwUXIM/AmWXXQ==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.9.3 - turbo-darwin-arm64: 1.9.3 - turbo-linux-64: 1.9.3 - turbo-linux-arm64: 1.9.3 - turbo-windows-64: 1.9.3 - turbo-windows-arm64: 1.9.3 + turbo-darwin-64: 1.9.8 + turbo-darwin-arm64: 1.9.8 + turbo-linux-64: 1.9.8 + turbo-linux-arm64: 1.9.8 + turbo-windows-64: 1.9.8 + turbo-windows-arm64: 1.9.8 dev: true /type-check/0.4.0: From b169faad0e5e5774877f47becc18c8171098e1e8 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 22 May 2023 11:48:18 +0200 Subject: [PATCH 09/39] verify email --- apps/login/app/(login)/accounts/page.tsx | 9 ++-- apps/login/app/(login)/verify/page.tsx | 8 ++- apps/login/app/layout.tsx | 6 ++- apps/login/app/resendverifyemail/route.ts | 20 +++++++ apps/login/app/verifyemail/route.ts | 10 ++-- apps/login/lib/zitadel.ts | 35 ++++++++++--- apps/login/ui/AddressBar.tsx | 8 ++- apps/login/ui/Alert.tsx | 14 +++++ apps/login/ui/VerifyEmailForm.tsx | 64 +++++++++++++++++++---- packages/zitadel-react/package.json | 5 +- packages/zitadel-server/src/index.ts | 8 +++ pnpm-lock.yaml | 5 +- 12 files changed, 159 insertions(+), 33 deletions(-) create mode 100644 apps/login/app/resendverifyemail/route.ts create mode 100644 apps/login/ui/Alert.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index 79e47be1f93..bdfc5456797 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -1,4 +1,5 @@ import { listSessions, server } from "#/lib/zitadel"; +import Alert from "#/ui/Alert"; import { Avatar } from "#/ui/Avatar"; import { getAllSessionIds } from "#/utils/cookies"; import { @@ -35,11 +36,12 @@ export default async function Page() {
{sessions ? ( - sessions.map((session: any) => { + sessions.map((session: any, index: number) => { const validPassword = session.factors.password?.verifiedAt; console.log(session); return ( - - No Sessions available! -
+ No Sessions available! )}
diff --git a/apps/login/app/(login)/verify/page.tsx b/apps/login/app/(login)/verify/page.tsx index e9ca6f80123..b074dfe8cfb 100644 --- a/apps/login/app/(login)/verify/page.tsx +++ b/apps/login/app/(login)/verify/page.tsx @@ -2,7 +2,7 @@ import VerifyEmailForm from "#/ui/VerifyEmailForm"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; export default async function Page({ searchParams }: { searchParams: any }) { - const { userID, code, orgID, loginname, passwordset } = searchParams; + const { userID, code, submit, orgID, loginname, passwordset } = searchParams; return (
@@ -12,7 +12,11 @@ export default async function Page({ searchParams }: { searchParams: any }) {

{userID ? ( - + ) : (
diff --git a/apps/login/app/layout.tsx b/apps/login/app/layout.tsx index 9ca933f31e6..65b9d41fbe1 100644 --- a/apps/login/app/layout.tsx +++ b/apps/login/app/layout.tsx @@ -34,6 +34,10 @@ export default async function RootLayout({ darkTheme: branding?.darkTheme, }; } + + let domain = process.env.ZITADEL_API_URL; + domain = domain ? domain.replace("https://", "") : "acme.com"; + return ( @@ -48,7 +52,7 @@ export default async function RootLayout({ {showNav && (
- +
)} diff --git a/apps/login/app/resendverifyemail/route.ts b/apps/login/app/resendverifyemail/route.ts new file mode 100644 index 00000000000..15376ee7672 --- /dev/null +++ b/apps/login/app/resendverifyemail/route.ts @@ -0,0 +1,20 @@ +import { setEmail, server } from "#/lib/zitadel"; +import { NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + const body = await request.json(); + if (body) { + const { userId, code } = body; + + // replace with resend Mail method once its implemented + return setEmail(server, userId) + .then((resp) => { + return NextResponse.json(resp); + }) + .catch((error) => { + return NextResponse.json(error, { status: 500 }); + }); + } else { + return NextResponse.error(); + } +} diff --git a/apps/login/app/verifyemail/route.ts b/apps/login/app/verifyemail/route.ts index e92be751d07..1b2ddd29da8 100644 --- a/apps/login/app/verifyemail/route.ts +++ b/apps/login/app/verifyemail/route.ts @@ -6,9 +6,13 @@ export async function POST(request: NextRequest) { if (body) { const { userId, code } = body; - return verifyEmail(server, userId, code).then((resp) => { - return NextResponse.json(resp); - }); + return verifyEmail(server, userId, code) + .then((resp) => { + return NextResponse.json(resp); + }) + .catch((error) => { + return NextResponse.json(error, { status: 500 }); + }); } else { return NextResponse.error(); } diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 9691882b53d..7f723c01a89 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -6,6 +6,11 @@ import { getServers, initializeServer, session, + GetGeneralSettingsResponse, + GetBrandingSettingsResponse, + GetPasswordComplexitySettingsResponse, + GetLegalAndSupportSettingsResponse, + AddHumanUserResponse, } from "@zitadel/server"; export const zitadelConfig: ZitadelServerOptions = { @@ -33,7 +38,7 @@ export function getBrandingSettings( // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.settings); + .then((resp: GetBrandingSettingsResponse) => resp.settings); } export function getGeneralSettings( @@ -48,7 +53,7 @@ export function getGeneralSettings( // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.supportedLanguages); + .then((resp: GetGeneralSettingsResponse) => resp.supportedLanguages); } export function getLegalAndSupportSettings( @@ -62,7 +67,7 @@ export function getLegalAndSupportSettings( // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.settings); + .then((resp: GetLegalAndSupportSettingsResponse) => resp.settings); } export function getPasswordComplexitySettings( @@ -77,7 +82,7 @@ export function getPasswordComplexitySettings( // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => resp.settings); + .then((resp: GetPasswordComplexitySettingsResponse) => resp.settings); } export function createSession( @@ -145,7 +150,7 @@ export function addHumanUser( // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") } ) - .then((resp) => { + .then((resp: AddHumanUserResponse) => { console.log("added user", resp.userId); return resp.userId; }); @@ -156,8 +161,8 @@ export function verifyEmail( userId: string, verificationCode: string ): Promise { - const mgmt = user.getUser(server); - return mgmt.verifyEmail( + const userservice = user.getUser(server); + return userservice.verifyEmail( { userId, verificationCode, @@ -166,4 +171,20 @@ export function verifyEmail( ); } +/** + * + * @param server + * @param userId the id of the user where the email should be set + * @returns the newly set email + */ +export function setEmail(server: ZitadelServer, userId: string): Promise { + const userservice = user.getUser(server); + return userservice.setEmail( + { + userId, + }, + {} + ); +} + export { server }; diff --git a/apps/login/ui/AddressBar.tsx b/apps/login/ui/AddressBar.tsx index 740af95fe57..8c5548bffcd 100644 --- a/apps/login/ui/AddressBar.tsx +++ b/apps/login/ui/AddressBar.tsx @@ -3,7 +3,11 @@ import React from "react"; import { usePathname } from "next/navigation"; -export function AddressBar() { +type Props = { + domain: string; +}; + +export function AddressBar({ domain }: Props) { const pathname = usePathname(); return ( @@ -24,7 +28,7 @@ export function AddressBar() {
- acme.com + {domain}
{pathname ? ( <> diff --git a/apps/login/ui/Alert.tsx b/apps/login/ui/Alert.tsx new file mode 100644 index 00000000000..d5eb9084ee8 --- /dev/null +++ b/apps/login/ui/Alert.tsx @@ -0,0 +1,14 @@ +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; + +type Props = { + children: React.ReactNode; +}; + +export default function Alert({ children }: Props) { + return ( +
+ + {children} +
+ ); +} diff --git a/apps/login/ui/VerifyEmailForm.tsx b/apps/login/ui/VerifyEmailForm.tsx index 79c8d1ed832..df5a1328b61 100644 --- a/apps/login/ui/VerifyEmailForm.tsx +++ b/apps/login/ui/VerifyEmailForm.tsx @@ -1,11 +1,12 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Button, ButtonVariants } from "./Button"; import { TextInput } from "./Input"; import { useForm } from "react-hook-form"; import { useRouter } from "next/navigation"; import { Spinner } from "./Spinner"; +import Alert from "#/ui/Alert"; type Inputs = { code: string; @@ -13,13 +14,24 @@ type Inputs = { type Props = { userId: string; + code: string; + submit: boolean; }; -export default function VerifyEmailForm({ userId }: Props) { +export default function VerifyEmailForm({ userId, code, submit }: Props) { const { register, handleSubmit, formState } = useForm({ mode: "onBlur", + defaultValues: { + code: code ?? "", + }, }); + useEffect(() => { + if (submit && code && userId) { + submitCode({ code }); + } + }, []); + const [error, setError] = useState(""); const [loading, setLoading] = useState(false); @@ -39,13 +51,37 @@ export default function VerifyEmailForm({ userId }: Props) { }), }); + const response = await res.json(); + if (!res.ok) { setLoading(false); - throw new Error("Failed to verify email"); + setError(response.details); + return Promise.reject(response); + } else { + setLoading(false); + return response; } + } - setLoading(false); - return res.json(); + async function resendCode() { + setLoading(true); + const res = await fetch("/resendverifyemail", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + + const response = await res.json(); + + if (!res.ok) { + setLoading(false); + setError(response.details); + return Promise.reject(response); + } else { + setLoading(false); + return response; + } } function submitCodeAndContinue(value: Inputs): Promise { @@ -55,8 +91,6 @@ export default function VerifyEmailForm({ userId }: Props) { }); } - const { errors } = formState; - return (
@@ -69,10 +103,20 @@ export default function VerifyEmailForm({ userId }: Props) { />
+ {error && ( +
+ {error} +
+ )} +
- {/* */} +
); diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index 216275612f2..a4f3cda1e0d 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -45,35 +45,42 @@ export async function PUT(request: NextRequest) { const { password } = body; const recent = await getMostRecentSessionCookie(); - const session = await setSession(server, recent.id, recent.token, password); - const sessionCookie: SessionCookie = { - id: recent.id, - token: session.sessionToken, - changeDate: session.details.changeDate, - loginName: recent.loginName, - }; - - return getSession(server, sessionCookie.id, sessionCookie.token).then( - ({ session }) => { - const newCookie: SessionCookie = { - id: sessionCookie.id, - token: sessionCookie.token, - changeDate: session.changeDate, - loginName: session.factors.user.loginName, + return setSession(server, recent.id, recent.token, password) + .then((session) => { + const sessionCookie: SessionCookie = { + id: recent.id, + token: session.sessionToken, + changeDate: session.details.changeDate, + loginName: recent.loginName, }; - return updateSessionCookie(sessionCookie.id, sessionCookie) - .then(() => { - console.log("updatedRecent:", sessionCookie); - return NextResponse.json({ factors: session.factors }); - }) - .catch((error) => { - console.error("errr", error); - return NextResponse.json(error, { status: 500 }); - }); - } - ); + return getSession(server, sessionCookie.id, sessionCookie.token).then( + ({ session }) => { + const newCookie: SessionCookie = { + id: sessionCookie.id, + token: sessionCookie.token, + changeDate: session.changeDate, + loginName: session.factors.user.loginName, + }; + + return updateSessionCookie(sessionCookie.id, newCookie) + .then(() => { + return NextResponse.json({ factors: session.factors }); + }) + .catch((error) => { + return NextResponse.json( + { details: "could not set cookie" }, + { status: 500 } + ); + }); + } + ); + }) + .catch((error) => { + console.error("erasd", error); + return NextResponse.json(error, { status: 500 }); + }); } else { return NextResponse.error(); } diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 925d9d65d6a..857713a4183 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -22,24 +22,29 @@ async function set(sessions: SessionCookie[]) { export async function addSessionToCookie(session: SessionCookie): Promise { const cookiesList = cookies(); - // const hasSessions = cookiesList.has("sessions"); - // if (hasSessions) { const stringifiedCookie = cookiesList.get("sessions"); - const currentSessions: SessionCookie[] = stringifiedCookie?.value + let currentSessions: SessionCookie[] = stringifiedCookie?.value ? JSON.parse(stringifiedCookie?.value) : []; + const index = currentSessions.findIndex( + (s) => s.loginName === session.loginName + ); + + if (index > -1) { + currentSessions[index] = session; + } else { + currentSessions = [...currentSessions, session]; + } + // @ts-ignore return cookiesList.set({ name: "sessions", - value: JSON.stringify([...currentSessions, session]), + value: JSON.stringify(currentSessions), httpOnly: true, path: "/", }); - // } else { - // return set([session]); - // } } export async function updateSessionCookie( @@ -47,8 +52,7 @@ export async function updateSessionCookie( session: SessionCookie ): Promise { const cookiesList = cookies(); - // const hasSessions = cookiesList.has("sessions"); - // if (hasSessions) { + const stringifiedCookie = cookiesList.get("sessions"); const sessions: SessionCookie[] = stringifiedCookie?.value @@ -65,17 +69,12 @@ export async function updateSessionCookie( httpOnly: true, path: "/", }); - // } else { - // return Promise.reject(); - // } } export async function removeSessionFromCookie( session: SessionCookie ): Promise { const cookiesList = cookies(); - // const hasSessions = cookiesList.has("sessions"); - // if (hasSessions) { const stringifiedCookie = cookiesList.get("sessions"); const sessions: SessionCookie[] = stringifiedCookie?.value @@ -88,14 +87,11 @@ export async function removeSessionFromCookie( // @ts-ignore return cookiesList.set({ - name: "__Secure-sessions", + name: "sessions", value: JSON.stringify(filteredSessions), httpOnly: true, path: "/", }); - // } else { - // return Promise.reject(); - // } } export async function getMostRecentSessionCookie(): Promise { @@ -105,7 +101,6 @@ export async function getMostRecentSessionCookie(): Promise { if (stringifiedCookie?.value) { const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); - console.log(sessions); const latest = sessions.reduce((prev, current) => { return new Date(prev.changeDate).getTime() > new Date(current.changeDate).getTime() @@ -147,12 +142,9 @@ export async function getMostRecentCookieWithLoginname( const sessions: SessionCookie[] = JSON.parse(stringifiedCookie?.value); const filtered = sessions.filter((cookie) => { - console.log(!!loginName); return !!loginName ? cookie.loginName === loginName : true; }); - console.log(filtered); - const latest = filtered && filtered.length ? filtered.reduce((prev, current) => { From 935c7af196f6ed573779920e51ef7c76c2cb7ea4 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 23 May 2023 09:31:56 +0200 Subject: [PATCH 15/39] accounts page --- apps/login/app/(login)/accounts/page.tsx | 6 +++--- apps/login/ui/Avatar.tsx | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index de67db4b6b4..d38187c30ca 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -27,7 +27,7 @@ export default async function Page() {

Accounts

Use your ZITADEL Account

-
+
{sessions ? ( sessions.map((session: any, index: number) => { const validPassword = session.factors.password?.verifiedAt; @@ -45,7 +45,7 @@ export default async function Page() { loginName: session.factors.user.loginName, }) } - className="group flex flex-row items-center hover:bg-black/10 dark:hover:bg-white/10 py-3 px-4 rounded-md" + className="group flex flex-row items-center border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all" >
No Sessions available! )} -
+
diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 294033f4d9f..278c6eed2db 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -61,12 +61,6 @@ export function Avatar({ color: color[900], }; - // const [mounted, setMounted] = useState(false); - // // useEffect only runs on the client, so now we can safely show the UI - // useEffect(() => { - // setMounted(true); - // }, []); - return (
Date: Tue, 23 May 2023 10:12:19 +0200 Subject: [PATCH 16/39] branding assets --- apps/login/app/(login)/accounts/page.tsx | 2 +- apps/login/app/(login)/layout.tsx | 18 ++++++++++++- apps/login/app/page.tsx | 14 ++++------ apps/login/next.config.js | 6 +++++ apps/login/ui/Logo.tsx | 33 ++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 apps/login/ui/Logo.tsx diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index d38187c30ca..7f87ff32541 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -45,7 +45,7 @@ export default async function Page() { loginName: session.factors.user.loginName, }) } - className="group flex flex-row items-center border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all" + className="group flex flex-row items-center bg-background-light-400 dark:bg-background-dark-400 border border-divider-light hover:shadow-lg dark:hover:bg-white/10 py-2 px-4 rounded-md transition-all" >
| undefined; + if (branding) { + partial = { + lightTheme: branding?.lightTheme, + darkTheme: branding?.darkTheme, + }; + } return (
- +
{children}
diff --git a/apps/login/app/page.tsx b/apps/login/app/page.tsx index ccc59dfbc0e..d5a4f10f13e 100644 --- a/apps/login/app/page.tsx +++ b/apps/login/app/page.tsx @@ -4,11 +4,9 @@ import Link from "next/link"; export default function Page() { return (
-

- Pages -

+

Pages

-
+
{demos.map((section) => { return (
@@ -21,14 +19,12 @@ export default function Page() { -
- {item.name} -
+
{item.name}
{item.description ? ( -
+
{item.description}
) : null} diff --git a/apps/login/next.config.js b/apps/login/next.config.js index 47304cc9a2d..b829b7bf2a0 100755 --- a/apps/login/next.config.js +++ b/apps/login/next.config.js @@ -7,6 +7,12 @@ const nextConfig = { }, images: { remotePatterns: [ + { + protocol: "https", + hostname: process.env.ZITADEL_API_URL.replace("https://", ""), + port: "", + pathname: "/**", + }, { protocol: "https", hostname: "zitadel.com", diff --git a/apps/login/ui/Logo.tsx b/apps/login/ui/Logo.tsx new file mode 100644 index 00000000000..af692d9fe5c --- /dev/null +++ b/apps/login/ui/Logo.tsx @@ -0,0 +1,33 @@ +import Image from "next/image"; + +type Props = { + darkSrc: string; + lightSrc: string; + height?: number; + width?: number; +}; + +export function Logo({ lightSrc, darkSrc, height = 40, width = 147.5 }: Props) { + return ( + <> +
+ logo +
+
+ logo +
+ + ); +} From d6145710f824787f636e951fb6ba5d5e7c60467d Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 23 May 2023 10:14:31 +0200 Subject: [PATCH 17/39] no fallback --- apps/login/app/(login)/layout.tsx | 4 ++-- apps/login/ui/Logo.tsx | 40 +++++++++++++++++-------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/apps/login/app/(login)/layout.tsx b/apps/login/app/(login)/layout.tsx index a092977f3fe..4979b00fab8 100644 --- a/apps/login/app/(login)/layout.tsx +++ b/apps/login/app/(login)/layout.tsx @@ -21,8 +21,8 @@ export default async function Layout({
diff --git a/apps/login/ui/Logo.tsx b/apps/login/ui/Logo.tsx index af692d9fe5c..afb1b79e4e1 100644 --- a/apps/login/ui/Logo.tsx +++ b/apps/login/ui/Logo.tsx @@ -10,24 +10,28 @@ type Props = { export function Logo({ lightSrc, darkSrc, height = 40, width = 147.5 }: Props) { return ( <> -
- logo -
-
- logo -
+ {darkSrc && ( +
+ logo +
+ )} + {lightSrc && ( +
+ logo +
+ )} ); } From 473b84041e9dd44f779d81afb656a1f90487c920 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 23 May 2023 10:14:53 +0200 Subject: [PATCH 18/39] optional src --- apps/login/ui/Logo.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/login/ui/Logo.tsx b/apps/login/ui/Logo.tsx index afb1b79e4e1..09819f2ac35 100644 --- a/apps/login/ui/Logo.tsx +++ b/apps/login/ui/Logo.tsx @@ -1,8 +1,8 @@ import Image from "next/image"; type Props = { - darkSrc: string; - lightSrc: string; + darkSrc?: string; + lightSrc?: string; height?: number; width?: number; }; From 92cacbeae401eca738c914de92b396f17ff91f04 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 23 May 2023 10:15:51 +0200 Subject: [PATCH 19/39] show with data --- apps/login/app/(login)/layout.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/login/app/(login)/layout.tsx b/apps/login/app/(login)/layout.tsx index 4979b00fab8..d346d2bdba6 100644 --- a/apps/login/app/(login)/layout.tsx +++ b/apps/login/app/(login)/layout.tsx @@ -20,12 +20,14 @@ export default async function Layout({ return (
- + {branding && ( + + )}
{children}
From d3956927d1c947e06998900a9ba62c31a76c94ee Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 23 May 2023 11:15:47 +0200 Subject: [PATCH 20/39] reset error on retry, keyframes --- apps/login/tailwind.config.js | 22 ++++++++++++++++++++++ apps/login/ui/GlobalNav.tsx | 4 ++-- apps/login/ui/PasswordForm.tsx | 3 ++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/login/tailwind.config.js b/apps/login/tailwind.config.js index eff117633da..c7ef01f23aa 100644 --- a/apps/login/tailwind.config.js +++ b/apps/login/tailwind.config.js @@ -50,6 +50,28 @@ module.exports = { theme: { extend: { colors, + animation: { + shake: "shake .8s cubic-bezier(.36,.07,.19,.97) both;", + }, + keyframes: { + shake: { + "10%, 90%": { + transform: "translate3d(-1px, 0, 0)", + }, + + "20%, 80%": { + transform: "translate3d(2px, 0, 0)", + }, + + "30%, 50%, 70%": { + transform: "translate3d(-4px, 0, 0)", + }, + + "40%, 60%": { + transform: "translate3d(4px, 0, 0)", + }, + }, + }, }, }, plugins: [require("@tailwindcss/forms")], diff --git a/apps/login/ui/GlobalNav.tsx b/apps/login/ui/GlobalNav.tsx index 611c4c50336..89d0d10eb48 100644 --- a/apps/login/ui/GlobalNav.tsx +++ b/apps/login/ui/GlobalNav.tsx @@ -13,7 +13,7 @@ export function GlobalNav() { const close = () => setIsOpen(false); return ( -
+
-
+
Date: Tue, 23 May 2023 11:24:00 +0200 Subject: [PATCH 21/39] rm logs --- apps/login/app/(login)/register/page.tsx | 2 -- apps/login/lib/zitadel.ts | 2 -- apps/login/ui/UsernameForm.tsx | 1 - apps/login/ui/VerifyEmailForm.tsx | 1 - 4 files changed, 6 deletions(-) diff --git a/apps/login/app/(login)/register/page.tsx b/apps/login/app/(login)/register/page.tsx index f873ddec76a..0762ff32249 100644 --- a/apps/login/app/(login)/register/page.tsx +++ b/apps/login/app/(login)/register/page.tsx @@ -11,8 +11,6 @@ export default async function Page() { server ); - console.log(legal); - return (

Register

diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 7f723c01a89..201869c10d6 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -121,7 +121,6 @@ export function listSessions( ): Promise { const sessionService = session.getSession(server); const query = { offset: 0, limit: 100, asc: true }; - console.log(ids); const queries = [{ idsQuery: { ids } }]; return sessionService.listSessions({ queries: queries }, {}); } @@ -151,7 +150,6 @@ export function addHumanUser( } ) .then((resp: AddHumanUserResponse) => { - console.log("added user", resp.userId); return resp.userId; }); } diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx index e68f8656e11..4717c81e2ad 100644 --- a/apps/login/ui/UsernameForm.tsx +++ b/apps/login/ui/UsernameForm.tsx @@ -43,7 +43,6 @@ export default function UsernameForm() { function submitUsernameAndContinue(value: Inputs): Promise { return submitUsername(value).then(({ factors }) => { - console.log(factors); return router.push( `/password?` + new URLSearchParams({ loginName: `${factors.user.loginName}` }) diff --git a/apps/login/ui/VerifyEmailForm.tsx b/apps/login/ui/VerifyEmailForm.tsx index df1f77ab96c..b29c79c5ca3 100644 --- a/apps/login/ui/VerifyEmailForm.tsx +++ b/apps/login/ui/VerifyEmailForm.tsx @@ -85,7 +85,6 @@ export default function VerifyEmailForm({ userId, code, submit }: Props) { } function submitCodeAndContinue(value: Inputs): Promise { - console.log(value); return submitCode(value).then((resp: any) => { return router.push(`/username`); }); From ca8fa25ed4839aab9b1c2e0f32a9dfb07a8d3d6d Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Tue, 23 May 2023 12:10:36 +0200 Subject: [PATCH 22/39] Delete zitadel-admin-sa.json --- acceptance/machinekey/zitadel-admin-sa.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 acceptance/machinekey/zitadel-admin-sa.json diff --git a/acceptance/machinekey/zitadel-admin-sa.json b/acceptance/machinekey/zitadel-admin-sa.json deleted file mode 100644 index 61ae3baab8e..00000000000 --- a/acceptance/machinekey/zitadel-admin-sa.json +++ /dev/null @@ -1 +0,0 @@ -{"type":"serviceaccount","keyId":"215296164894277635","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAx7HqIdjxIC7pjaA9bH8bec9r63uWrzVvs4GtoxHdJE0+GaaV\nR4ehvXLqVHnLcx6rXgSBwi8HzRJ+1sZSr6nxHVnNjgVK9Ewh08uA0xaHJpwmQH7I\npRiWZ2B1JQ4a7o1S9q+ylHWYpa5b+dm/trySAshhn/1DpEuty6b0DD6hcNsm+Gut\nxkJwPkoe0OK8BC+6b5hd3ISWkHI8k7Ny1zQGNWKkcbcztdBxJbtXRxouiSfcs+FP\ndbCMEXcY8y2ief8/AdBUnfGBr+wJuMnUKEOk7FDub5lkG04+Nzx3G9TAv1poovxu\n63ORWFqbmrUI9ajpZqaO7MjPUkJiRtGrP8EIgQIDAQABAoIBABLSWRABo5B1+3tm\nzANK46sSesiCKIyxC6suoNAK4f4G7cB7K/zG8t6pUx7NL/jUvjApWpWT2zdf8+aI\nYMi/ysMKcM5e/1aRSmrs9mJu8CQaeZyAlqbA74Qk2kW4058CYPxxSPKnNtD+4xpk\nhKZTlat/eXwb4eE9ZM8aGEyDJrI9OwXOKluZaFivxSHV4gH8tUQKl0VX8T1wi8sw\nErKEK0LhV9EY9C9ovZFrUS5SJM4thVJWq/TFbwtgvufGhUiNYA4mpJt2lpUb+2hh\ntcdUWxPIMQ6/xfEEavownMYt/kXZ0pELItAA/lfUe59gRvbwBCkk1kCvrzz3w6Vf\ndak7qbECgYEA6SDd2vUxPUM81PE8FFU3A/ReNORFaTPyOC9w7d+ANg0tSaBhu89Q\n7atP066VBcRihmkTSOwBhZT1EgbbFyPyEY4kWYgkywgWNpslpV4zWdf/YfIA6PV0\nqsYhqGEioCZOBysjcvyEonECMIjhNEV+7r9z2PhivjjNlS8iffM7+h0CgYEA20la\nAvwDUVBWxsLOKF9pI9zVer3X39hYA5Lj0xCy3n6ivPr+oZ6YgVuhvU0JfCQJiIc7\nAt9VGW8hU4gX0Msaie7lw4uzLpt2Jnx14hMfvCytMDJ27JbFJO0/J80bSyrYOg92\nWmuFsT4DG4tYeqmodltloQuRi3TsV4KQbI4nWrUCgYEAnMmJ67RzhSwvQVdsfJBD\nC9nRcekD9ZkGElt1q8VgyPlbt+SxVko7t1w7t7X2a4aEbLlGSwjOjTR9Db6moo1+\nQ+hA8iXp+NJbfiYhz3HnP0lRbGIB0qsh56iRAlkxnFumppOQp8jEuysdud6U9z61\n+4OvOgDzB9PUD70/iU2IzNECgYB3FzcTN9p1ZrQ57cIYmvh2yZAGwONkZhImnres\nAP2jaBLpn0Z6b69TUXhdXmEwUkH7K7YYsUF+NqRawSZt9l/LWTrfIBeH0FYyQJWk\n2c36XCkSa9W6V4dpro8GBpY0FGip34vfP1W0FIGLi/nZZBphbPGcrLO2kSFpxTa6\nPdmRoQKBgQDPAeYf1U/IhUYQlAM/78JuB2pzun3L/PrUWdXvYY5KM/8F2onwbv3S\ny5o9ZwSX8snIeCxXZz2/KNSK1OflW0pZtHegRTjZCsgkiXuEYNh4fnwLGOznl1Mh\nPzUoITq2bR6FGeVrut1IbuyvFmz5oWxnUNoEErtn54Cq2jP1m1nQ8Q==\n-----END RSA PRIVATE KEY-----\n","userId":"215296164893884419"} From 3786f40f4eb53eaea79154ebacd9ab49d4f08191 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 11:00:15 +0200 Subject: [PATCH 23/39] Update packages/zitadel-server/src/v2/settings/settings.ts Co-authored-by: Elio Bischof --- packages/zitadel-server/src/v2/settings/settings.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/zitadel-server/src/v2/settings/settings.ts b/packages/zitadel-server/src/v2/settings/settings.ts index e3cc156e7e9..3adcf32a625 100644 --- a/packages/zitadel-server/src/v2/settings/settings.ts +++ b/packages/zitadel-server/src/v2/settings/settings.ts @@ -8,7 +8,6 @@ import { import { ZitadelServer, createClient, getServers } from "../../server"; export const getSettings = (server?: string | ZitadelServer) => { - console.log("init settings"); let config; if (server && typeof server === "string") { const apps = getServers(); From 316e116104217e11e269734d89d64997a5a29614 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 14:12:38 +0200 Subject: [PATCH 24/39] theme, cookie --- apps/login/ui/Theme.tsx | 47 +++++++++++++++++-------------------- apps/login/utils/cookies.ts | 4 +--- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/apps/login/ui/Theme.tsx b/apps/login/ui/Theme.tsx index 20ae7244b47..7f52e2c6284 100644 --- a/apps/login/ui/Theme.tsx +++ b/apps/login/ui/Theme.tsx @@ -1,11 +1,9 @@ "use client"; -import { Switch } from "@headlessui/react"; -import { MoonIcon, SunIcon } from "@heroicons/react/24/outline"; +import React, { useEffect, useState } from "react"; import { useTheme } from "next-themes"; -import { useEffect, useState } from "react"; -export default function Theme() { +function Theme() { const { resolvedTheme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); @@ -21,28 +19,27 @@ export default function Theme() { } return ( - setTheme(checked ? "dark" : "light")} - className={`${ - isDark - ? "!bg-gray-800 dark:bg-background-dark-400" - : "!bg-gray-200 dark:bg-background-dark-400" - } - relative inline-flex h-4 w-9 items-center rounded-full`} +
- - + + + +
); } + +export default Theme; diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 857713a4183..00502cc830f 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -81,9 +81,7 @@ export async function removeSessionFromCookie( ? JSON.parse(stringifiedCookie?.value) : [session]; - const filteredSessions = sessions.filter( - (session) => session.id !== session.id - ); + const filteredSessions = sessions.filter((s) => s.id !== session.id); // @ts-ignore return cookiesList.set({ From 4ec963847d296d5196cb62c2770802d4fbf2ec03 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 14:28:55 +0200 Subject: [PATCH 25/39] searchparam --- apps/login/app/(login)/password/page.tsx | 8 ++++++-- apps/login/ui/PasswordForm.tsx | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index e057dab4254..ba763b1f10d 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -4,11 +4,15 @@ import PasswordForm from "#/ui/PasswordForm"; import UserAvatar from "#/ui/UserAvatar"; import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; -export default async function Page({ searchParams }: { searchParams: any }) { +export default async function Page({ + searchParams, +}: { + searchParams: Record; +}) { const { loginName } = searchParams; const sessionFactors = await loadSession(loginName); - async function loadSession(loginName: string) { + async function loadSession(loginName?: string) { try { const recent = await getMostRecentCookieWithLoginname(loginName); diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx index a771838ce25..33b5bf6166a 100644 --- a/apps/login/ui/PasswordForm.tsx +++ b/apps/login/ui/PasswordForm.tsx @@ -13,7 +13,7 @@ type Inputs = { }; type Props = { - loginName: string; + loginName?: string; }; export default function PasswordForm({ loginName }: Props) { From f0bd712859ce9833006574752bb2096834d83a4e Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 14:49:07 +0200 Subject: [PATCH 26/39] cleaner navigation --- apps/login/app/layout.tsx | 10 -------- apps/login/ui/Byline.tsx | 13 ---------- apps/login/ui/GlobalNav.tsx | 50 +++++++++++++++++++++++-------------- apps/login/ui/Theme.tsx | 5 ++-- 4 files changed, 34 insertions(+), 44 deletions(-) delete mode 100644 apps/login/ui/Byline.tsx diff --git a/apps/login/app/layout.tsx b/apps/login/app/layout.tsx index 65b9d41fbe1..ddde9f36bf3 100644 --- a/apps/login/app/layout.tsx +++ b/apps/login/app/layout.tsx @@ -62,16 +62,6 @@ export default async function RootLayout({ {children}
- -
-
- -
-
diff --git a/apps/login/ui/Byline.tsx b/apps/login/ui/Byline.tsx deleted file mode 100644 index bbaa55efad7..00000000000 --- a/apps/login/ui/Byline.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Theme from "./Theme"; - -export default function Byline() { - return ( -
-
-
By
-
ZITADEL
-
- -
- ); -} diff --git a/apps/login/ui/GlobalNav.tsx b/apps/login/ui/GlobalNav.tsx index 89d0d10eb48..d24864ff0d3 100644 --- a/apps/login/ui/GlobalNav.tsx +++ b/apps/login/ui/GlobalNav.tsx @@ -7,6 +7,7 @@ import { useSelectedLayoutSegment, usePathname } from "next/navigation"; import clsx from "clsx"; import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/solid"; import { useState } from "react"; +import Theme from "./Theme"; export function GlobalNav() { const [isOpen, setIsOpen] = useState(false); @@ -29,27 +30,34 @@ export function GlobalNav() {
- + +
+ + +
); diff --git a/apps/login/ui/Theme.tsx b/apps/login/ui/Theme.tsx index 7f52e2c6284..126d9403ff4 100644 --- a/apps/login/ui/Theme.tsx +++ b/apps/login/ui/Theme.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { useTheme } from "next-themes"; +import { MoonIcon, SunIcon } from "@heroicons/react/24/outline"; function Theme() { const { resolvedTheme, setTheme } = useTheme(); @@ -28,7 +29,7 @@ function Theme() { }`} onClick={() => setTheme("dark")} > - +
); From 34ec74badfd9d5d672a3912a94f6435715003f9a Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 14:56:06 +0200 Subject: [PATCH 27/39] red warn color --- apps/login/app/(login)/error.tsx | 4 ++-- apps/login/ui/Boundary.tsx | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/login/app/(login)/error.tsx b/apps/login/app/(login)/error.tsx index c0885c16dad..11d47e07c9b 100644 --- a/apps/login/app/(login)/error.tsx +++ b/apps/login/app/(login)/error.tsx @@ -10,9 +10,9 @@ export default function Error({ error, reset }: any) { }, [error]); return ( - +
-
+
Error: {error?.message}
diff --git a/apps/login/ui/Boundary.tsx b/apps/login/ui/Boundary.tsx index ffda6e97372..ebfa0c6b699 100644 --- a/apps/login/ui/Boundary.tsx +++ b/apps/login/ui/Boundary.tsx @@ -8,15 +8,16 @@ const Label = ({ }: { children: React.ReactNode; animateRerendering?: boolean; - color?: "default" | "pink" | "blue" | "violet" | "cyan" | "orange"; + color?: "default" | "pink" | "blue" | "violet" | "cyan" | "orange" | "red"; }) => { return (
{ return ( @@ -48,6 +49,7 @@ export const Boundary = ({ "border-pink-500": color === "pink", "border-blue-500": color === "blue", "border-cyan-500": color === "cyan", + "border-red-500": color === "red", "border-violet-500": color === "violet", "border-orange-500": color === "orange", "animate-[rerender_1s_ease-in-out_1] text-pink-500": animateRerendering, @@ -55,7 +57,7 @@ export const Boundary = ({ >
Date: Wed, 24 May 2023 16:27:35 +0200 Subject: [PATCH 28/39] cleanup --- apps/login/app/(login)/accounts/page.tsx | 8 +-- apps/login/app/layout.tsx | 4 +- apps/login/lib/zitadel.ts | 64 +++++++++--------------- packages/zitadel-server/src/index.ts | 12 ++++- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index 7f87ff32541..fbdc5c9320e 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -1,3 +1,4 @@ +import { Session } from "#/../../packages/zitadel-server/dist"; import { listSessions, server } from "#/lib/zitadel"; import Alert from "#/ui/Alert"; import { Avatar } from "#/ui/Avatar"; @@ -6,21 +7,22 @@ import { UserPlusIcon, XCircleIcon } from "@heroicons/react/24/outline"; import moment from "moment"; import Link from "next/link"; -async function loadSessions() { +async function loadSessions(): Promise { const ids = await getAllSessionIds(); if (ids && ids.length) { - return listSessions( + const response = await listSessions( server, ids.filter((id: string | undefined) => !!id) ); + return response?.sessions ?? []; } else { return []; } } export default async function Page() { - const { sessions } = await loadSessions(); + const sessions = await loadSessions(); return (
diff --git a/apps/login/app/layout.tsx b/apps/login/app/layout.tsx index ddde9f36bf3..04712ebd269 100644 --- a/apps/login/app/layout.tsx +++ b/apps/login/app/layout.tsx @@ -2,7 +2,6 @@ import "#/styles/globals.scss"; import { AddressBar } from "#/ui/AddressBar"; import { GlobalNav } from "#/ui/GlobalNav"; import { Lato } from "next/font/google"; -import Byline from "#/ui/Byline"; import { LayoutProviders } from "#/ui/LayoutProviders"; import { Analytics } from "@vercel/analytics/react"; import ThemeWrapper from "#/ui/ThemeWrapper"; @@ -25,8 +24,7 @@ export default async function RootLayout({ // later only shown with dev mode enabled const showNav = true; - // const general = await getGeneralSettings(server); - const branding: BrandingSettings = await getBrandingSettings(server); + const branding = await getBrandingSettings(server); let partial: Partial | undefined; if (branding) { partial = { diff --git a/apps/login/lib/zitadel.ts b/apps/login/lib/zitadel.ts index 201869c10d6..4d514d1758e 100644 --- a/apps/login/lib/zitadel.ts +++ b/apps/login/lib/zitadel.ts @@ -7,10 +7,18 @@ import { initializeServer, session, GetGeneralSettingsResponse, + CreateSessionResponse, GetBrandingSettingsResponse, GetPasswordComplexitySettingsResponse, GetLegalAndSupportSettingsResponse, AddHumanUserResponse, + BrandingSettings, + ListSessionsResponse, + LegalAndSupportSettings, + PasswordComplexitySettings, + GetSessionResponse, + VerifyEmailResponse, + SetSessionResponse, } from "@zitadel/server"; export const zitadelConfig: ZitadelServerOptions = { @@ -28,67 +36,47 @@ if (!getServers().length) { export function getBrandingSettings( server: ZitadelServer -): Promise { - // settings.branding_settings.BrandingSettings +): Promise { const settingsService = settings.getSettings(server); return settingsService - .getBrandingSettings( - {}, - { - // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") - } - ) + .getBrandingSettings({}, {}) .then((resp: GetBrandingSettingsResponse) => resp.settings); } export function getGeneralSettings( server: ZitadelServer -): Promise { - // settings.branding_settings.BrandingSettings +): Promise { const settingsService = settings.getSettings(server); return settingsService - .getGeneralSettings( - {}, - { - // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") - } - ) + .getGeneralSettings({}, {}) .then((resp: GetGeneralSettingsResponse) => resp.supportedLanguages); } export function getLegalAndSupportSettings( server: ZitadelServer -): Promise { +): Promise { const settingsService = settings.getSettings(server); return settingsService - .getLegalAndSupportSettings( - {}, - { - // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") - } - ) - .then((resp: GetLegalAndSupportSettingsResponse) => resp.settings); + .getLegalAndSupportSettings({}, {}) + .then((resp: GetLegalAndSupportSettingsResponse) => { + return resp.settings; + }); } export function getPasswordComplexitySettings( server: ZitadelServer -): Promise { +): Promise { const settingsService = settings.getSettings(server); return settingsService - .getPasswordComplexitySettings( - {}, - { - // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") - } - ) + .getPasswordComplexitySettings({}, {}) .then((resp: GetPasswordComplexitySettingsResponse) => resp.settings); } export function createSession( server: ZitadelServer, loginName: string -): Promise { +): Promise { const sessionService = session.getSession(server); return sessionService.createSession({ checks: { user: { loginName } } }, {}); } @@ -98,7 +86,7 @@ export function setSession( sessionId: string, sessionToken: string, password: string -): Promise { +): Promise { const sessionService = session.getSession(server); return sessionService.setSession( { sessionId, sessionToken, checks: { password: { password } } }, @@ -110,7 +98,7 @@ export function getSession( server: ZitadelServer, sessionId: string, sessionToken: string -): Promise { +): Promise { const sessionService = session.getSession(server); return sessionService.getSession({ sessionId, sessionToken }, {}); } @@ -118,7 +106,7 @@ export function getSession( export function listSessions( server: ZitadelServer, ids: string[] -): Promise { +): Promise { const sessionService = session.getSession(server); const query = { offset: 0, limit: 100, asc: true }; const queries = [{ idsQuery: { ids } }]; @@ -145,9 +133,7 @@ export function addHumanUser( profile: { firstName, lastName }, password: { password }, }, - { - // metadata: orgMetadata(process.env.ZITADEL_ORG_ID ?? "") - } + {} ) .then((resp: AddHumanUserResponse) => { return resp.userId; @@ -158,7 +144,7 @@ export function verifyEmail( server: ZitadelServer, userId: string, verificationCode: string -): Promise { +): Promise { const userservice = user.getUser(server); return userservice.verifyEmail( { diff --git a/packages/zitadel-server/src/index.ts b/packages/zitadel-server/src/index.ts index 7958efda9a6..bc8b26f2835 100644 --- a/packages/zitadel-server/src/index.ts +++ b/packages/zitadel-server/src/index.ts @@ -11,13 +11,23 @@ export { Theme, } from "./proto/server/zitadel/settings/v2alpha/branding_settings"; +export { Session } from "./proto/server/zitadel/session/v2alpha/session"; +export { + ListSessionsResponse, + GetSessionResponse, + CreateSessionResponse, + SetSessionResponse, +} from "./proto/server/zitadel/session/v2alpha/session_service"; export { GetPasswordComplexitySettingsResponse, GetBrandingSettingsResponse, GetLegalAndSupportSettingsResponse, GetGeneralSettingsResponse, } from "./proto/server/zitadel/settings/v2alpha/settings_service"; -export { AddHumanUserResponse } from "./proto/server/zitadel/user/v2alpha/user_service"; +export { + AddHumanUserResponse, + VerifyEmailResponse, +} from "./proto/server/zitadel/user/v2alpha/user_service"; export { type LegalAndSupportSettings } from "./proto/server/zitadel/settings/v2alpha/legal_settings"; export { type PasswordComplexitySettings } from "./proto/server/zitadel/settings/v2alpha/password_settings"; From fc70515cbd12ea7b3e15869bc891ca752960eaae Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 16:37:52 +0200 Subject: [PATCH 29/39] layout --- apps/login/app/(login)/layout.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/login/app/(login)/layout.tsx b/apps/login/app/(login)/layout.tsx index d346d2bdba6..381ffbd2c35 100644 --- a/apps/login/app/(login)/layout.tsx +++ b/apps/login/app/(login)/layout.tsx @@ -1,5 +1,4 @@ import { BrandingSettings } from "@zitadel/server"; -import { ZitadelLogo } from "#/ui/ZitadelLogo"; import React from "react"; import { getBrandingSettings, server } from "#/lib/zitadel"; import { Logo } from "#/ui/Logo"; @@ -9,7 +8,7 @@ export default async function Layout({ }: { children: React.ReactNode; }) { - const branding: BrandingSettings = await getBrandingSettings(server); + const branding = await getBrandingSettings(server); let partial: Partial | undefined; if (branding) { partial = { From 04f79ee319cf99f3d12211b99ac5b7653cf560ff Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 16:41:06 +0200 Subject: [PATCH 30/39] throw error --- apps/login/app/(login)/password/page.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/apps/login/app/(login)/password/page.tsx b/apps/login/app/(login)/password/page.tsx index ba763b1f10d..e7c0eb21808 100644 --- a/apps/login/app/(login)/password/page.tsx +++ b/apps/login/app/(login)/password/page.tsx @@ -13,15 +13,13 @@ export default async function Page({ const sessionFactors = await loadSession(loginName); async function loadSession(loginName?: string) { - try { - const recent = await getMostRecentCookieWithLoginname(loginName); + const recent = await getMostRecentCookieWithLoginname(loginName); - return getSession(server, recent.id, recent.token).then(({ session }) => { - return session; - }); - } catch (error) { - console.log(error); - } + return getSession(server, recent.id, recent.token).then((response) => { + if (response?.session) { + return response.session; + } + }); } return ( @@ -40,7 +38,7 @@ export default async function Page({ {sessionFactors && ( From 195e10d40206519b2fa39648c680f61abf214238 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 16:57:35 +0200 Subject: [PATCH 31/39] rm unused code --- apps/login/next.config.js | 12 ------------ apps/login/utils/cookies.ts | 11 ----------- 2 files changed, 23 deletions(-) diff --git a/apps/login/next.config.js b/apps/login/next.config.js index b829b7bf2a0..841a2fc2fe7 100755 --- a/apps/login/next.config.js +++ b/apps/login/next.config.js @@ -13,18 +13,6 @@ const nextConfig = { port: "", pathname: "/**", }, - { - protocol: "https", - hostname: "zitadel.com", - port: "", - pathname: "/**", - }, - { - protocol: "https", - hostname: "zitadel.cloud", - port: "", - pathname: "/**", - }, ], }, }; diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 00502cc830f..74206a23db4 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -9,17 +9,6 @@ export type SessionCookie = { changeDate: string; }; -async function set(sessions: SessionCookie[]) { - const cookiesList = cookies(); - // @ts-ignore - cookiesList.set({ - name: "sessions", - value: JSON.stringify(sessions), - httpOnly: true, - path: "/", - }); -} - export async function addSessionToCookie(session: SessionCookie): Promise { const cookiesList = cookies(); const stringifiedCookie = cookiesList.get("sessions"); From 4649d309adbd5d2993019ee2809dc04cf67773ad Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 17:00:25 +0200 Subject: [PATCH 32/39] Update apps/login/ui/PasswordForm.tsx Co-authored-by: Elio Bischof --- apps/login/ui/PasswordForm.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/login/ui/PasswordForm.tsx b/apps/login/ui/PasswordForm.tsx index 33b5bf6166a..f28ce0772a1 100644 --- a/apps/login/ui/PasswordForm.tsx +++ b/apps/login/ui/PasswordForm.tsx @@ -42,15 +42,12 @@ export default function PasswordForm({ loginName }: Props) { const response = await res.json(); + setLoading(false); if (!res.ok) { - setLoading(false); - console.log(response); setError(response.details); return Promise.reject(response.details); - } else { - setLoading(false); - return response; } + return response; } function submitPasswordAndContinue(value: Inputs): Promise { From bd6e830eb9becc7a63c3e0355d54d32bf1d71254 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 17:00:42 +0200 Subject: [PATCH 33/39] rename --- apps/login/ui/Avatar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/login/ui/Avatar.tsx b/apps/login/ui/Avatar.tsx index 278c6eed2db..096f97de331 100644 --- a/apps/login/ui/Avatar.tsx +++ b/apps/login/ui/Avatar.tsx @@ -11,7 +11,7 @@ interface AvatarProps { shadow?: boolean; } -function getCredentials(name: string, loginName: string) { +function getInitials(name: string, loginName: string) { let credentials = ""; if (name) { const split = name.split(" "); @@ -47,7 +47,7 @@ export function Avatar({ shadow, }: AvatarProps) { const { resolvedTheme } = useTheme(); - const credentials = getCredentials(name ?? loginName, loginName); + const credentials = getInitials(name ?? loginName, loginName); const color: ColorShade = getColorHash(loginName); From 3810401d8f3b6b43d44a470e43b197632445a110 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 17:04:49 +0200 Subject: [PATCH 34/39] Update apps/login/ui/UsernameForm.tsx Co-authored-by: Elio Bischof --- apps/login/ui/UsernameForm.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/login/ui/UsernameForm.tsx b/apps/login/ui/UsernameForm.tsx index 4717c81e2ad..1e63dcb560c 100644 --- a/apps/login/ui/UsernameForm.tsx +++ b/apps/login/ui/UsernameForm.tsx @@ -32,12 +32,10 @@ export default function UsernameForm() { }), }); + setLoading(false); if (!res.ok) { - setLoading(false); throw new Error("Failed to set user"); } - - setLoading(false); return res.json(); } From 841e9a6b3ed2fb0f9aed385632aed1b20f878a77 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 17:15:59 +0200 Subject: [PATCH 35/39] session date type --- apps/login/app/(login)/signedin/page.tsx | 21 ++--- apps/login/app/session/route.ts | 115 +++++++++++++++-------- 2 files changed, 85 insertions(+), 51 deletions(-) diff --git a/apps/login/app/(login)/signedin/page.tsx b/apps/login/app/(login)/signedin/page.tsx index 3a6a76171b0..2231bcb13a0 100644 --- a/apps/login/app/(login)/signedin/page.tsx +++ b/apps/login/app/(login)/signedin/page.tsx @@ -1,18 +1,15 @@ import { getSession, server } from "#/lib/zitadel"; -import PasswordForm from "#/ui/PasswordForm"; import UserAvatar from "#/ui/UserAvatar"; import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; async function loadSession(loginName: string) { - try { - const recent = await getMostRecentCookieWithLoginname(`${loginName}`); + const recent = await getMostRecentCookieWithLoginname(`${loginName}`); - return getSession(server, recent.id, recent.token).then(({ session }) => { - return session; - }); - } catch (error) { - throw new Error("Session could not be loaded!"); - } + return getSession(server, recent.id, recent.token).then((response) => { + if (response?.session) { + return response.session; + } + }); } export default async function Page({ searchParams }: { searchParams: any }) { @@ -21,12 +18,12 @@ export default async function Page({ searchParams }: { searchParams: any }) { return (
-

{`Welcome ${sessionFactors.factors?.user?.displayName}`}

+

{`Welcome ${sessionFactors?.factors?.user?.displayName}`}

You are signed in.

diff --git a/apps/login/app/session/route.ts b/apps/login/app/session/route.ts index a4f3cda1e0d..142f5bf51f2 100644 --- a/apps/login/app/session/route.ts +++ b/apps/login/app/session/route.ts @@ -13,24 +13,40 @@ export async function POST(request: NextRequest) { const { loginName } = body; const createdSession = await createSession(server, loginName); - - return getSession( - server, - createdSession.sessionId, - createdSession.sessionToken - ).then(({ session }) => { - const sessionCookie: SessionCookie = { - id: createdSession.sessionId, - token: createdSession.sessionToken, - changeDate: session.changeDate, - loginName: session.factors.user.loginName, - }; - return addSessionToCookie(sessionCookie).then(() => { - return NextResponse.json({ factors: session.factors }); + if (createdSession) { + return getSession( + server, + createdSession.sessionId, + createdSession.sessionToken + ).then((response) => { + if (response?.session && response.session?.factors?.user?.loginName) { + const sessionCookie: SessionCookie = { + id: createdSession.sessionId, + token: createdSession.sessionToken, + changeDate: response.session.changeDate?.toString() ?? "", + loginName: response.session?.factors?.user?.loginName ?? "", + }; + return addSessionToCookie(sessionCookie).then(() => { + return NextResponse.json({ factors: response?.session?.factors }); + }); + } else { + return NextResponse.json( + { + details: + "could not get session or session does not have loginName", + }, + { status: 500 } + ); + } }); - }); + } else { + return NextResponse.error(); + } } else { - return NextResponse.error(); + return NextResponse.json( + { details: "Session could not be created" }, + { status: 500 } + ); } } @@ -48,34 +64,55 @@ export async function PUT(request: NextRequest) { return setSession(server, recent.id, recent.token, password) .then((session) => { - const sessionCookie: SessionCookie = { - id: recent.id, - token: session.sessionToken, - changeDate: session.details.changeDate, - loginName: recent.loginName, - }; + if (session) { + const sessionCookie: SessionCookie = { + id: recent.id, + token: session.sessionToken, + changeDate: session.details?.changeDate?.toString() ?? "", + loginName: recent.loginName, + }; - return getSession(server, sessionCookie.id, sessionCookie.token).then( - ({ session }) => { - const newCookie: SessionCookie = { - id: sessionCookie.id, - token: sessionCookie.token, - changeDate: session.changeDate, - loginName: session.factors.user.loginName, - }; + return getSession(server, sessionCookie.id, sessionCookie.token).then( + (response) => { + if ( + response?.session && + response.session.factors?.user?.loginName + ) { + const { session } = response; + const newCookie: SessionCookie = { + id: sessionCookie.id, + token: sessionCookie.token, + changeDate: session.changeDate?.toString() ?? "", + loginName: session.factors?.user?.loginName ?? "", + }; - return updateSessionCookie(sessionCookie.id, newCookie) - .then(() => { - return NextResponse.json({ factors: session.factors }); - }) - .catch((error) => { + return updateSessionCookie(sessionCookie.id, newCookie) + .then(() => { + return NextResponse.json({ factors: session.factors }); + }) + .catch((error) => { + return NextResponse.json( + { details: "could not set cookie" }, + { status: 500 } + ); + }); + } else { return NextResponse.json( - { details: "could not set cookie" }, + { + details: + "could not get session or session does not have loginName", + }, { status: 500 } ); - }); - } - ); + } + } + ); + } else { + return NextResponse.json( + { details: "Session not be set" }, + { status: 500 } + ); + } }) .catch((error) => { console.error("erasd", error); From dcb7a0e0bfda64feabb7b0a76c7834ece03639ab Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 18:00:45 +0200 Subject: [PATCH 36/39] wrap cookie set --- apps/login/utils/cookies.ts | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 74206a23db4..92509835211 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -9,6 +9,15 @@ export type SessionCookie = { changeDate: string; }; +function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { + // @ts-ignore + return cookiesList.set({ + name: "sessions", + value: JSON.stringify(sessions), + httpOnly: true, + path: "/", + }); +} export async function addSessionToCookie(session: SessionCookie): Promise { const cookiesList = cookies(); const stringifiedCookie = cookiesList.get("sessions"); @@ -27,13 +36,7 @@ export async function addSessionToCookie(session: SessionCookie): Promise { currentSessions = [...currentSessions, session]; } - // @ts-ignore - return cookiesList.set({ - name: "sessions", - value: JSON.stringify(currentSessions), - httpOnly: true, - path: "/", - }); + setSessionHttpOnlyCookie(value: string); } export async function updateSessionCookie( @@ -51,13 +54,7 @@ export async function updateSessionCookie( const foundIndex = sessions.findIndex((session) => session.id === id); sessions[foundIndex] = session; - // @ts-ignore - return cookiesList.set({ - name: "sessions", - value: JSON.stringify(sessions), - httpOnly: true, - path: "/", - }); + return setSessionHttpOnlyCookie(sessions); } export async function removeSessionFromCookie( @@ -72,13 +69,7 @@ export async function removeSessionFromCookie( const filteredSessions = sessions.filter((s) => s.id !== session.id); - // @ts-ignore - return cookiesList.set({ - name: "sessions", - value: JSON.stringify(filteredSessions), - httpOnly: true, - path: "/", - }); + return setSessionHttpOnlyCookie(filteredSessions); } export async function getMostRecentSessionCookie(): Promise { From 399da6a14f801b4d21251a7ba92cc9a8b9263995 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 18:47:30 +0200 Subject: [PATCH 37/39] cookieset --- apps/login/utils/cookies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 92509835211..3032130df3f 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -10,7 +10,7 @@ export type SessionCookie = { }; function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { - // @ts-ignore + // @ts-ignore return cookiesList.set({ name: "sessions", value: JSON.stringify(sessions), @@ -36,7 +36,7 @@ export async function addSessionToCookie(session: SessionCookie): Promise { currentSessions = [...currentSessions, session]; } - setSessionHttpOnlyCookie(value: string); + setSessionHttpOnlyCookie(currentSessions); } export async function updateSessionCookie( From 5e19841bc10c64c59b748a633c90ade9c4a3a51c Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 22:57:45 +0200 Subject: [PATCH 38/39] cookies --- apps/login/utils/cookies.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/login/utils/cookies.ts b/apps/login/utils/cookies.ts index 3032130df3f..43ef8df5399 100644 --- a/apps/login/utils/cookies.ts +++ b/apps/login/utils/cookies.ts @@ -10,6 +10,7 @@ export type SessionCookie = { }; function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { + const cookiesList = cookies(); // @ts-ignore return cookiesList.set({ name: "sessions", @@ -44,7 +45,6 @@ export async function updateSessionCookie( session: SessionCookie ): Promise { const cookiesList = cookies(); - const stringifiedCookie = cookiesList.get("sessions"); const sessions: SessionCookie[] = stringifiedCookie?.value @@ -113,7 +113,6 @@ export async function getMostRecentCookieWithLoginname( loginName?: string ): Promise { const cookiesList = cookies(); - const stringifiedCookie = cookiesList.get("sessions"); if (stringifiedCookie?.value) { From 2c5821ca9aa28db94cf9589e783068d43b421023 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 24 May 2023 23:00:55 +0200 Subject: [PATCH 39/39] sessions typing --- apps/login/app/(login)/accounts/page.tsx | 98 ++++++++++++------------ 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/apps/login/app/(login)/accounts/page.tsx b/apps/login/app/(login)/accounts/page.tsx index fbdc5c9320e..e95693658ef 100644 --- a/apps/login/app/(login)/accounts/page.tsx +++ b/apps/login/app/(login)/accounts/page.tsx @@ -31,57 +31,61 @@ export default async function Page() {
{sessions ? ( - sessions.map((session: any, index: number) => { - const validPassword = session.factors.password?.verifiedAt; - return ( - -
- -
+ sessions + .filter((session) => session?.factors?.user?.loginName) + .map((session, index) => { + const validPassword = session?.factors?.password?.verifiedAt; + return ( + +
+ +
-
- {session.factors.user.displayName} - - {session.factors.user.loginName} - - {validPassword && ( - - {moment(new Date(validPassword)).fromNow()} +
+ + {session.factors?.user?.displayName} - )} -
+ + {session.factors?.user?.loginName} + + {validPassword && ( + + {moment(new Date(validPassword)).fromNow()} + + )} +
- -
- {validPassword ? ( -
- ) : ( -
- )} + +
+ {validPassword ? ( +
+ ) : ( +
+ )} - -
- - ); - }) + +
+ + ); + }) ) : ( No Sessions available! )}