From 331eec5fd710a490fc54354b01340e3e2200ea6c Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Sun, 23 Oct 2022 23:38:11 +0800 Subject: [PATCH 01/15] fix(console): axis calculations in use-position hook --- packages/console/src/hooks/use-position.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/console/src/hooks/use-position.ts b/packages/console/src/hooks/use-position.ts index da6bea222..6a8d88c85 100644 --- a/packages/console/src/hooks/use-position.ts +++ b/packages/console/src/hooks/use-position.ts @@ -196,12 +196,11 @@ export default function usePosition({ const anchorRect = anchorRef.current.getBoundingClientRect(); const overlayRect = overlayRef.current.getBoundingClientRect(); - const { scrollTop, scrollLeft } = document.documentElement; - const verticalTop = anchorRect.y - overlayRect.height + scrollTop - offset.vertical; + const verticalTop = anchorRect.y - overlayRect.height - offset.vertical; const verticalCenter = - anchorRect.y - anchorRect.height / 2 - overlayRect.height / 2 + scrollTop + offset.vertical; - const verticalBottom = anchorRect.y + anchorRect.height + scrollTop + offset.vertical; + anchorRect.y - anchorRect.height / 2 - overlayRect.height / 2 + offset.vertical; + const verticalBottom = anchorRect.y + anchorRect.height + offset.vertical; const verticalPositionMap = { top: verticalTop, @@ -209,11 +208,10 @@ export default function usePosition({ bottom: verticalBottom, }; - const horizontalStart = anchorRect.x + scrollLeft + offset.horizontal; + const horizontalStart = anchorRect.x + offset.horizontal; const horizontalCenter = - anchorRect.x + anchorRect.width / 2 - overlayRect.width / 2 + scrollLeft + offset.horizontal; - const horizontalEnd = - anchorRect.x + anchorRect.width - overlayRect.width + scrollLeft + offset.horizontal; + anchorRect.x + anchorRect.width / 2 - overlayRect.width / 2 + offset.horizontal; + const horizontalEnd = anchorRect.x + anchorRect.width - overlayRect.width + offset.horizontal; const horizontalPositionMap = { start: horizontalStart, From 73d1d4a64954c5000c1a44de3cf1534dea43e38b Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Wed, 26 Oct 2022 15:26:27 +0800 Subject: [PATCH 02/15] chore(phrases): update contents (#2254) --- packages/phrases-ui/src/locales/zh-cn.ts | 2 +- packages/phrases/src/locales/zh-cn/errors.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/phrases-ui/src/locales/zh-cn.ts b/packages/phrases-ui/src/locales/zh-cn.ts index 707459fae..533e00765 100644 --- a/packages/phrases-ui/src/locales/zh-cn.ts +++ b/packages/phrases-ui/src/locales/zh-cn.ts @@ -29,7 +29,7 @@ const translation = { got_it: '知道了', sign_in_with: '通过 {{name}} 登录', forgot_password: '重置密码', - switch_to: '用{{method}}登录', + switch_to: '切换到{{method}}', }, description: { email: '邮箱', diff --git a/packages/phrases/src/locales/zh-cn/errors.ts b/packages/phrases/src/locales/zh-cn/errors.ts index 28629dc3a..cf01bb8bf 100644 --- a/packages/phrases/src/locales/zh-cn/errors.ts +++ b/packages/phrases/src/locales/zh-cn/errors.ts @@ -56,7 +56,7 @@ const errors = { connector_id_mismatch: '传入的连接器 ID 与 session 中保存的记录不一致', connector_session_not_found: '无法找到连接器登录信息,请尝试重新登录。', verification_session_not_found: '验证失败,请重新验证。', - verification_expired: '无密码验证已过期。请返回重新验证。', + verification_expired: '当前页面已超时。为确保你的账号安全,请重新验证。', unauthorized: '请先登录', unsupported_prompt_name: '不支持的 prompt name', }, From 476d7642dfabbc80feff4c5c003036f232c19685 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:12:30 +0000 Subject: [PATCH 03/15] chore(deps): update logto js sdk monorepo packages to v1.0.0-beta.11 --- packages/console/package.json | 2 +- packages/demo-app/package.json | 2 +- packages/integration-tests/package.json | 2 +- pnpm-lock.yaml | 54 ++++++++++--------------- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/packages/console/package.json b/packages/console/package.json index 82ab2c3c9..07e767f61 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -22,7 +22,7 @@ "@logto/language-kit": "1.0.0-beta.20", "@logto/phrases": "workspace:^", "@logto/phrases-ui": "workspace:^", - "@logto/react": "1.0.0-beta.10", + "@logto/react": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@mdx-js/react": "^1.6.22", "@parcel/core": "2.7.0", diff --git a/packages/demo-app/package.json b/packages/demo-app/package.json index 8968ed885..3aa49eb91 100644 --- a/packages/demo-app/package.json +++ b/packages/demo-app/package.json @@ -20,7 +20,7 @@ "@logto/core-kit": "1.0.0-beta.20", "@logto/language-kit": "1.0.0-beta.20", "@logto/phrases": "workspace:^", - "@logto/react": "1.0.0-beta.10", + "@logto/react": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@parcel/core": "2.7.0", "@parcel/transformer-sass": "2.7.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 8b20f21de..ae31215c1 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@jest/types": "^29.1.2", - "@logto/node": "1.0.0-beta.10", + "@logto/node": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@peculiar/webcrypto": "^1.3.3", "@silverhand/eslint-config": "1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a69e77ce3..845cb4563 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,7 +109,7 @@ importers: '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': workspace:^ '@logto/phrases-ui': workspace:^ - '@logto/react': 1.0.0-beta.10 + '@logto/react': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@mdx-js/react': ^1.6.22 '@parcel/core': 2.7.0 @@ -176,7 +176,7 @@ importers: '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': link:../phrases '@logto/phrases-ui': link:../phrases-ui - '@logto/react': 1.0.0-beta.10_react@18.2.0 + '@logto/react': 1.0.0-beta.11_react@18.2.0 '@logto/schemas': link:../schemas '@mdx-js/react': 1.6.22_react@18.2.0 '@parcel/core': 2.7.0 @@ -408,7 +408,7 @@ importers: '@logto/core-kit': 1.0.0-beta.20 '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': workspace:^ - '@logto/react': 1.0.0-beta.10 + '@logto/react': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@parcel/core': 2.7.0 '@parcel/transformer-sass': 2.7.0 @@ -435,7 +435,7 @@ importers: '@logto/core-kit': 1.0.0-beta.20 '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': link:../phrases - '@logto/react': 1.0.0-beta.10_react@18.2.0 + '@logto/react': 1.0.0-beta.11_react@18.2.0 '@logto/schemas': link:../schemas '@parcel/core': 2.7.0 '@parcel/transformer-sass': 2.7.0_@parcel+core@2.7.0 @@ -462,7 +462,7 @@ importers: packages/integration-tests: specifiers: '@jest/types': ^29.1.2 - '@logto/node': 1.0.0-beta.10 + '@logto/node': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@peculiar/webcrypto': ^1.3.3 '@silverhand/eslint-config': 1.3.0 @@ -487,7 +487,7 @@ importers: typescript: ^4.7.4 devDependencies: '@jest/types': 29.1.2 - '@logto/node': 1.0.0-beta.10 + '@logto/node': 1.0.0-beta.11 '@logto/schemas': link:../schemas '@peculiar/webcrypto': 1.3.3 '@silverhand/eslint-config': 1.3.0_swk2g7ygmfleszo5c33j4vooni @@ -2293,19 +2293,19 @@ packages: dev: true optional: true - /@logto/browser/1.0.0-beta.10: - resolution: {integrity: sha512-ziZv8TTWwzK9PgBtioF9Wplfaj0J/InyxSBmfgFS5PX54GAVfOy3uGBi9hGL6HDRPj3SYs2U1bi21YPlF9z/8w==} + /@logto/browser/1.0.0-beta.11: + resolution: {integrity: sha512-Ofdj5UqLzwoW66XGnff+1U6Lix90qI2Q30+TISJz6soCm97oqJdWpY2SRlMAfnvDobsFPqAxyCAz49Cg16h50Q==} dependencies: - '@logto/client': 1.0.0-beta.10 + '@logto/client': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 js-base64: 3.7.2 dev: true - /@logto/client/1.0.0-beta.10: - resolution: {integrity: sha512-XHkOJdvxsBix/8cZ3a6Hx3UiiQkaJBDF9D7BGM8lZxPz5nsGq8YkX5ZRvjVCX9Y3fE3nRb5Iv1ccWvV192lUWg==} + /@logto/client/1.0.0-beta.11: + resolution: {integrity: sha512-7Nl+53JPgB0wjMU9zJH6SrOj4OKn0Kl9U1cumt/IHSYuaDyV7TgvR/HaaeSZuZ0JxbMg66Riee87Qx9Tv46k6A==} dependencies: - '@logto/core-kit': 1.0.0-beta.19 - '@logto/js': 1.0.0-beta.10 + '@logto/core-kit': 1.0.0-beta.20 + '@logto/js': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 camelcase-keys: 7.0.2 jose: 4.6.0 @@ -2333,16 +2333,6 @@ packages: zod: 3.19.1 dev: false - /@logto/core-kit/1.0.0-beta.19: - resolution: {integrity: sha512-cqwfz+Ic/t7mV23QUEXWeRaLTqN71NSv02adaul8VWZQg4IePOlWLLaiTyY6ods88f3ZOarIBlJ5fYexw3J8qg==} - engines: {node: ^16.0.0} - dependencies: - '@logto/language-kit': 1.0.0-beta.20 - color: 4.2.3 - nanoid: 3.3.4 - zod: 3.19.1 - dev: true - /@logto/core-kit/1.0.0-beta.20: resolution: {integrity: sha512-seYvL/aGYRfO4d0FYfKIW/Cu9PnFMRpRM5/oRXwXbcbv+LY1a3TcAX0itrVXeBygIrxiAmWd9DL7CGIWzb48Qg==} engines: {node: ^16.0.0} @@ -2352,10 +2342,10 @@ packages: nanoid: 3.3.4 zod: 3.19.1 - /@logto/js/1.0.0-beta.10: - resolution: {integrity: sha512-mMzverjbeKtGjSb0NmEUHzDBRrXhCPOydCE37yhzL/qORiehyblPqntw3lLrf5oCNUKaxv7PzT5q/Lfbxb3Q8g==} + /@logto/js/1.0.0-beta.11: + resolution: {integrity: sha512-V0cV+T+DFcpqAAIjfdiEuEYG+ePASR7VbPM1zMLv02S/5zz3pStedbLrksZaSVUOmQvsieEF5d5/kZ8ZshQa0A==} dependencies: - '@logto/core-kit': 1.0.0-beta.19 + '@logto/core-kit': 1.0.0-beta.20 '@silverhand/essentials': 1.3.0 camelcase-keys: 7.0.2 jose: 4.6.0 @@ -2368,10 +2358,10 @@ packages: dependencies: zod: 3.19.1 - /@logto/node/1.0.0-beta.10: - resolution: {integrity: sha512-4st77cD1h/bCIrt+BttDHva/I7ibLw0ilvi30uadEwuH0Ih8WFYvlyi2V1wOljd5Ym+fqSMbyKXQQIwba6CenQ==} + /@logto/node/1.0.0-beta.11: + resolution: {integrity: sha512-nsa9RtrzBRmLMJNeNzutJ72bLwZpGUr4lhBAze7YXJi6o9ujvZOLgdvZAZeoSdxxBhTilwbDWNXmj8n3Ndavaw==} dependencies: - '@logto/client': 1.0.0-beta.10 + '@logto/client': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 js-base64: 3.7.2 node-fetch: 2.6.7 @@ -2379,12 +2369,12 @@ packages: - encoding dev: true - /@logto/react/1.0.0-beta.10_react@18.2.0: - resolution: {integrity: sha512-MutQplD5VkUqYIhnaQgNiRpbQTRK/OS2Ut70j99mVax/frImlIu1m1YeuE8NEqGmB9SBQvJoP5kECxVNf2a4Nw==} + /@logto/react/1.0.0-beta.11_react@18.2.0: + resolution: {integrity: sha512-W9L1QrJml4tHlrUkmdaJRsj4ln+SgwGAeyF+QhmKBq5CoSR+EauE63ySLhPn2KFVrc3m/sioejJIVy+1sUe21w==} peerDependencies: react: '>=16.8.0 || ^18.0.0' dependencies: - '@logto/browser': 1.0.0-beta.10 + '@logto/browser': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 react: 18.2.0 dev: true From feccf2b51902bd9a0f80c97f6867ed366c9b0600 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 27 Oct 2022 13:46:01 +0800 Subject: [PATCH 04/15] fix(ui): fix tab key not working bug (#2261) --- packages/ui/src/utils/a11y.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/utils/a11y.ts b/packages/ui/src/utils/a11y.ts index 100becdad..bbec4f556 100644 --- a/packages/ui/src/utils/a11y.ts +++ b/packages/ui/src/utils/a11y.ts @@ -19,6 +19,9 @@ export const onKeyDownHandler = if (typeof callback === 'object') { callback[key]?.(event); - event.preventDefault(); + + if (callback[key]) { + event.preventDefault(); + } } }; From 45a7ac8d6deb92c721808852ac385d7188204a1d Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 27 Oct 2022 13:46:12 +0800 Subject: [PATCH 05/15] style(ui): remove outline button style (#2260) --- .../src/components/Button/index.module.scss | 25 ------------------- packages/ui/src/components/Button/index.tsx | 2 +- .../src/components/ConfirmModal/AcModal.tsx | 2 +- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/packages/ui/src/components/Button/index.module.scss b/packages/ui/src/components/Button/index.module.scss index 18791f966..ba8784ec7 100644 --- a/packages/ui/src/components/Button/index.module.scss +++ b/packages/ui/src/components/Button/index.module.scss @@ -55,21 +55,6 @@ } } -.outline { - border: _.border(var(--color-brand-default)); - background: transparent; - color: var(--color-type-link); - - &.disabled, - &:disabled { - border-color: var(--color-type-disable); - color: var(--color-type-disable); - } - - &:active { - background: var(--color-overlay-brand-pressed); - } -} :global(body.desktop) { .primary { @@ -91,14 +76,4 @@ background: var(--color-overlay-neutral-hover); } } - - .outline { - &:focus-visible { - outline: 3px solid var(--color-overlay-brand-focused); - } - - &:not(:disabled):not(:active):hover { - background: var(--color-overlay-brand-hover); - } - } } diff --git a/packages/ui/src/components/Button/index.tsx b/packages/ui/src/components/Button/index.tsx index 61768432e..6b2098abb 100644 --- a/packages/ui/src/components/Button/index.tsx +++ b/packages/ui/src/components/Button/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import * as styles from './index.module.scss'; -export type ButtonType = 'primary' | 'secondary' | 'outline'; +export type ButtonType = 'primary' | 'secondary'; type BaseProps = Omit, 'type' | 'size' | 'title'> & { htmlType?: 'button' | 'submit' | 'reset'; diff --git a/packages/ui/src/components/ConfirmModal/AcModal.tsx b/packages/ui/src/components/ConfirmModal/AcModal.tsx index 2dc670d56..7b4bb0687 100644 --- a/packages/ui/src/components/ConfirmModal/AcModal.tsx +++ b/packages/ui/src/components/ConfirmModal/AcModal.tsx @@ -37,7 +37,7 @@ const AcModal = ({
{children}
-
From 12eef42c9b03e3ae207dcdec5c8224489f8aac07 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 27 Oct 2022 14:18:34 +0800 Subject: [PATCH 06/15] style(ui): remove autofill styling (#2256) --- packages/ui/src/components/Input/index.module.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/ui/src/components/Input/index.module.scss b/packages/ui/src/components/Input/index.module.scss index 44fa9c63e..7e8d1db7f 100644 --- a/packages/ui/src/components/Input/index.module.scss +++ b/packages/ui/src/components/Input/index.module.scss @@ -35,13 +35,6 @@ &::placeholder { color: var(--color-type-secondary); } - - // Overwrite webkit auto-fill style - &:-webkit-autofill { - box-shadow: 0 0 0 30px var(--color-bg-body) inset; - -webkit-text-fill-color: var(--color-type-primary); - transition: none; - } } &:focus-within { From ee374d19cc1c6f2ee97cefe0d7535d04d9d0f78e Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 27 Oct 2022 16:04:45 +0800 Subject: [PATCH 07/15] fix(ui): fix apple logo (#2259) --- .../src/containers/SocialSignIn/SocialSignInIconList/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/containers/SocialSignIn/SocialSignInIconList/index.tsx b/packages/ui/src/containers/SocialSignIn/SocialSignInIconList/index.tsx index 0d2521ab8..c35fa3ed5 100644 --- a/packages/ui/src/containers/SocialSignIn/SocialSignInIconList/index.tsx +++ b/packages/ui/src/containers/SocialSignIn/SocialSignInIconList/index.tsx @@ -36,7 +36,7 @@ const SocialSignInIconList = ({ { void invokeSocialSignIn(connector); From 4074d694efd8e0949b3b71183cc99f15335bcbf0 Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Thu, 27 Oct 2022 16:56:13 +0800 Subject: [PATCH 08/15] fix(console): useApi hook should not cause component re-render --- packages/console/src/hooks/use-api.ts | 36 ++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/console/src/hooks/use-api.ts b/packages/console/src/hooks/use-api.ts index a5aabc193..d7f229af4 100644 --- a/packages/console/src/hooks/use-api.ts +++ b/packages/console/src/hooks/use-api.ts @@ -2,7 +2,7 @@ import { useLogto } from '@logto/react'; import type { RequestErrorBody } from '@logto/schemas'; import { managementResource } from '@logto/schemas/lib/seeds'; import ky from 'ky'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -19,31 +19,27 @@ export class RequestError extends Error { } } -const useToastError = () => { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - const toastError = async (response: Response) => { - const fallbackErrorMessage = t('errors.unknown_server_error'); - - try { - const data = await response.json(); - toast.error([data.message, data.details].join('\n') || fallbackErrorMessage); - } catch { - toast.error(fallbackErrorMessage); - } - }; - - return toastError; -}; - type Props = { hideErrorToast?: boolean; }; const useApi = ({ hideErrorToast }: Props = {}) => { const { isAuthenticated, getAccessToken } = useLogto(); - const { i18n } = useTranslation(); - const toastError = useToastError(); + const { t, i18n } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + const toastError = useCallback( + async (response: Response) => { + const fallbackErrorMessage = t('errors.unknown_server_error'); + + try { + const data = await response.json(); + toast.error([data.message, data.details].join('\n') || fallbackErrorMessage); + } catch { + toast.error(fallbackErrorMessage); + } + }, + [t] + ); const api = useMemo( () => From 537b5cd577f08901f8e4a0a02a1d462d15324184 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Fri, 28 Oct 2022 18:55:36 +0800 Subject: [PATCH 09/15] style(ui): update link button styling (#2269) --- .../ui/src/components/TextLink/index.module.scss | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/TextLink/index.module.scss b/packages/ui/src/components/TextLink/index.module.scss index d8e3746c2..7dfa46fba 100644 --- a/packages/ui/src/components/TextLink/index.module.scss +++ b/packages/ui/src/components/TextLink/index.module.scss @@ -9,6 +9,12 @@ color: var(--color-brand-default); text-decoration: none; font: var(--font-label-2); + border-radius: _.unit(1); + padding: _.unit(1) _.unit(0.5); + + &:active { + background: var(--color-overlay-brand-pressed); + } } &.secondary { @@ -21,10 +27,15 @@ :global(body.desktop) { .link { &.primary:hover { - text-decoration: underline; + background: var(--color-overlay-brand-hover); } - &.secondary:hover { + &.primary:focus-visible { + outline: _.border(var(--color-overlay-brand-focused)); + } + + &.secondary:hover, + &.secondary:active { color: var(--color-brand-default); } } From 0f068d372cdb9de2f43e2c343d76aa2a1e35c552 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Sat, 29 Oct 2022 00:06:23 +0800 Subject: [PATCH 10/15] chore: upgrade lint runners (#2274) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 44d0150b9..dffec17c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,7 @@ jobs: main-lint: # avoid out of memory issue since macOS has bigger memory # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources - runs-on: macos-latest + runs-on: ubuntu-latest-4-cores steps: - uses: actions/checkout@v3 From ab9936c74eb62e4cc62ebf9e33d40220650e5131 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Mon, 31 Oct 2022 10:38:48 +0800 Subject: [PATCH 11/15] style(ui): update notification style (#2265) --- .../components/Notification/index.module.scss | 36 ++++++++++--------- .../ui/src/components/Notification/index.tsx | 25 +++---------- packages/ui/src/scss/_colors.scss | 2 ++ 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/packages/ui/src/components/Notification/index.module.scss b/packages/ui/src/components/Notification/index.module.scss index b5322444c..d3a430ddd 100644 --- a/packages/ui/src/components/Notification/index.module.scss +++ b/packages/ui/src/components/Notification/index.module.scss @@ -4,20 +4,30 @@ padding: _.unit(3) _.unit(4); font: var(--font-body-2); color: var(--color-type-primary); - background: var(--color-alert-99); margin: 0 auto _.unit(2); @include _.flex_row; - &:focus-visible { - outline: none; + .icon { + width: 20px; + height: 20px; + margin-right: _.unit(3); } -} -.icon { - color: var(--color-alert-70); - width: 20px; - height: 20px; - margin-right: _.unit(3); + &.alert { + background: var(--color-alert-99); + + .icon { + color: var(--color-alert-70); + } + } + + &.info { + background: var(--color-neutral-variant-80); + + .icon { + color: var(--color-neutral-variant-60); + } + } } .message { @@ -26,8 +36,6 @@ } .link { - text-decoration: underline; - cursor: pointer; max-width: 20%; } @@ -35,10 +43,4 @@ .notification { border-radius: var(--radius); } - - .link { - &:hover { - color: var(--color-brand-default); - } - } } diff --git a/packages/ui/src/components/Notification/index.tsx b/packages/ui/src/components/Notification/index.tsx index 38510e3c6..95d3032fe 100644 --- a/packages/ui/src/components/Notification/index.tsx +++ b/packages/ui/src/components/Notification/index.tsx @@ -1,38 +1,23 @@ import classNames from 'classnames'; -import { useTranslation } from 'react-i18next'; import InfoIcon from '@/assets/icons/info-icon.svg'; -import { onKeyDownHandler } from '@/utils/a11y'; +import TextLink from '../TextLink'; import * as styles from './index.module.scss'; type Props = { className?: string; message: string; onClose: () => void; + type?: 'info' | 'alert'; }; -const Notification = ({ className, message, onClose }: Props) => { - const { t } = useTranslation(); - +const Notification = ({ className, message, onClose, type = 'info' }: Props) => { return ( -
+
{message}
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} - - {t('action.got_it')} - +
); }; diff --git a/packages/ui/src/scss/_colors.scss b/packages/ui/src/scss/_colors.scss index 3666003dd..a0064313e 100644 --- a/packages/ui/src/scss/_colors.scss +++ b/packages/ui/src/scss/_colors.scss @@ -20,6 +20,7 @@ --color-neutral-95: #eff1f1; --color-neutral-100: #fff; + --color-neutral-variant-60: #928f9a; --color-neutral-variant-80: #e5e1ec; --color-danger-30: #930006; @@ -104,6 +105,7 @@ --color-neutral-99: #191c1d; --color-neutral-100: #000; + --color-neutral-variant-60: #928f9a; --color-neutral-variant-80: #5f5d67; --color-neutral-variant-90: #47464e; From 229a7862552d44634543117be67872a01b4e165f Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 31 Oct 2022 18:18:13 +0800 Subject: [PATCH 12/15] feat(core): hasura unauthed access support (#2278) --- .vscode/settings.json | 3 +- packages/core/src/middleware/koa-auth.ts | 8 +- packages/core/src/routes/authn.test.ts | 106 +++++++++++++++++++++++ packages/core/src/routes/authn.ts | 34 ++++++-- 4 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/routes/authn.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index c85102924..10453f7bc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "silverhand", "slonik", "stylelint", - "topbar" + "topbar", + "hasura" ] } diff --git a/packages/core/src/middleware/koa-auth.ts b/packages/core/src/middleware/koa-auth.ts index eda71e3bb..07e3b1778 100644 --- a/packages/core/src/middleware/koa-auth.ts +++ b/packages/core/src/middleware/koa-auth.ts @@ -2,6 +2,7 @@ import type { IncomingHttpHeaders } from 'http'; import { UserRole } from '@logto/schemas'; import { managementResource } from '@logto/schemas/lib/seeds'; +import type { Optional } from '@silverhand/essentials'; import { conditional } from '@silverhand/essentials'; import { jwtVerify } from 'jose'; import type { MiddlewareType, Request } from 'koa'; @@ -49,7 +50,7 @@ type TokenInfo = { // eslint-disable-next-line complexity export const verifyBearerTokenFromRequest = async ( request: Request, - resourceIndicator = managementResource.indicator + resourceIndicator: Optional ): Promise => { const { isProduction, isIntegrationTest, developmentUserId } = envSet.values; const userId = request.headers['development-user-id']?.toString() ?? developmentUserId; @@ -83,7 +84,10 @@ export default function koaAuth, ResponseBodyT> { return async (ctx, next) => { - const { sub, clientId, roleNames } = await verifyBearerTokenFromRequest(ctx.request); + const { sub, clientId, roleNames } = await verifyBearerTokenFromRequest( + ctx.request, + managementResource.indicator + ); if (forRole) { assertThat( diff --git a/packages/core/src/routes/authn.test.ts b/packages/core/src/routes/authn.test.ts new file mode 100644 index 000000000..4a856693c --- /dev/null +++ b/packages/core/src/routes/authn.test.ts @@ -0,0 +1,106 @@ +import RequestError from '@/errors/RequestError'; +import * as functions from '@/middleware/koa-auth'; +import { createRequester } from '@/utils/test-utils'; + +import authnRoutes from './authn'; + +describe('authn route for Hasura', () => { + const request = createRequester({ anonymousRoutes: authnRoutes }); + const mockUserId = 'foo'; + const mockExpectedRole = 'some_role'; + const mockUnauthorizedRole = 'V'; + const keys = Object.freeze({ + expectedRole: 'Expected-Role', + hasuraUserId: 'X-Hasura-User-Id', + hasuraRole: 'X-Hasura-Role', + }); + + describe('with successful verification', () => { + beforeEach(() => { + jest.spyOn(functions, 'verifyBearerTokenFromRequest').mockResolvedValue({ + clientId: 'ok', + sub: mockUserId, + roleNames: [mockExpectedRole], + }); + }); + + it('has expected role', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockExpectedRole, + }); + }); + + it('throws 401 if no expected role present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole + '1'); + expect(response.status).toEqual(401); + }); + + it('falls back to unauthorized role if no expected role present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }) + .set(keys.expectedRole, mockExpectedRole + '1'); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + }); + + describe('with failed verification', () => { + beforeEach(() => { + jest + .spyOn(functions, 'verifyBearerTokenFromRequest') + .mockImplementation(async (_, resource) => { + if (resource) { + throw new RequestError({ code: 'auth.jwt_sub_missing', status: 401 }); + } + + return { clientId: 'not ok', sub: mockUserId }; + }); + }); + + it('throws 401 if no unauthorized role presents', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(401); + }); + + it('falls back to unauthorized role with user id if no expected resource present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + + it('falls back to unauthorized role if JWT is invalid', async () => { + jest + .spyOn(functions, 'verifyBearerTokenFromRequest') + .mockRejectedValue(new RequestError({ code: 'auth.jwt_sub_missing', status: 401 })); + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + }); +}); diff --git a/packages/core/src/routes/authn.ts b/packages/core/src/routes/authn.ts index d6a99b439..aa523a84a 100644 --- a/packages/core/src/routes/authn.ts +++ b/packages/core/src/routes/authn.ts @@ -16,15 +16,39 @@ export default function authnRoutes(router: T) { router.get( '/authn/hasura', koaGuard({ - query: z.object({ resource: z.string().min(1) }), + query: z.object({ resource: z.string().min(1), unauthorizedRole: z.string().optional() }), status: [200, 401], }), async (ctx, next) => { + const { resource, unauthorizedRole } = ctx.guard.query; const expectedRole = ctx.headers['expected-role']?.toString(); - const { sub, roleNames } = await verifyBearerTokenFromRequest( - ctx.request, - ctx.guard.query.resource - ); + + const verifyToken = async (expectedResource?: string) => { + try { + return await verifyBearerTokenFromRequest(ctx.request, expectedResource); + } catch { + return { + sub: undefined, + roleNames: undefined, + }; + } + }; + + const { sub, roleNames } = await verifyToken(resource); + + if (unauthorizedRole && (!expectedRole || !roleNames?.includes(expectedRole))) { + ctx.body = { + 'X-Hasura-User-Id': + sub ?? + // When the previous token verification throws, the reason could be resource mismatch. + // So we verify the token again with no resource provided. + (await verifyToken().then(({ sub }) => sub)), + 'X-Hasura-Role': unauthorizedRole, + }; + ctx.status = 200; + + return next(); + } if (expectedRole) { assertThat( From f657ed3598ce0825ede9349c7d90a3ae55e426a2 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 31 Oct 2022 19:47:00 +0800 Subject: [PATCH 13/15] chore: fix gitpod config --- .gitpod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index b2887d25f..06891a0ed 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -19,7 +19,7 @@ tasks: pnpm start:dev env: TRUST_PROXY_HEADER: 1 - DB_URL: postgres://postgres:p0stgr3s@127.0.0.1:5432 + DB_URL: postgres://postgres:p0stgr3s@127.0.0.1:5432/logto ports: - name: Logto From 02040c16294fe17a28dbc0e199caa9a1df71907c Mon Sep 17 00:00:00 2001 From: Randy Date: Wed, 2 Nov 2022 18:24:08 +0800 Subject: [PATCH 14/15] chore: ignore fly.toml (#2297) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0a6e52864..4ebbdef2d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ cache .idea/ *.pem .history +fly.toml # connectors /packages/core/connectors From 0dcfd17a4d0e6a55155611264fc23bbced6e30ad Mon Sep 17 00:00:00 2001 From: wangsijie Date: Thu, 3 Nov 2022 10:34:12 +0800 Subject: [PATCH 15/15] chore: config codespaces container (#2267) --- .devcontainer/devcontainer.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..59d648c36 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + "ghcr.io/devcontainers/features/node:1": {} + }, + "updateContentCommand": "npm i -g pnpm && pnpm i && pnpm prepack && pnpm cli connector add --official -p .", + "postStartCommand": "docker run -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=p0stgr3s postgres:14-alpine", + "postAttachCommand": "pnpm cli db seed && [[ ! -z $CODESPACES ]] && export ENDPOINT=https://$CODESPACE_NAME-3001.preview.app.github.dev", + "containerEnv": { + "DB_URL": "postgres://postgres:p0stgr3s@localhost:5432/logto", + "TRUST_PROXY_HEADER": "1" + } +}