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..d3bca802cc 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 @@ -56,7 +56,7 @@ OPENAI_API_KEY="YOUR_API_KEY" LANGGRAPH_API_URL=http://localhost:54367 ``` -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. +To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to Applications > Applications in the Auth0 Dashboard and select your client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -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" @@ -156,7 +157,7 @@ OPENAI_API_KEY="YOUR_API_KEY" LANGGRAPH_API_URL=http://localhost:54367 ``` -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. +To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to Applications > Applications in the Auth0 Dashboard and select your client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field. @@ -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..32addcb69d 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}" @@ -45,7 +45,12 @@ OPENAI_API_KEY="YOUR_API_KEY" 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. +To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to Applications > Applications in the Auth0 Dashboard and select your Regular Web application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. + +To get your API's `AUTH0_AUDIENCE`, navigate to Applications > APIs in the Auth0 Dashboard and select your API. You'll find the identifier in the **General Settings** section at the top. + +To get your Custom API Client's `AUTH0_CUSTOM_API_CLIENT_ID`, and `AUTH0_CUSTOM_API_CLIENT_SECRET` navigate to Applications > Applications in the Auth0 Dashboard and select your Custom API Client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -111,7 +116,6 @@ AUTH0_CLIENT_ID='' AUTH0_CLIENT_SECRET='' AUTH0_AUDIENCE="https://your.domain.us.langgraph.app" -AUTH0_SCOPE="openid profile email" AUTH0_CUSTOM_API_CLIENT_ID="{yourCustomApiClientId}" AUTH0_CUSTOM_API_CLIENT_SECRET="{yourCustomApiClientSecret}" @@ -122,8 +126,12 @@ OPENAI_API_KEY="YOUR_API_KEY" 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 Applications > Applications in the Auth0 Dashboard and select your Regular Web application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. -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. +To get your API's `AUTH0_AUDIENCE`, navigate to Applications > APIs in the Auth0 Dashboard and select your API. You'll find the identifier in the **General Settings** section at the top. + +To get your Custom API Client's `AUTH0_CUSTOM_API_CLIENT_ID`, and `AUTH0_CUSTOM_API_CLIENT_SECRET` navigate to Applications > Applications in the Auth0 Dashboard and select your Custom API Client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -132,6 +140,9 @@ openssl rand -hex 32 ``` Lastly, set your OpenAI API key or use any provider supported by the Vercel AI SDK: +```bash .env.local wrap lines +OPENAI_API_KEY="YOUR_API_KEY" +``` ### Set up Token Vault for Google social connection @@ -167,7 +178,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 +194,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..3f127cf3cf 100644 --- a/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx +++ b/auth4genai/snippets/get-started/prerequisites/call-others-api.jsx @@ -8,9 +8,10 @@ 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 + * @param {boolean|undefined} [props.createAuth0ApplicationStep.enableAllowRefreshTokenRotation] - Enable or Disable Allow Refresh Token Rotation for the application (omitted if undefined) * * @param {Object|undefined} [props.createAuth0ApiStep] - Configuration for Auth0 API creation step * @@ -27,6 +28,7 @@ export const Prerequisites = ({ allowedWebOrigins: undefined, enableTokenVaultGrant: undefined, enableRefreshTokenGrant: undefined, + enableAllowRefreshTokenRotation: undefined, }, createAuth0ApiStep = undefined, createResourceServerClientStep = undefined, @@ -43,6 +45,7 @@ export const Prerequisites = ({ allowedWebOrigins: createAuth0ApplicationStep.allowedWebOrigins, enableTokenVaultGrant: createAuth0ApplicationStep.enableTokenVaultGrant, enableRefreshTokenGrant: createAuth0ApplicationStep.enableRefreshTokenGrant, + enableAllowRefreshTokenRotation: createAuth0ApplicationStep.enableAllowRefreshTokenRotation, }) ); @@ -91,24 +94,13 @@ export const Prerequisites = ({
    • - Navigate to{" "} - - Applications > APIs - + From the Settings page of the API that you just created, click the Add Application button in the right top corner. This will open a modal to create a new Custom API Client.
    • - Click the{" "} - Create API button to create a new Custom API. -
    • -
    • - Go to the Custom API you created and click the Add Application button in the right top corner. -
    • -
    • - After that click the Configure Application button in the right top corner. + Give your Custom API Client a name in the Application Name field and click the Add button to create a new Custom API Client.
    • - Note down the client id and client secret{" "} - for your environment variables. + After creation is successful, you should be redirected to the settings page for your newly created Custom API Client application. Note down the client id and client secret{" "}for your environment variables.
    @@ -118,21 +110,32 @@ export const Prerequisites = ({ steps.push( - In the Auth0 Dashboard, configure the My Account API to enable account linking: + In the Auth0 Dashboard, configure the My Account API:
      -
    • Navigate to Authentication > APIs, locate the My Account API banner, and select Activate to activate the My Account API.
    • -
    • To configure your client application to authorize the My Account API: +
    • Navigate to Authentication > APIs, locate the My Account API banner, and select Activate to activate the Auth0 My Account API.
    • +
    • Once activated, select Auth0 My Account API and then select the Applications tab.
        -
      • Navigate to Applications > Applications and select your client application.
      • +
      • Toogle your client application to authorize it to access the My Account API.
      • +
      • In the dropdown menu, select the Connected Accounts scopes for the application, ensuring that at a minimum, the create:me:connected_accounts permission is selected.
      • Under APIs, toggle on Auth0 My Account API.
      • -
      • Select the Connected Accounts scopes for the application in the dropdown.
      • Select Update.
    • +
    • If you're using Multi-Resource Refresh Tokens, navigate to the Settings tab. Under Access Settings, select Allow Skipping User Consent.
    ); + 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..24282883c3 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 @@ -35,7 +35,7 @@ AUTH0_CLIENT_SECRET='' OPENAI_API_KEY="YOUR_API_KEY" ``` -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. +To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to Applications > Applications in the Auth0 Dashboard and select your client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -97,12 +97,8 @@ AUTH0_CLIENT_SECRET='' # 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. +To get your Auth0 application's `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`, navigate to Applications > Applications in the Auth0 Dashboard and select your client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -134,7 +130,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 +143,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 +259,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 +275,7 @@ export function TokenVaultInterruptHandler({ - + ### Download sample app Start by downloading and extracting the sample app. Then open in your preferred IDE. Applications > Applications in the Auth0 Dashboard and select your client application. You'll find these values in the **Basic Information** section at the top. Copy each value to the matching setting. Next, run this command to generate a random 32 byte value and copy it to the `AUTH0_SECRET` field: @@ -219,4 +219,4 @@ Sign up to your application to create a new user account. You will then see a we Want to see how it all comes together? Explore or clone the fully implemented sample application on [GitHub](https://github.com/auth0-samples/auth0-ai-samples/tree/main/authenticate-users/vercel-ai-next-js). - \ No newline at end of file + 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 06869cf17c..99a7ae0c94 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 @@ -10,6 +10,7 @@ import { DownloadQuickstartButton } from "/snippets/download-quickstart/Download allowedWebOrigins: "http://localhost:5173", enableTokenVaultGrant: true, enableRefreshTokenGrant: true, + enableAllowRefreshTokenRotation: true, }} createAuth0ApiStep={{}} createResourceServerClientStep={{}} @@ -25,9 +26,14 @@ This React SPA implementation differs from the + ### Download sample app Start by downloading and extracting the sample app. Then open in your preferred IDE. +### Create your environment files + +Add separate `.env` files with environment variables for the client and server. + +#### Client (client/.env) + +```bash .env wrap lines +VITE_AUTH0_DOMAIN=your-auth0-domain +VITE_AUTH0_CLIENT_ID=your-spa-client-id +VITE_AUTH0_AUDIENCE=your-api-identifier +VITE_API_URL=http://localhost:3001 +``` +To get your SPA application's `VITE_AUTH0_DOMAIN`, and `VITE_AUTH0_CLIENT_ID`, navigate to Applications > Applications in the Auth0 Dashboard and select your SPA application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. + +To get your API's `AUTH0_AUDIENCE`, navigate to APIs > APIs in the Auth0 Dashboard and select your API. You'll find the identifier in the **General Settings** section at the top. + +#### Server (server/.env) + +```bash .env wrap lines +AUTH0_DOMAIN=your-auth0-domain +AUTH0_AUDIENCE=your-api-identifier +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 +``` +To get your Custom API Client's `AUTH0_DOMAIN`, `AUTH0_AUDIENCE`, and `AUTH0_CUSTOM_API_CLIENT_ID`, and `AUTH0_CUSTOM_API_CLIENT_SECRET` navigate to Applications > Applications in the Auth0 Dashboard and select your Custom API Client application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. + +The `AUTH0_AUDIENCE` should match the identifier of the API referenced in the `client/.env` file above. + +Lastly, set your OpenAI API key or use any provider supported by the Vercel AI SDK: +```bash .env.local wrap lines +OPENAI_API_KEY="YOUR_API_KEY" +``` + +### Install packages +Ensure you have `npm` installed or follow the instructions to [install npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) in its documentation. +In the root directory of your project, run the following command to install the required packages: + +```bash wrap lines +npm install +``` + +### Test your application + +1. Start the client and server using Turbo: `npm run dev`. +2. Navigate to `http://localhost:5173`. +3. Log in with Google and ask your AI agent about your calendar. + +The application will automatically prompt for additional calendar permissions when needed using Auth0's step-up authorization flow. + +That's it! You've successfully called a third-party API using Token Vault in your React SPA + Vercel AI application. + + + ### Install dependencies -In the root directory of your project, you have the following client and server dependencies: +In the root directory of your project, ensure you have the following client and server dependencies: **Client dependencies:** @@ -72,16 +137,190 @@ VITE_AUTH0_AUDIENCE=your-api-identifier VITE_API_URL=http://localhost:3001 ``` +To get your SPA application's `VITE_AUTH0_DOMAIN`, and `VITE_AUTH0_CLIENT_ID`, navigate to Applications > Applications in the Auth0 Dashboard and select your SPA application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. + +To get your API's `AUTH0_AUDIENCE`, navigate to APIs > APIs in the Auth0 Dashboard and select your API. You'll find the identifier in the **General Settings** section at the top. + #### Server (server/.env) ```bash .env wrap lines AUTH0_DOMAIN=your-auth0-domain AUTH0_AUDIENCE=your-api-identifier -AUTH0_CLIENT_ID=your-resource-server-client-id -AUTH0_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 ``` +To get your Custom API Client's `AUTH0_DOMAIN`, `AUTH0_AUDIENCE`, and `AUTH0_CUSTOM_API_CLIENT_ID`, and `AUTH0_CUSTOM_API_CLIENT_SECRET` navigate to Applications > Applications in the Auth0 Dashboard and select your Custom API Client application. You'll find these values in the **Basic Information** section at the top. +Copy each value to the matching setting. + +The `AUTH0_AUDIENCE` should match the identifier of the API referenced in the `client/.env` file above. + +Lastly, set your OpenAI API key or use any provider supported by the Vercel AI SDK: +```bash .env.local wrap lines +OPENAI_API_KEY="YOUR_API_KEY" +``` + +### Create an Auth0 Provider and initialize the Auth0 SPA SDK + +Create `client/src/lib/auth0.ts` and initialize the Auth0 SPA SDK with your Auth0 application details, along with configuration for token storage and refresh tokens: + +```ts client/src/lib/auth0.ts wrap lines +import { Auth0Client, createAuth0Client } from "@auth0/auth0-spa-js"; + +// Auth0 configuration +const AUTH0_DOMAIN = import.meta.env.VITE_AUTH0_DOMAIN; +const AUTH0_CLIENT_ID = import.meta.env.VITE_AUTH0_CLIENT_ID; +const AUTH0_AUDIENCE = import.meta.env.VITE_AUTH0_AUDIENCE; + +export const initAuth0 = async (): Promise => { + if (auth0Client) { + return auth0Client; + } + + auth0Client = await createAuth0Client({ + domain: AUTH0_DOMAIN, + clientId: AUTH0_CLIENT_ID, + authorizationParams: { + redirect_uri: window.location.origin, + audience: AUTH0_AUDIENCE, + scope: "openid profile email offline_access", + }, + // Store tokens in localstorage to allow restoring the user session after following redirects. + // Redirects are necessary to connect an account for the user. + cacheLocation: 'localstorage', + useRefreshTokens: true, + useMrrt: true, + useDpop: true, + }); + + return auth0Client; +}; +``` + +Create a Provider component to wrap your application with Auth0 context in `client/src/components/Auth0Provider.tsx`: + +```tsx client/src/components/Auth0Provider.tsx wrap lines +import React, { ReactNode, useEffect, useRef, useState } from "react"; + +import { User } from "@auth0/auth0-ai-js-examples-react-hono-ai-sdk-shared"; + +import { + getToken, + getUser, + initAuth0, + isAuthenticated, + login, + logout, +} from "../lib/auth0"; +import { Auth0Context, Auth0ContextType } from "./auth0-context"; + +interface Auth0ProviderProps { + children: ReactNode; +} + +export const Auth0Provider: React.FC = ({ children }) => { + const [isLoading, setIsLoading] = useState(true); + const [isAuthenticatedState, setIsAuthenticatedState] = useState(false); + const [user, setUser] = useState(null); + const [error, setError] = useState(null); + const initRef = useRef(false); + + useEffect(() => { + // Prevent double execution in React Strict Mode + if (initRef.current) return; + initRef.current = true; + + const initializeAuth0 = async () => { + try { + setIsLoading(true); + const client = await initAuth0(); + + // Check if user is returning from login redirect or connect flow + if ( + (window.location.search.includes("code=") || + window.location.search.includes("connect_code=")) && + window.location.search.includes("state=") + ) { + await client.handleRedirectCallback(); + window.history.replaceState({}, document.title, window.location.pathname); + } + + const authenticated = await isAuthenticated(); + setIsAuthenticatedState(authenticated); + + if (authenticated) { + const userData = await getUser(); + setUser(userData as User); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Authentication error"); + } finally { + setIsLoading(false); + } + }; + + initializeAuth0(); + }, []); + + const handleLogin = async (targetUrl?: string) => { + try { + setError(null); + await login(targetUrl); + } catch (err) { + setError(err instanceof Error ? err.message : "Login failed"); + } + }; + + const handleLogout = async () => { + try { + setError(null); + await logout(); + setIsAuthenticatedState(false); + setUser(null); + } catch (err) { + setError(err instanceof Error ? err.message : "Logout failed"); + } + }; + + const contextValue: Auth0ContextType = { + isLoading, + isAuthenticated: isAuthenticatedState, + user, + error, + login: handleLogin, + logout: handleLogout, + getToken, + }; + + return ( + + {children} + + ); +}; +``` + +Finally, wrap your application with the `Auth0Provider` in `client/src/main.tsx`: + +```tsx client/src/main.tsx wrap lines +import "./index.css"; + +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +import App from "./App.tsx"; +import { Auth0Provider } from "./contexts/Auth0Context.tsx"; + +createRoot(document.getElementById("root")!).render( + + + + + +); +``` ### Configure the SPA for step-up authorization @@ -90,11 +329,19 @@ Unlike the { + // Use Auth0 SPA SDK to connect an external 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 external 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 +382,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 +416,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 +425,8 @@ export function TokenVaultConsentPopup({ .filter((scope: string) => scope && scope.trim() !== "") .join(", ")}

    -
    @@ -272,6 +518,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 ], @@ -571,3 +818,6 @@ The application will automatically prompt for additional calendar permissions wh That's it! You've successfully integrated Token Vault with access tokens into your React SPA + Vercel AI application. Explore [the example app on GitHub](https://github.com/auth0/auth0-ai-js/tree/main/examples/calling-apis/spa-with-backend-api/react-hono-ai-sdk). + + +