diff --git a/packages/console/src/assets/docs/guides/api-express/README.mdx b/packages/console/src/assets/docs/guides/api-express/README.mdx new file mode 100644 index 000000000..d66708bbb --- /dev/null +++ b/packages/console/src/assets/docs/guides/api-express/README.mdx @@ -0,0 +1,134 @@ +import Tabs from '@mdx/components/Tabs'; +import TabItem from '@mdx/components/TabItem'; +import InlineNotification from '@/ds-components/InlineNotification'; +import Steps from '@/mdx-components/Steps'; +import Step from '@/mdx-components/Step'; + + + + + +A authorized request should contain an Authorization header with Bearer `` as its content. Extract the Authorization Token from the request header: + +```ts +// auth_middleware.ts + +import { IncomingHttpHeaders } from 'http'; + +const extractBearerTokenFromHeaders = ({ authorization }: IncomingHttpHeaders) => { + if (!authorization) { + throw new Error({ code: 'auth.authorization_header_missing', status: 401 }); + } + + if (!authorization.startsWith('Bearer')) { + throw new Error({ code: 'auth.authorization_token_type_not_supported', status: 401 }); + } + + return authorization.slice(bearerTokenIdentifier.length + 1); +}; +``` + + + + + + + For demonstration, we use jose package to validate the token's signature, expiration status, and required claims. + + +### Install `jose` as dependency + + + + +```bash +npm install jose +``` + + + + +```bash +yarn add jose +``` + + + + +```bash +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`. + +e.g. Call `https://logto.dev/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" +} +``` + +### Add auth middleware + +Jose's `jwtVerify` method may helps you to verify the token's JWS format, token signature, issuer, audience and the expiration status. A exception will be thrown if validation failed. + + + For 🔐 RBAC, scope validation is also required. + + +```ts +// auth-middleware.ts + +import { createRemoteJWKSet, jwtVerify } from 'jose'; + +//... + +export const verifyAuthFromRequest = async (req, res, next) => { + // Extract the token + const token = extractBearerTokenFromHeaders(req.headers); + + const { payload } = await jwtVerify( + // The raw Bearer Token extracted from the request header + token, + // Generate a jwks using jwks_uri inquired from Logto server + createRemoteJWKSet('https:///oidc/jwks'), + { + // Expected issuer of the token, should be issued by the Logto server + issuer: 'https:///oidc', + // Expected audience token, should be the resource indicator of the current API + audience: '', + } + ); + + // If you are using RBAC + assert(payload.scope.includes('some_scope')); + + // Custom payload logic + userId = payload.sub; + + return next(); +}; +``` + + + + + +```ts +import { verifyAuthFromRequest } from '/middleware/auth-middleware.ts'; + +app.get('/user/:id', verifyAuthFromRequest, (req, res, next) => { + // Custom code +}); +``` + + + + diff --git a/packages/console/src/assets/docs/guides/api-express/index.ts b/packages/console/src/assets/docs/guides/api-express/index.ts new file mode 100644 index 000000000..e5f14a72b --- /dev/null +++ b/packages/console/src/assets/docs/guides/api-express/index.ts @@ -0,0 +1,9 @@ +import { type GuideMetadata } from '../types'; + +const metadata: Readonly = Object.freeze({ + name: 'Express', + description: 'Protect your API on node (Express)', + target: 'API', +}); + +export default metadata; diff --git a/packages/console/src/assets/docs/guides/api-express/logo.svg b/packages/console/src/assets/docs/guides/api-express/logo.svg new file mode 100644 index 000000000..bda5f80da --- /dev/null +++ b/packages/console/src/assets/docs/guides/api-express/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/console/src/assets/docs/guides/index.ts b/packages/console/src/assets/docs/guides/index.ts index ef9322446..15aaa9ec1 100644 --- a/packages/console/src/assets/docs/guides/index.ts +++ b/packages/console/src/assets/docs/guides/index.ts @@ -2,6 +2,7 @@ import { lazy } from 'react'; +import apiExpress from './api-express/index'; import m2mGeneral from './m2m-general/index'; import nativeAndroidJava from './native-android-java/index'; import nativeAndroidKt from './native-android-kt/index'; @@ -157,6 +158,13 @@ const guides: Readonly = Object.freeze([ Component: lazy(async () => import('./web-outline/README.mdx')), metadata: webOutline, }, + { + order: Number.POSITIVE_INFINITY, + id: 'api-express', + Logo: lazy(async () => import('./api-express/logo.svg')), + Component: lazy(async () => import('./api-express/README.mdx')), + metadata: apiExpress, + }, ]); export default guides;