From 380956935553c982ece5a7d652cb1802b8dff622 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Thu, 13 Nov 2025 15:58:07 -0500 Subject: [PATCH 1/7] feat: [AIDX-240] Connected Account Flow, quickstart updates --- .../langchain-fastapi-py/call-others-api.mdx | 3 +- .../langchain-next-js/call-others-api.mdx | 7 ++- .../prerequisites/account-app-steps.jsx | 6 ++ .../prerequisites/call-others-api.jsx | 14 ++++- .../vercel-ai-next-js/call-others-api.mdx | 15 +++-- .../call-others-api.mdx | 57 +++++++++++-------- 6 files changed, 67 insertions(+), 35 deletions(-) diff --git a/auth4genai/snippets/get-started/langchain-fastapi-py/call-others-api.mdx b/auth4genai/snippets/get-started/langchain-fastapi-py/call-others-api.mdx index 10e411c6d5..c82fa9c1e1 100644 --- a/auth4genai/snippets/get-started/langchain-fastapi-py/call-others-api.mdx +++ b/auth4genai/snippets/get-started/langchain-fastapi-py/call-others-api.mdx @@ -148,6 +148,7 @@ AUTH0_SECRET='random 32 byte value' AUTH0_DOMAIN='' AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' +AUTH0_SCOPE='openid profile email offline_access' # OpenAI API Key or any provider supported by the Vercel AI SDK OPENAI_API_KEY="YOUR_API_KEY" @@ -194,7 +195,7 @@ auth0_ai = Auth0AI( with_calendar_access = auth0_ai.with_token_vault( connection="google-oauth2", - scopes=["https://www.googleapis.com/auth/calendar.events"], + scopes=["openid", "https://www.googleapis.com/auth/calendar.events"], ) ``` diff --git a/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx b/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx index 30d1aa5df8..ff0c37bbe8 100644 --- a/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx +++ b/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx @@ -34,7 +34,7 @@ AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' AUTH0_AUDIENCE="https://your.domain.us.langgraph.app" -AUTH0_SCOPE="openid profile email" +AUTH0_SCOPE="openid profile email offline_access" AUTH0_CUSTOM_API_CLIENT_ID="{yourCustomApiClientId}" AUTH0_CUSTOM_API_CLIENT_SECRET="{yourCustomApiClientSecret}" @@ -111,7 +111,7 @@ AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' AUTH0_AUDIENCE="https://your.domain.us.langgraph.app" -AUTH0_SCOPE="openid profile email" +AUTH0_SCOPE="openid profile email offline_access" AUTH0_CUSTOM_API_CLIENT_ID="{yourCustomApiClientId}" AUTH0_CUSTOM_API_CLIENT_SECRET="{yourCustomApiClientSecret}" @@ -167,7 +167,7 @@ const withAccessTokenForConnection = (connection: string, scopes: string[]) => // Connection for Google services export const withGmailSearch = withAccessTokenForConnection( 'google-oauth2', - ['https://www.googleapis.com/auth/gmail.readonly'], + ['openid', 'https://www.googleapis.com/auth/gmail.readonly'], ); ``` @@ -183,6 +183,7 @@ export const auth0 = new Auth0Client({ scope: process.env.AUTH0_SCOPE, audience: process.env.AUTH0_AUDIENCE, }, + enableConnectAccountEndpoint: true, }); export const getAccessToken = async () => { diff --git a/auth4genai/snippets/get-started/prerequisites/account-app-steps.jsx b/auth4genai/snippets/get-started/prerequisites/account-app-steps.jsx index 31a1534a3d..600949a0be 100644 --- a/auth4genai/snippets/get-started/prerequisites/account-app-steps.jsx +++ b/auth4genai/snippets/get-started/prerequisites/account-app-steps.jsx @@ -7,6 +7,7 @@ export const AccountAndAppSteps = ({ copyDomain, enableTokenVaultGrant = false, enableRefreshTokenGrant = false, + enableAllowRefreshTokenRotation = undefined, }) => { const steps = [ @@ -66,6 +67,11 @@ export const AccountAndAppSteps = ({ Set Allowed Web Origins as: {allowedWebOrigins} )} + {enableAllowRefreshTokenRotation !== undefined && ( +
  • + Scroll down to the Refresh Token Rotation section and {enableAllowRefreshTokenRotation === true ? "enable" : "disable"} the Allow Refresh Token Rotation option. +
  • + )} {enableTokenVaultGrant && !enableRefreshTokenGrant && (
  • Scroll down and expand the Advanced section. Switch to the Grant Types tab and enable the Token Vault grant type. diff --git a/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx b/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx index 2b1b36b85f..6e31306bf8 100644 --- a/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx +++ b/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx @@ -8,7 +8,7 @@ import { AccountAndAppSteps } from "/snippets/get-started/prerequisites/account- * @param {string} [props.createAuth0ApplicationStep.applicationType] - Type of Auth0 application (e.g., "Regular Web") * @param {string} [props.createAuth0ApplicationStep.callbackUrl] - Allowed callback URL for the application * @param {string} [props.createAuth0ApplicationStep.logoutUrl] - Allowed logout URL for the application - * @param {string|undefined} [props.createAuth0ApplicationStep.allowedWebOrigins] - Allowed web origins for the application + * @param {string|undefined} [props.createAuth0ApplicationStep.allowedWebOrigins] - Allowed web origins for the application * @param {boolean} [props.createAuth0ApplicationStep.enableTokenVaultGrant] - Enable Token Vault Grant for the application * @param {boolean} [props.createAuth0ApplicationStep.enableRefreshTokenGrant] - Enable Refresh Token Grant for the application * @@ -27,6 +27,7 @@ export const Prerequisites = ({ allowedWebOrigins: undefined, enableTokenVaultGrant: undefined, enableRefreshTokenGrant: undefined, + enableAllowRefreshTokenRotation: undefined, }, createAuth0ApiStep = undefined, createResourceServerClientStep = undefined, @@ -43,6 +44,7 @@ export const Prerequisites = ({ allowedWebOrigins: createAuth0ApplicationStep.allowedWebOrigins, enableTokenVaultGrant: createAuth0ApplicationStep.enableTokenVaultGrant, enableRefreshTokenGrant: createAuth0ApplicationStep.enableRefreshTokenGrant, + enableAllowRefreshTokenRotation: createAuth0ApplicationStep.enableAllowRefreshTokenRotation, }) ); @@ -133,6 +135,16 @@ export const Prerequisites = ({ ); + steps.push( + After your web application has been granted access to the My Account API, you will also need to leverage the Multi-Resource Refresh Token feature, which enables the refresh token delivered to your application to also obtain an access token to call the My Account API.

    + You can quickly define a refresh token policy for your application to use when requesting access tokens for the My Account API by doing the following: +
      +
    • Navigate to Applications > Applications and select your client application.
    • +
    • On the Settings tab, scroll down to the Multi-Resource Refresh Token section.
    • +
    • Select Edit Configuration and then enable the MRRT toggle for the Auth0 My Account API.
    • +
    +
    ) + steps.push( Set up a Google developer account that allows for third-party API calls by diff --git a/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx b/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx index bd7c704ecc..f903e92120 100644 --- a/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx +++ b/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx @@ -30,6 +30,7 @@ AUTH0_SECRET='random 32 byte value' AUTH0_DOMAIN='' AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' +AUTH0_SCOPE='openid profile email offline_access' # OpenAI API Key or any provider supported by the Vercel AI SDK OPENAI_API_KEY="YOUR_API_KEY" @@ -94,13 +95,10 @@ AUTH0_SECRET='random 32 byte value' AUTH0_DOMAIN='' AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' +AUTH0_SCOPE='openid profile email offline_access' # OpenAI API Key or any provider supported by the Vercel AI SDK OPENAI_API_KEY="YOUR_API_KEY" - -# LANGGRAPH -LANGGRAPH_API_URL=http://localhost:54367 -LANGCHAIN_CALLBACKS_BACKGROUND=false ``` To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to the **Settings** tab in the Auth0 Dashboard. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. @@ -134,7 +132,7 @@ const auth0AI = new Auth0AI(); // Connection for Google services export const withGoogleConnection = auth0AI.withTokenVault({ connection: "google-oauth2", - scopes: ["https://www.googleapis.com/auth/calendar.events"], + scopes: ["openid", "https://www.googleapis.com/auth/calendar.events"], refreshToken: getRefreshToken, }); ``` @@ -147,7 +145,9 @@ Create a file at `/src/lib/auth0.ts` file with the following code: import { Auth0Client } from '@auth0/nextjs-auth0/server'; // Create an Auth0 Client. -export const auth0 = new Auth0Client(); +export const auth0 = new Auth0Client({ + enableConnectAccountEndpoint: true, +}); // Get the refresh token from Auth0 session export const getRefreshToken = async () => { @@ -261,10 +261,12 @@ import { TokenVaultConsent } from "@/components/auth0-ai/TokenVault"; interface TokenVaultInterruptHandlerProps { interrupt: Auth0InterruptionUI | null; + onFinish?: () => void; } export function TokenVaultInterruptHandler({ interrupt, + onFinish, }: TokenVaultInterruptHandlerProps) { if (!TokenVaultInterrupt.isInterrupt(interrupt)) { return null; @@ -275,6 +277,7 @@ export function TokenVaultInterruptHandler({ { + // Use Auth0 SPA SDK to connect a third-party account + const startConnectAccountFlow = useCallback(async () => { try { setIsLoading(true); // Filter out empty scopes const validScopes = requiredScopes.filter( - (scope: string) => scope && scope.trim() !== "" + (scope: string) => scope && scope.trim() !== "", ); const auth0Client = getAuth0Client(); - // Use getTokenWithPopup for step-up authorization to request additional scopes - await auth0Client.getTokenWithPopup({ - authorizationParams: { - prompt: "consent", // Required for Google Calendar scopes - connection: connection, // e.g., "google-oauth2" - connection_scope: validScopes.join(" "), // Google-specific scopes - access_type: "offline", - }, + // Use the connect account flow to request authorization+consent for the 3rd party API. + // This will redirect the browser away from the SPA. + await auth0Client.connectAccountWithRedirect({ + connection, + scopes: validScopes, + ...(authorizationParams + ? { authorization_params: authorizationParams } + : {}), }); - // The Auth0 client should automatically use the new token, but we should trigger - // a refresh to ensure the latest token is cached. - await auth0Client.getTokenSilently(); - setIsLoading(false); // Resume the interrupted tool after successful authorization @@ -139,13 +144,15 @@ export function TokenVaultConsentPopup({ resume(); } } catch (error) { + console.error("Connect account flow failed:", error); setIsLoading(false); + // Even if login fails, we should clear the interrupt if (typeof resume === "function") { resume(); } } - }, [connection, requiredScopes, resume]); + }, [connection, requiredScopes, authorizationParams, resume]); if (isLoading) { return ( @@ -171,7 +178,8 @@ export function TokenVaultConsentPopup({

    - To access your {connection.replace("-", " ")} data, you need to authorize this application. + To access your {connection.replace("-", " ")} data, you need to + connect your account and authorize this application.

    Required permissions:{" "} @@ -179,8 +187,8 @@ export function TokenVaultConsentPopup({ .filter((scope: string) => scope && scope.trim() !== "") .join(", ")}

    -
    @@ -272,6 +280,7 @@ export const createGoogleCalendarTool = (c: Context): ToolWrapper => { subjectTokenType: SUBJECT_TOKEN_TYPES.SUBJECT_TYPE_ACCESS_TOKEN, connection: process.env.GOOGLE_CONNECTION_NAME || "google-oauth2", scopes: [ + "openid", "https://www.googleapis.com/auth/calendar.calendarlist.readonly", // Read-only access to calendar list "https://www.googleapis.com/auth/calendar.events.readonly", // Read-only access to events ], From acaa0a0ef94b5ab798fba9a88a15c076805d6902 Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Fri, 14 Nov 2025 12:03:19 -0500 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: Patrick Malouin --- .../get-started/langchain-next-js/call-others-api.mdx | 1 - .../get-started/vercel-ai-next-js/call-others-api.mdx | 2 -- .../get-started/vercel-ai-react-spa-js/call-others-api.mdx | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx b/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx index ff0c37bbe8..9796c8acbd 100644 --- a/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx +++ b/auth4genai/snippets/get-started/langchain-next-js/call-others-api.mdx @@ -111,7 +111,6 @@ AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' AUTH0_AUDIENCE="https://your.domain.us.langgraph.app" -AUTH0_SCOPE="openid profile email offline_access" AUTH0_CUSTOM_API_CLIENT_ID="{yourCustomApiClientId}" AUTH0_CUSTOM_API_CLIENT_SECRET="{yourCustomApiClientSecret}" diff --git a/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx b/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx index f903e92120..55dd9875d1 100644 --- a/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx +++ b/auth4genai/snippets/get-started/vercel-ai-next-js/call-others-api.mdx @@ -30,7 +30,6 @@ AUTH0_SECRET='random 32 byte value' AUTH0_DOMAIN='' AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' -AUTH0_SCOPE='openid profile email offline_access' # OpenAI API Key or any provider supported by the Vercel AI SDK OPENAI_API_KEY="YOUR_API_KEY" @@ -95,7 +94,6 @@ AUTH0_SECRET='random 32 byte value' AUTH0_DOMAIN='' AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' -AUTH0_SCOPE='openid profile email offline_access' # OpenAI API Key or any provider supported by the Vercel AI SDK OPENAI_API_KEY="YOUR_API_KEY" diff --git a/auth4genai/snippets/get-started/vercel-ai-react-spa-js/call-others-api.mdx b/auth4genai/snippets/get-started/vercel-ai-react-spa-js/call-others-api.mdx index 09624994e7..95f2f53d32 100644 --- a/auth4genai/snippets/get-started/vercel-ai-react-spa-js/call-others-api.mdx +++ b/auth4genai/snippets/get-started/vercel-ai-react-spa-js/call-others-api.mdx @@ -78,8 +78,8 @@ VITE_API_URL=http://localhost:3001 ```bash .env wrap lines AUTH0_DOMAIN=your-auth0-domain AUTH0_AUDIENCE=your-api-identifier -AUTH0_CUSTOM_API_CLIENT_ID=your-resource-server-client-id -AUTH0_CUSTOM_API_CLIENT_SECRET=your-resource-server-client-secret +AUTH0_CUSTOM_API_CLIENT_ID=your-custom-api-client-id +AUTH0_CUSTOM_API_CLIENT_SECRET=your-custom-api-client-secret OPENAI_API_KEY=your-openai-api-key PORT=3001 ``` From 24383a1cab1512f9a38245f441200638bfe4d436 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Fri, 14 Nov 2025 13:23:03 -0500 Subject: [PATCH 3/7] fix: My Account API prerequisite updates --- .../snippets/get-started/prerequisites/call-others-api.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx b/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx index 6e31306bf8..eebc5c13dd 100644 --- a/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx +++ b/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx @@ -122,12 +122,12 @@ export const Prerequisites = ({ In the Auth0 Dashboard, configure the My Account API to enable account linking: