From db68a177bafae8f26d00b9eeb63c892d06abd986 Mon Sep 17 00:00:00 2001 From: spastorelli Date: Mon, 2 Dec 2024 15:03:58 +0100 Subject: [PATCH] Add support to VA Auth0 integration to include claims from backend in VA token (#629) * Add support to VA Auth0 integration to include user claims from backend in VA token * review: rename include_claims_in_va_token & change description of config toggle * review: remove sanitizeJWTTokenClaims * Move privateKey check out of try/catch block * review: Be more defensive when parsing error response * Add changeset * Revert "Add changeset" This reverts commit 712dbdcf937b0e3d95ab78f8a71c05523c67cecb. * Add changeset --- .changeset/gorgeous-beds-compare.md | 5 + bun.lockb | Bin 381832 -> 382192 bytes integrations/va-auth0/package.json | 2 +- integrations/va-auth0/src/index.tsx | 353 +++++++++++++++++----------- 4 files changed, 222 insertions(+), 138 deletions(-) create mode 100644 .changeset/gorgeous-beds-compare.md diff --git a/.changeset/gorgeous-beds-compare.md b/.changeset/gorgeous-beds-compare.md new file mode 100644 index 000000000..a40059f15 --- /dev/null +++ b/.changeset/gorgeous-beds-compare.md @@ -0,0 +1,5 @@ +--- +'@gitbook/integration-va-auth0': minor +--- + +Add support to VA Auth0 integration to include claims from backend in token diff --git a/bun.lockb b/bun.lockb index cdd6e8eaabcfc481a7374337c539698c194d17da..298aa4422012c0cb658a29e1232c030545353050 100755 GIT binary patch delta 28432 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 delta 27772 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 Ldg+|GiSmB{Oal=L diff --git a/integrations/va-auth0/package.json b/integrations/va-auth0/package.json index 6d18d3b38..14905242d 100644 --- a/integrations/va-auth0/package.json +++ b/integrations/va-auth0/package.json @@ -6,7 +6,7 @@ "@gitbook/api": "*", "@gitbook/runtime": "*", "itty-router": "^4.0.14", - "@tsndr/cloudflare-worker-jwt": "2.3.2" + "@tsndr/cloudflare-worker-jwt": "3.1.3" }, "devDependencies": { "@gitbook/cli": "workspace:*", diff --git a/integrations/va-auth0/src/index.tsx b/integrations/va-auth0/src/index.tsx index c42c53f94..2cc138adb 100644 --- a/integrations/va-auth0/src/index.tsx +++ b/integrations/va-auth0/src/index.tsx @@ -1,4 +1,4 @@ -import { sign } from '@tsndr/cloudflare-worker-jwt'; +import * as jwt from '@tsndr/cloudflare-worker-jwt'; import { Router } from 'itty-router'; import { IntegrationInstallationConfiguration } from '@gitbook/api'; @@ -22,6 +22,7 @@ type Auth0SiteInstallationConfiguration = { client_id?: string; issuer_base_url?: string; client_secret?: string; + enrich_session?: boolean; }; type Auth0State = Auth0SiteInstallationConfiguration; @@ -35,6 +36,18 @@ type Auth0Props = { }; }; +type Auth0TokenResponseData = { + access_token?: string; + refresh_token?: string; + token_type: 'Bearer'; + expires_in: number; +}; + +type Auth0TokenResponseError = { + error: string; + error_description: string; +}; + export type Auth0Action = { action: 'save.config' }; const getDomainWithHttps = (url: string): string => { @@ -55,6 +68,7 @@ const configBlock = createComponent { @@ -68,6 +82,7 @@ const configBlock = createComponent - - The unique identifier of your Auth0 application. - - {' '} - More Details - - - } - element={} - /> - - - The Auth0 domain (also known as tenant). - - {' '} - More Details - - - } - element={} - /> - - - The secret used for signing and validating tokens. - - {' '} - More Details - - - } - element={} - /> - - - - The following URL needs to be saved as an allowed callback URL in Auth0: - - - - + + + + + The Auth0 domain (also known as tenant). + + {' '} + More Details + + + } + element={} /> - } - /> - + + + The unique identifier of your Auth0 application. + + {' '} + More Details + + + } + element={} + /> + + + The secret used for signing and validating tokens. + + {' '} + More Details + + + } + element={ + + } + /> + + + Add this URL to the{' '} + Allowed Callback URLs section in your + application's settings in Auth0. + + } + element={} + /> + + + + + } + /> + + + } + /> + + ); }, }); @@ -213,80 +249,120 @@ const handleFetchEvent: FetchEventCallback = async (request router.get('/visitor-auth/response', async (request) => { if ('site' in siteInstallation && siteInstallation.site) { const publishedContentUrls = await getPublishedContentUrls(context); - const privateKey = context.environment.signingSecrets.siteInstallation!; - let token; - try { - token = await sign( - { exp: Math.floor(Date.now() / 1000) + 1 * (60 * 60) }, - privateKey, - ); - } catch (e) { - return new Response('Error: Could not sign JWT token', { - status: 500, - }); - } + const includeClaimsInToken = siteInstallation?.configuration.enrich_session; const issuerBaseUrl = siteInstallation?.configuration.issuer_base_url; const clientId = siteInstallation?.configuration.client_id; const clientSecret = siteInstallation?.configuration.client_secret; - if (clientId && clientSecret) { - const searchParams = new URLSearchParams({ - grant_type: 'authorization_code', - client_id: clientId, - client_secret: clientSecret, - code: `${request.query.code}`, - redirect_uri: `${installationURL}/visitor-auth/response`, - }); - const accessTokenURL = `${issuerBaseUrl}/oauth/token/`; - const resp: any = await fetch(accessTokenURL, { - method: 'POST', - headers: { 'content-type': 'application/x-www-form-urlencoded' }, - body: searchParams, - }) - .then((response) => response.json()) - .catch((err) => { - return new Response('Error: Could not fetch access token from Auth0', { - status: 401, - }); - }); - if ('access_token' in resp) { - let url; - if (request.query.state) { - url = new URL( - `${publishedContentUrls?.published}${request.query.state}`, - ); - url.searchParams.append('jwt_token', token); - } else { - url = new URL(publishedContentUrls?.published!); - url.searchParams.append('jwt_token', token); - } - if (publishedContentUrls?.published && token) { - return Response.redirect(url.toString()); - } else { - return new Response( - "Error: Either JWT token or space's published URL is missing", - { - status: 500, - }, - ); - } - } else { - logger.debug(JSON.stringify(resp, null, 2)); + if (!clientId || !clientSecret || !issuerBaseUrl) { + return new Response( + 'Error: Either client id, client secret or issuer base url is missing', + { + status: 400, + }, + ); + } + + const searchParams = new URLSearchParams({ + grant_type: 'authorization_code', + client_id: clientId, + client_secret: clientSecret, + code: `${request.query.code}`, + redirect_uri: `${installationURL}/visitor-auth/response`, + }); + const accessTokenURL = `${issuerBaseUrl}/oauth/token/`; + const auth0TokenResp = await fetch(accessTokenURL, { + method: 'POST', + headers: { 'content-type': 'application/x-www-form-urlencoded' }, + body: searchParams, + }); + + if (!auth0TokenResp.ok) { + if (auth0TokenResp.headers.get('content-type')?.includes('application/json')) { + const errorResponse = await auth0TokenResp.json(); + logger.debug(JSON.stringify(errorResponse, null, 2)); logger.debug( - `Did not receive access token. Error: ${(resp && resp.error) || ''} ${ - (resp && resp.error_description) || '' - }`, + `Did not receive access token. Error: ${ + (errorResponse && errorResponse.error) || '' + } ${(errorResponse && errorResponse.error_description) || ''}`, ); + } + return new Response('Error: Could not fetch token from Auth0', { + status: 401, + }); + } + + let userInfo; + if (includeClaimsInToken) { + // Auth0 returns an opaque access token (i.e., one that doesn't include user or custom claims) when + // exchanging an authorization code for a token—unless an audience (aud parameter) is set to a valid + // Auth0 API application during the authorization request. + // In this case, since we only request a token to verify authentication and generate the VA, + // there is no valid API target to specify as audience. + // As a result, we must call the /userinfo endpoint (an OIDC-compliant endpoint) to retrieve user claims. + // + // An alternative approach would be to request an ID Token using the Implicit Grant flow (with form post). + // However, many authentication providers discourage this method in favor of the Authorization Code flow. + // Additionally, some customers may disable the Implicit Grant flow in their Auth0 application. + // Therefore, retrieving user data via the /userinfo endpoint is a more robust solution. + const auth0TokenData = await auth0TokenResp.json(); + if (!auth0TokenData.access_token) { return new Response('Error: No Access Token found in response from Auth0', { status: 401, }); } - } else { - return new Response('Error: Either ClientId or Client Secret is missing', { + + const userInfoURL = `${issuerBaseUrl}/userinfo/`; + const userInfoResp = await fetch(userInfoURL, { + method: 'GET', + headers: { Authorization: `Bearer ${auth0TokenData.access_token}` }, + }); + + if (!userInfoResp.ok) { + return new Response('Error: Unable to fetch user info from Auth0', { + status: 401, + }); + } + + userInfo = await userInfoResp.json>(); + } + + const privateKey = context.environment.signingSecrets.siteInstallation; + if (!privateKey) { + return new Response('Error: Missing private key from site installation', { status: 400, }); } + + try { + const jwtToken = await jwt.sign( + { + ...(userInfo ?? {}), + exp: Math.floor(Date.now() / 1000) + 1 * (60 * 60), + }, + privateKey, + ); + + const publishedContentUrl = publishedContentUrls?.published; + if (!publishedContentUrl || !jwtToken) { + return new Response( + "Error: Either JWT token or site's published URL is missing", + { + status: 500, + }, + ); + } + + const url = new URL(`${publishedContentUrl}${request.query.state || ''}`); + url.searchParams.append('jwt_token', jwtToken); + + return Response.redirect(url.toString()); + } catch (e) { + return new Response('Error: Could not sign JWT token', { + status: 500, + }); + } } }); @@ -330,6 +406,9 @@ export default createIntegration({ const url = new URL(`${issuerBaseUrl}/authorize`); url.searchParams.append('client_id', clientId); url.searchParams.append('response_type', 'code'); + if (configuration.enrich_session) { + url.searchParams.append('scope', 'openid'); + } url.searchParams.append('redirect_uri', `${installationURL}/visitor-auth/response`); url.searchParams.append('state', location);