Skip to content

Commit 958ceda

Browse files
authored
feat(vercel-react-spa-js): add markdown formatting (#57)
* feat(vercel-react-spa-js): add markdown formatting * fix: various inconsistencies with twin sample in `auth0-ai-js` * fix: formatting * fix: get rid of `any`
1 parent f8cefda commit 958ceda

File tree

9 files changed

+2305
-69
lines changed

9 files changed

+2305
-69
lines changed

call-apis-on-users-behalf/others-api/vercel-react-spa-js/README.md

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The flow works by having the client send its access token to a First Party API,
1010

1111
## Features
1212

13-
The following example app demonstrates using a SPA chatbot application, a backend API (and a linked Resource Server Client), and Token Vault to access a Third Party API (Google Calendar API).
13+
The following example app demonstrates using a SPA chatbot application, a backend API (and a linked Custom API Client), and Token Vault to access a Third Party API (Google Calendar API).
1414

1515
This template leverages a modern stack for building a React SPA application with a Hono API.
1616

@@ -51,7 +51,7 @@ You will need the following prerequisites to run this app:
5151

5252
### 1. Auth0 Configuration
5353

54-
1. Create an Auth0 Application:
54+
1. Create an Auth0 SPA Application:
5555
- Go to your [Auth0 Dashboard](https://manage.auth0.com/)
5656
- Create a new **Single Page Application**
5757
- Configure the following settings:
@@ -64,59 +64,62 @@ You will need the following prerequisites to run this app:
6464
2. Create an Auth0 API (representing your back-end API):
6565
- In your Auth0 Dashboard, go to APIs
6666
- Create a new API with an identifier (audience)
67-
- Enable "Allow Offline Access" in Access Settings
67+
- Make sure to "Allow Offline Access" in Access Settings
6868
- Note down the API identifier for your environment variables
6969

7070
3. Create a Custom API Client (for Token Vault Token Exchange):
71-
- The Custom API Client allows your API server to perform token exchanges using **access tokens** instead of **refresh tokens**. This client enables Token Vault to exchange an access token for an external API access token (e.g., Google Calendar API).
72-
- Setup steps:
73-
- Go to the API you created in Step #2 and click the **Add Application** button in the right top corner and create the application
74-
- In the application settings, ensure that the `Token Vault` grant type is enabled under the Advanced Settings
75-
- Note down the <code>client id</code> and <code>client secret</code> for your environment variables
71+
- This is a special application that allows your API to perform token exchanges
72+
- In your Auth0 Dashboard, on the configuration page of your API, click the "Add Application" button in the header and create the Custom API Client
73+
- Ensure that the `Token Vault` grant type is enabled under the Advanced Settings
74+
- Note down the "Client ID" and "Client Secret" of this newly created Custom API Client
75+
- Now your Custom API will be able to use Token Vault, to exchange an access token for an external API access token (e.g., Google Calendar API)
7676

7777
4. Grant access to My Account API from your application:
78-
- When a call to Token Vault fails due to the user not having a connected account (or lacking some permissions), this demo triggers a Connect Account flow for this user. This flow leverages Auth0's [My Account API](https://auth0.com/docs/manage-users/my-account-api), and as such, your application will need to have access to it in order to enable this flow.
79-
- In order to grant access, use the [Application Access to APIs](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) feature, by creating a client grant for user flows.
80-
- In your Auth0 Dashboard, go to APIs, and open the Settings for "Auth0 My Account API".
81-
- On the Settings tab, make sure to enable the "Allow Skipping User Consent" toggle.
82-
- On the Applications tab, authorize your SPA application, ensuring that the `create:me:connected_accounts` permission at least is selected.
78+
- When a call to Token Vault fails due to the user not having a connected account (or lacking some permissions), this demo triggers a Connect Account flow for this user. This flow leverages Auth0's [My Account API](https://auth0.com/docs/manage-users/my-account-api), and as such, your application will need to have access to it in order to enable this flow.
79+
- In order to grant access, use the [Application Access to APIs](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) feature, by creating a client grant for user flows.
80+
- In your Auth0 Dashboard, go to APIs, and open the Settings for "Auth0 My Account API".
81+
- On the Settings tab, make sure to enable the "Allow Skipping User Consent" toggle.
82+
- On the Applications tab, authorize your application, ensuring that the `create:me:connected_accounts` permission at least is selected.
8383

8484
5. Define a Multi-Resource Refresh Token policy for your SPA Application:
85-
- After your SPA Application has been granted access to the My Account API, you will also need to leverage the [Multi-Resource Refresh Token](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token) feature, where the refresh token delivered to your SPA will allow it to obtain an access token to call My Account API.
86-
- This will require defining a new [refresh token policy](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token/configure-and-implement-multi-resource-refresh-token) for your SPA Application where the `audience` is `https://<your auth0 domain>/me/` and the `scope` should include at least the `"create:me:connected_accounts"` scope.
87-
- Setup steps:
88-
- In your Auth0 Dashboard, go to Applications, and open the Settings for your SPA application created at step 1.
89-
- Under the "Multi-Resource Refresh Token" section, click "Edit Configuration".
90-
- Enable MRRT for "Auth0 My Account API".
91-
92-
6. Configure a Social Connection for Google in Auth0:
93-
- Make sure to enable the "Use for Connected Accounts with Token Vault" toggle
94-
- Make sure to enable `Offline Access` and all `Calendar` scopes from the Permissions options
95-
- On the Applications tab, make sure to enable the connection for your SPA Application created in Step 1 and the Custom API Client created in Step 3
96-
- Test the connection in Auth0 "Try Connection" screen and make sure connection is working & configured correctly
85+
- After your SPA Application has been granted access to the My Account API, you will also need to leverage the [Multi-Resource Refresh Token](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token) feature, where the refresh token delivered to your SPA will allow it to obtain an access token to call My Account API.
86+
- This will require defining a new [refresh token policy](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token/configure-and-implement-multi-resource-refresh-token) for your SPA Application where the `audience` is `https://<your auth0 domain>/me/` and the `scope` should include at least the `"create:me:connected_accounts"` scope.
87+
- Setup steps:
88+
- In your Auth0 Dashboard, go to Applications, and open the Settings for your SPA application created at step 1.
89+
- Under the "Multi-Resource Refresh Token" section, click "Edit Configuration".
90+
- Enable MRRT for "Auth0 My Account API".
91+
92+
6. Configure a Social Connection for Google in Auth0
93+
- Make sure to enable the "Use for Connected Accounts with Token Vault" toggle
94+
- Make sure to enable `Offline Access` and all `Calendar` scopes from the Permissions options
95+
- On the Applications tab, make sure to enable the connection for your SPA Application created in Step 1
96+
- Test the connection in Auth0 "Try Connection" screen and make sure the connection is working & configured correctly
9797

9898
### 2. Environment Variables
9999

100100
#### Client (.env)
101-
Copy `.env.example` to `.env` and fill in your Auth0 configuration:
101+
From the `./client` directory, copy `.env.example` to `.env` and fill in your Auth0 configuration using details from your SPA Application:
102102

103103
```bash
104+
# Auth0 Configuration
104105
VITE_AUTH0_DOMAIN=your-auth0-domain.auth0.com
105106
VITE_AUTH0_CLIENT_ID=your-spa-client-id
106107
VITE_AUTH0_AUDIENCE=your-api-identifier
108+
109+
# Server Configuration
107110
VITE_SERVER_URL=http://localhost:3000
108111
```
109112

110113
#### Server (.env)
111-
Copy `.env.example` to `.env` and fill in your Auth0 configuration:
114+
From the `./server` directory, copy `.env.example` to `.env` and fill in your Auth0 configuration using details from API and Custom API Client:
112115

113116
```bash
114117
# Auth0 Configuration
115118
AUTH0_DOMAIN=your-auth0-domain.auth0.com
116119
AUTH0_AUDIENCE=your-api-identifier
117120

118-
# Custom API CLient Configuration (for Token Vault token exchange)
119-
# These credentials belong to a special "resource_server" client that can perform token exchanges
121+
# Custom API Client Configuration (for Token Vault token exchange)
122+
# These credentials belong to Custom API client that can perform token exchanges
120123
AUTH0_CUSTOM_API_CLIENT_ID=your-custom-api-client-id
121124
AUTH0_CUSTOM_API_CLIENT_SECRET=your-custom-api-client-secret
122125

@@ -149,7 +152,7 @@ npm run dev
149152
or run them individually with:
150153
```bash
151154
npm run dev:client # Run the Vite dev server for React
152-
npm run dev:server # Run the Hono backend
155+
npm run dev:server # Run the Hono API backend
153156
```
154157

155158
The client will be available at `http://localhost:5173` and will communicate with the server at `http://localhost:3000`.
@@ -187,7 +190,7 @@ The client will be available at `http://localhost:5173` and will communicate wit
187190
## SDK Notes
188191

189192
The core `@auth0/ai` package now supports:
190-
- **Resource Server Client Credentials**: Separate client credentials for token exchange operations
193+
- **Custom API Client Credentials**: Separate client credentials for token exchange operations
191194
- **Access Token Support**: Direct access token exchange instead of requiring refresh tokens
192195

193196
The example uses the enhanced SDK pattern with dedicated access token support:
@@ -196,9 +199,9 @@ The example uses the enhanced SDK pattern with dedicated access token support:
196199
const auth0AI = new Auth0AI({
197200
auth0: {
198201
domain: process.env.AUTH0_DOMAIN!,
199-
// For token exchange with Token Vault, we want to provide the resource server client (linked client's) credentials
200-
clientId: process.env.AUTH0_CUSTOM_API_CLIENT_ID!, // Resource server client
201-
clientSecret: process.env.AUTH0_CUSTOM_API_CLIENT_SECRET!, // Resource server secret
202+
// For token exchange with Token Vault, we want to provide the Custom API Client credentials
203+
clientId: process.env.AUTH0_CUSTOM_API_CLIENT_ID!, // Custom API Client ID for token exchange
204+
clientSecret: process.env.AUTH0_CUSTOM_API_CLIENT_SECRET!, // Custom API Client secret
202205
}
203206
});
204207

@@ -212,12 +215,12 @@ export const withGoogleCalendar = auth0AI.withTokenVault({
212215
Tools can also now use the SDK's built-in token management when using access tokens with Token Vault token exchange:
213216
```typescript
214217
// tools/listNearbyEvents.ts
215-
import { getAccessTokenForConnection } from "@auth0/ai-vercel";
218+
import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel";
216219

217220
export const listNearbyEvents = withGoogleCalendar(
218221
tool({
219222
execute: async ({ start, end, calendarId }) => {
220-
const token = getAccessTokenForConnection(); // Automatic token retrieval
223+
const token = getAccessTokenFromTokenVault(); // Automatic token retrieval
221224
// Use token with Google Calendar API...
222225
}
223226
})

call-apis-on-users-behalf/others-api/vercel-react-spa-js/client/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,15 @@
2121
"ai": "5.0.33",
2222
"class-variance-authority": "^0.7.1",
2323
"clsx": "^2.1.1",
24+
"katex": "^0.16.25",
2425
"lucide-react": "^0.507.0",
2526
"react": "^19.1.0",
2627
"react-dom": "^19.1.0",
28+
"react-markdown": "^10.1.0",
29+
"react-syntax-highlighter": "^16.1.0",
30+
"rehype-katex": "^7.0.1",
31+
"remark-gfm": "^4.0.1",
32+
"remark-math": "^6.0.0",
2733
"tailwind-merge": "^3.3.1",
2834
"tailwindcss": "^4.1.10"
2935
},
@@ -32,6 +38,7 @@
3238
"@types/node": "^22.15.31",
3339
"@types/react": "^19.1.8",
3440
"@types/react-dom": "^19.1.6",
41+
"@types/react-syntax-highlighter": "^15.5.13",
3542
"@vitejs/plugin-react": "^4.5.2",
3643
"eslint": "^9.28.0",
3744
"eslint-plugin-react-hooks": "^5.2.0",

call-apis-on-users-behalf/others-api/vercel-react-spa-js/client/src/components/Chat.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
1111

1212
import { useAuth0 } from "../hooks/useAuth0";
1313
import { TokenVaultConsentPopup } from "./TokenVaultConsentPopup";
14+
import { MarkdownText } from "./MarkdownText";
1415
import { Button } from "./ui/button";
1516
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
1617
import { Input } from "./ui/input";
@@ -42,7 +43,7 @@ export function Chat() {
4243
console.error("Chat error:", error);
4344
}),
4445
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
45-
})
46+
}),
4647
);
4748

4849
const { messages, sendMessage, status, error, setMessages, toolInterrupt } =
@@ -155,7 +156,13 @@ function MessageBubble({ message }: { message: UIMessage }) {
155156
isUser ? "bg-primary text-primary-foreground" : "bg-muted"
156157
}`}
157158
>
158-
<p className="text-sm whitespace-pre-wrap">{textContent}</p>
159+
{isUser ? (
160+
<p className="text-sm whitespace-pre-wrap">{textContent}</p>
161+
) : (
162+
<div className="text-sm">
163+
<MarkdownText>{textContent}</MarkdownText>
164+
</div>
165+
)}
159166
</div>
160167
</div>
161168
);

0 commit comments

Comments
 (0)