From a449a0a21f9abd5f46b1f62ff7158bea6fa65cd4 Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Fri, 15 Sep 2023 18:31:06 +0800 Subject: [PATCH] refactor(console): add metadata and support endpoint and audience variables in api guides (#4515) refactor(console): support endpoint and audience variables in api guides --- .../assets/docs/guides/api-express/README.mdx | 39 ++++++++++++------- .../assets/docs/guides/api-express/index.ts | 3 +- .../console/src/components/Guide/index.tsx | 2 + .../components/ApiGuide/index.tsx | 25 ++++++++---- .../components/GuideDrawer/index.tsx | 5 ++- .../components/GuideModal/index.tsx | 11 +++++- .../src/pages/ApiResourceDetails/index.tsx | 3 +- 7 files changed, 60 insertions(+), 28 deletions(-) diff --git a/packages/console/src/assets/docs/guides/api-express/README.mdx b/packages/console/src/assets/docs/guides/api-express/README.mdx index d66708bbb..c5c4a98bb 100644 --- a/packages/console/src/assets/docs/guides/api-express/README.mdx +++ b/packages/console/src/assets/docs/guides/api-express/README.mdx @@ -3,6 +3,7 @@ import TabItem from '@mdx/components/TabItem'; import InlineNotification from '@/ds-components/InlineNotification'; import Steps from '@/mdx-components/Steps'; import Step from '@/mdx-components/Step'; +import { appendPath } from '@silverhand/essentials'; @@ -64,16 +65,22 @@ pnpm add jose ### Retrieve Logto's OIDC configurations -You will need a JWK public key set and the token issuer to verify the signature and source of the received JWS token. All the latest public Logto Authorization Configurations can be found at `https:///oidc/.well-known/openid-configuration`. +

+You will need a JWK public key set and the token issuer to verify the signature and source of the received JWS token. All the latest public Logto Authorization Configurations can be found at {`${appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}`}. +

-e.g. Call `https://logto.dev/oidc/.well-known/openid-configuration`. And locate the following two fields in the response body: +

+e.g. Call {`${appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}`}. And locate the following two fields in the response body: +

-```ts -{ - "jwks_uri": "https://logto.dev/oidc/jwks", - "issuer": "https://logto.dev/oidc" -} -``` +
+  
+{`{
+  "jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}",
+  "issuer": "${appendPath(props.endpoint, '/oidc')}"
+}`}
+  
+
### Add auth middleware @@ -83,8 +90,9 @@ Jose's `jwtVerify` method may helps you to verify the token's JWS format, token For 🔐 RBAC, scope validation is also required. -```ts -// auth-middleware.ts +
+  
+{`// auth-middleware.ts
 
 import { createRemoteJWKSet, jwtVerify } from 'jose';
 
@@ -98,12 +106,12 @@ export const verifyAuthFromRequest = async (req, res, next) => {
     // The raw Bearer Token extracted from the request header
     token,
     // Generate a jwks using jwks_uri inquired from Logto server
-    createRemoteJWKSet('https:///oidc/jwks'),
+    createRemoteJWKSet('${appendPath(props.endpoint, '/oidc/jwks')}'),
     {
       // Expected issuer of the token, should be issued by the Logto server
-      issuer: 'https:///oidc',
+      issuer: '${appendPath(props.endpoint, '/oidc')}',
       // Expected audience token, should be the resource indicator of the current API
-      audience: '',
+      audience: '${props.audience}',
     }
   );
 
@@ -114,8 +122,9 @@ export const verifyAuthFromRequest = async (req, res, next) => {
   userId = payload.sub;
 
   return next();
-};
-```
+};`}
+  
+
diff --git a/packages/console/src/assets/docs/guides/api-express/index.ts b/packages/console/src/assets/docs/guides/api-express/index.ts index e5f14a72b..3a6f72d70 100644 --- a/packages/console/src/assets/docs/guides/api-express/index.ts +++ b/packages/console/src/assets/docs/guides/api-express/index.ts @@ -2,7 +2,8 @@ import { type GuideMetadata } from '../types'; const metadata: Readonly = Object.freeze({ name: 'Express', - description: 'Protect your API on node (Express)', + description: + 'Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.', target: 'API', }); diff --git a/packages/console/src/components/Guide/index.tsx b/packages/console/src/components/Guide/index.tsx index e322a66cd..1c84f002c 100644 --- a/packages/console/src/components/Guide/index.tsx +++ b/packages/console/src/components/Guide/index.tsx @@ -28,6 +28,7 @@ export type GuideContextType = { origin: string; callback: string; }; + audience?: string; }; type Props = { @@ -50,6 +51,7 @@ export const GuideContext = createContext({ postLogoutRedirectUris: [], isCompact: false, sampleUrls: { origin: '', callback: '' }, + audience: '', }); function Guide({ className, guideId, isEmpty, isLoading, onClose }: Props) { diff --git a/packages/console/src/pages/ApiResourceDetails/components/ApiGuide/index.tsx b/packages/console/src/pages/ApiResourceDetails/components/ApiGuide/index.tsx index 9ad9fa25a..3cabfaa3d 100644 --- a/packages/console/src/pages/ApiResourceDetails/components/ApiGuide/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/components/ApiGuide/index.tsx @@ -1,29 +1,38 @@ +import { type Resource } from '@logto/schemas'; import { conditional } from '@silverhand/essentials'; -import { useMemo } from 'react'; +import { useContext, useMemo } from 'react'; import guides from '@/assets/docs/guides'; import Guide, { GuideContext, type GuideContextType } from '@/components/Guide'; +import { AppDataContext } from '@/contexts/AppDataProvider'; +import useCustomDomain from '@/hooks/use-custom-domain'; type Props = { className?: string; guideId: string; + apiResource?: Resource; isCompact?: boolean; onClose: () => void; }; -function ApiGuide({ className, guideId, isCompact, onClose }: Props) { +function ApiGuide({ className, guideId, apiResource, isCompact, onClose }: Props) { + const { tenantEndpoint } = useContext(AppDataContext); + const { applyDomain: applyCustomDomain } = useCustomDomain(); const guide = guides.find(({ id }) => id === guideId); const memorizedContext = useMemo( () => conditional( - !!guide && { - metadata: guide.metadata, - Logo: guide.Logo, - isCompact: Boolean(isCompact), - } + !!guide && + !!apiResource && { + metadata: guide.metadata, + Logo: guide.Logo, + isCompact: Boolean(isCompact), + endpoint: applyCustomDomain(tenantEndpoint?.href ?? ''), + audience: apiResource.indicator, + } ) satisfies GuideContextType | undefined, - [guide, isCompact] + [apiResource, applyCustomDomain, guide, isCompact, tenantEndpoint?.href] ); return memorizedContext ? ( diff --git a/packages/console/src/pages/ApiResourceDetails/components/GuideDrawer/index.tsx b/packages/console/src/pages/ApiResourceDetails/components/GuideDrawer/index.tsx index 8a478dd81..0925d97e1 100644 --- a/packages/console/src/pages/ApiResourceDetails/components/GuideDrawer/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/components/GuideDrawer/index.tsx @@ -1,3 +1,4 @@ +import { type Resource } from '@logto/schemas'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -14,10 +15,11 @@ import ApiGuide from '../ApiGuide'; import * as styles from './index.module.scss'; type Props = { + apiResource: Resource; onClose: () => void; }; -function GuideDrawer({ onClose }: Props) { +function GuideDrawer({ apiResource, onClose }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.guide' }); const guides = useApiGuideMetadata(); const [selectedGuide, setSelectedGuide] = useState(); @@ -59,6 +61,7 @@ function GuideDrawer({ onClose }: Props) { isCompact className={styles.guide} guideId={selectedGuide.id} + apiResource={apiResource} onClose={() => { setSelectedGuide(undefined); }} diff --git a/packages/console/src/pages/ApiResourceDetails/components/GuideModal/index.tsx b/packages/console/src/pages/ApiResourceDetails/components/GuideModal/index.tsx index 706c50009..e89e1f047 100644 --- a/packages/console/src/pages/ApiResourceDetails/components/GuideModal/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/components/GuideModal/index.tsx @@ -1,3 +1,4 @@ +import { type Resource } from '@logto/schemas'; import Modal from 'react-modal'; import ModalHeader from '@/components/Guide/ModalHeader'; @@ -9,10 +10,11 @@ import * as styles from './index.module.scss'; type Props = { guideId: string; + apiResource?: Resource; onClose: () => void; }; -function GuideModal({ guideId, onClose }: Props) { +function GuideModal({ guideId, apiResource, onClose }: Props) { return (
@@ -25,7 +27,12 @@ function GuideModal({ guideId, onClose }: Props) { requestSuccessMessage="guide.request_guide_successfully" onClose={onClose} /> - +
); diff --git a/packages/console/src/pages/ApiResourceDetails/index.tsx b/packages/console/src/pages/ApiResourceDetails/index.tsx index b710fcb8b..941b9a796 100644 --- a/packages/console/src/pages/ApiResourceDetails/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/index.tsx @@ -83,6 +83,7 @@ function ApiResourceDetails() { return ( { navigate(`/api-resources/${id}`); }} @@ -129,7 +130,7 @@ function ApiResourceDetails() { }} /> - + , size: 'large' }}