|
| 1 | +--- |
| 2 | +sidebar_position: 4 |
| 3 | +sidebar_label: Connect your agent to third-party APIs |
| 4 | +--- |
| 5 | + |
| 6 | +# Connect your AI agent to third-party APIs |
| 7 | + |
| 8 | +This guide walks you through enabling your AI agent to access third-party APIs (e.g., Google Calendar, GitHub, etc.) on behalf of users. By leveraging Logto's social connectors and Secret Vault, you can securely store and manage access tokens, allowing your agent to perform automated tasks without repeatedly asking users to re-authenticate. |
| 9 | + |
| 10 | +You'll learn how to: |
| 11 | + |
| 12 | +- Configure social connectors with third-party token storage. |
| 13 | +- Request minimal permissions during initial sign-in. |
| 14 | +- Progressively request additional permissions as needed. |
| 15 | +- Retrieve and use stored tokens to access third-party APIs. |
| 16 | + |
| 17 | +## Why your AI agent needs third-party API access \{#why-your-ai-agent-needs-third-party-api-access} |
| 18 | + |
| 19 | +AI agents are increasingly being used to automate tasks that require interaction with external services. For example: |
| 20 | + |
| 21 | +- **📅 Calendar management**: Your AI agent can automatically schedule meetings, add events, or adjust appointments on Google Calendar. |
| 22 | +- **📧 Email automation**: Send follow-up emails, organize inboxes, or draft responses using Gmail APIs. |
| 23 | +- **💻 Code management**: Create GitHub issues, review pull requests, or manage repositories. |
| 24 | +- **📁 File management**: Upload, organize, or share files on Google Drive or Dropbox. |
| 25 | + |
| 26 | +To perform these tasks, your AI agent needs secure access to user-authorized third-party APIs, which means handling OAuth tokens correctly and securely. |
| 27 | + |
| 28 | +## How it works \{#how-it-works} |
| 29 | + |
| 30 | +Here's a quick overview of the flow: |
| 31 | + |
| 32 | +```mermaid |
| 33 | +sequenceDiagram |
| 34 | + participant User |
| 35 | + participant Agent as Your AI agent |
| 36 | + participant Logto |
| 37 | + participant Google as Third-party provider<br/>(e.g., Google) |
| 38 | +
|
| 39 | + rect rgba(200, 230, 200, 0.5) |
| 40 | + Note over User, Google: User grants permissions to third-party provider |
| 41 | + User->>Agent: "Add a meeting to my calendar tomorrow at 3pm" |
| 42 | + Agent->>User: Needs Google Calendar access, please authorize |
| 43 | + User->>Agent: Initiate Google authorization |
| 44 | + Agent->>Logto: Initiate social verification |
| 45 | + Logto->>Google: Redirect to Google |
| 46 | + User->>Google: Authenticate and grant permissions |
| 47 | + Google->>Logto: Return authorization code |
| 48 | + Logto->>Google: Exchange code for tokens |
| 49 | + Logto->>Logto: Store tokens in Secret Vault |
| 50 | + Logto->>Agent: Return verification result |
| 51 | + end |
| 52 | +
|
| 53 | + rect rgba(200, 200, 230, 0.5) |
| 54 | + Note over User, Google: AI agent task execution |
| 55 | + Agent->>Logto: Retrieve Google access token |
| 56 | + Logto->>Agent: Return access token |
| 57 | + Agent->>Google: Call Google Calendar API |
| 58 | + Google->>Agent: Return success |
| 59 | + Agent->>User: "Done! I've added the meeting to your calendar." |
| 60 | + end |
| 61 | +``` |
| 62 | + |
| 63 | +1. **User requests a task**: The user asks the AI agent to perform a task that requires third-party API access (e.g., adding a calendar event). |
| 64 | +2. **Authorization prompt**: The agent detects the need for third-party access and prompts the user to authorize. |
| 65 | +3. **Tokens stored**: After user authorization, Logto securely stores the access and refresh tokens in the Secret Vault. |
| 66 | +4. **Task execution**: The agent retrieves the stored token and calls the third-party API to complete the task. |
| 67 | + |
| 68 | +Once authorized, the user can perform multiple tasks without re-authorizing. Logto stores the tokens securely and automatically refreshes them when needed, providing a seamless experience for ongoing AI agent interactions. |
| 69 | + |
| 70 | +## Prerequisites \{#prerequisites} |
| 71 | + |
| 72 | +Before you begin, ensure you have: |
| 73 | + |
| 74 | +- A [Logto Cloud](https://cloud.logto.io) (or self-hosted Logto v1.31+) tenant |
| 75 | +- A third-party provider account with API access (e.g., [Google Cloud Console](https://console.cloud.google.com)) |
| 76 | +- An AI agent application integrated with Logto SDK (users can sign in to your AI agent) |
| 77 | + |
| 78 | +## Set up social connector with token storage \{#set-up-social-connector-with-token-storage} |
| 79 | + |
| 80 | +To enable your AI agent to access third-party APIs, you need to configure a social connector with token storage enabled. This allows Logto to store and manage access tokens when users authorize third-party services during their interaction with your AI agent. |
| 81 | + |
| 82 | +Let's use Google as an example: |
| 83 | + |
| 84 | +1. Navigate to <CloudLink to="/connectors/social">Console > Connectors > Social connectors</CloudLink>. |
| 85 | +2. Click **Add social connector** and select **Google**. |
| 86 | +3. Follow the [Google connector setup guide](/integrations/google) to configure your OAuth client credentials. |
| 87 | +4. In the connector settings: |
| 88 | + - Enable **Store tokens for persistent API access** to store tokens in the Secret Vault. |
| 89 | + - Set **Prompts** to include `consent` to ensure users see the permission request. |
| 90 | + - Enable **Offline access** to receive refresh tokens for long-lived API access. |
| 91 | +5. Save your changes. |
| 92 | + |
| 93 | +:::info |
| 94 | +You don't need to add this connector to your sign-in experience. The connector will be used for on-demand authorization when your AI agent needs to access third-party APIs, not for user sign-in. |
| 95 | +::: |
| 96 | + |
| 97 | +## Request authorization and access third-party APIs \{#request-authorization-and-access-third-party-apis} |
| 98 | + |
| 99 | +When your AI agent needs to access a third-party API (e.g., Google Calendar), it should first check if the user has already authorized access. If not, prompt the user to authorize. |
| 100 | + |
| 101 | +:::info Enable Account API |
| 102 | +Before proceeding, enable the Account API at <CloudLink to="/sign-in-experience/account-center">Console > Sign-in experience > Account center</CloudLink>. Learn more about [enabling the Account API](/end-user-flows/account-settings/by-account-api#how-to-enable-account-api). |
| 103 | +::: |
| 104 | + |
| 105 | +### Step 1: Check for existing authorization \{#step-1-check-for-existing-authorization} |
| 106 | + |
| 107 | +First, try to retrieve the stored access token to see if the user has already authorized: |
| 108 | + |
| 109 | +```tsx |
| 110 | +async function getGoogleAccessToken(userAccessToken: string) { |
| 111 | + const response = await fetch( |
| 112 | + 'https://[tenant-id].logto.app/my-account/identities/google/access-token', |
| 113 | + { |
| 114 | + headers: { |
| 115 | + Authorization: `Bearer ${userAccessToken}`, |
| 116 | + }, |
| 117 | + } |
| 118 | + ); |
| 119 | + |
| 120 | + return response.json(); |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +### Step 2: Request authorization if needed \{#step-2-request-authorization-if-needed} |
| 125 | + |
| 126 | +If no token exists, the token has expired, or you need to extend the access token's scope, use Logto's [Social Verification API](/secret-vault/federated-token-set#reauthentication-and-token-renewal) to initiate the authorization flow: |
| 127 | + |
| 128 | +```tsx |
| 129 | +async function requestGoogleAuthorization(userAccessToken: string, scopes: string) { |
| 130 | + // Generate a random state for CSRF protection |
| 131 | + const state = crypto.randomUUID(); |
| 132 | + sessionStorage.setItem('oauth_state', state); |
| 133 | + |
| 134 | + // Initiate social verification |
| 135 | + const response = await fetch('https://[tenant-id].logto.app/api/verification/social', { |
| 136 | + method: 'POST', |
| 137 | + headers: { |
| 138 | + Authorization: `Bearer ${userAccessToken}`, |
| 139 | + 'Content-Type': 'application/json', |
| 140 | + }, |
| 141 | + body: JSON.stringify({ |
| 142 | + connectorId: '<google_connector_id>', |
| 143 | + state, |
| 144 | + redirectUri: 'https://your-ai-agent.com/callback', |
| 145 | + scope: scopes, |
| 146 | + }), |
| 147 | + }); |
| 148 | + |
| 149 | + const { verificationRecordId, authorizationUri } = await response.json(); |
| 150 | + |
| 151 | + // Store verificationRecordId for later use |
| 152 | + sessionStorage.setItem('verificationRecordId', verificationRecordId); |
| 153 | + |
| 154 | + // Redirect user to Google for authorization |
| 155 | + window.location.href = authorizationUri; |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +### Step 3: Handle the authorization callback \{#step-3-handle-the-authorization-callback} |
| 160 | + |
| 161 | +After the user grants permissions, Google redirects back to your app. Complete the verification and store the tokens: |
| 162 | + |
| 163 | +```tsx |
| 164 | +async function handleAuthorizationCallback( |
| 165 | + userAccessToken: string, |
| 166 | + callbackParams: URLSearchParams |
| 167 | +) { |
| 168 | + const verificationRecordId = sessionStorage.getItem('verificationRecordId'); |
| 169 | + const storedState = sessionStorage.getItem('oauth_state'); |
| 170 | + const code = callbackParams.get('code'); |
| 171 | + const state = callbackParams.get('state'); |
| 172 | + |
| 173 | + // Validate state to prevent CSRF attacks |
| 174 | + if (state !== storedState) { |
| 175 | + throw new Error('Invalid state parameter'); |
| 176 | + } |
| 177 | + |
| 178 | + // Verify the authorization |
| 179 | + await fetch('https://[tenant-id].logto.app/api/verification/social/verify', { |
| 180 | + method: 'POST', |
| 181 | + headers: { |
| 182 | + Authorization: `Bearer ${userAccessToken}`, |
| 183 | + 'Content-Type': 'application/json', |
| 184 | + }, |
| 185 | + body: JSON.stringify({ |
| 186 | + verificationRecordId, |
| 187 | + connectorData: { |
| 188 | + code, |
| 189 | + state, |
| 190 | + redirectUri: 'https://your-ai-agent.com/callback', |
| 191 | + }, |
| 192 | + }), |
| 193 | + }); |
| 194 | + |
| 195 | + // Store the tokens in Logto's Secret Vault |
| 196 | + await fetch('https://[tenant-id].logto.app/my-account/identities/google/access-token', { |
| 197 | + method: 'PUT', |
| 198 | + headers: { |
| 199 | + Authorization: `Bearer ${userAccessToken}`, |
| 200 | + 'Content-Type': 'application/json', |
| 201 | + }, |
| 202 | + body: JSON.stringify({ |
| 203 | + verificationRecordId, |
| 204 | + }), |
| 205 | + }); |
| 206 | + |
| 207 | + // Clean up |
| 208 | + sessionStorage.removeItem('verificationRecordId'); |
| 209 | + sessionStorage.removeItem('oauth_state'); |
| 210 | +} |
| 211 | +``` |
| 212 | + |
| 213 | +### Step 4: Call the third-party API \{#step-4-call-the-third-party-api} |
| 214 | + |
| 215 | +Now your AI agent can retrieve the token and call the API: |
| 216 | + |
| 217 | +```tsx |
| 218 | +async function addCalendarEvent(userAccessToken: string, eventDetails: EventDetails) { |
| 219 | + // Get the stored Google access token |
| 220 | + const tokenData = await getGoogleAccessToken(userAccessToken); |
| 221 | + |
| 222 | + if (!tokenData) { |
| 223 | + // User hasn't authorized, request authorization with calendar scope |
| 224 | + await requestGoogleAuthorization( |
| 225 | + userAccessToken, |
| 226 | + 'https://www.googleapis.com/auth/calendar.events' |
| 227 | + ); |
| 228 | + return; // Will continue after redirect |
| 229 | + } |
| 230 | + |
| 231 | + // Call the Google Calendar API |
| 232 | + const response = await fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { |
| 233 | + method: 'POST', |
| 234 | + headers: { |
| 235 | + Authorization: `Bearer ${tokenData.accessToken}`, |
| 236 | + 'Content-Type': 'application/json', |
| 237 | + }, |
| 238 | + body: JSON.stringify(eventDetails), |
| 239 | + }); |
| 240 | + |
| 241 | + return response.json(); |
| 242 | +} |
| 243 | +``` |
| 244 | + |
| 245 | +Logto handles token refresh automatically. If the access token is expired but a refresh token exists, Logto will obtain a new access token transparently when you call the retrieval endpoint. |
| 246 | + |
| 247 | +## Request additional permissions \{#request-additional-permissions} |
| 248 | + |
| 249 | +As your AI agent takes on more tasks, you may need to request additional permissions. For example, if the user initially authorized read-only calendar access but now wants to create events, you need write permissions. |
| 250 | + |
| 251 | +### Why incremental authorization? \{#why-incremental-authorization} |
| 252 | + |
| 253 | +- **Better user experience**: Users are more likely to grant permissions when they understand why they're needed in context. |
| 254 | +- **Higher conversion rates**: Fewer upfront permissions mean less friction. |
| 255 | +- **Trust building**: Users trust applications that only ask for what they need. |
| 256 | + |
| 257 | +### Example: Upgrading from read to write access \{#example-upgrading-from-read-to-write-access} |
| 258 | + |
| 259 | +```tsx |
| 260 | +async function createCalendarEvent(userAccessToken: string, eventDetails: EventDetails) { |
| 261 | + const tokenData = await getGoogleAccessToken(userAccessToken); |
| 262 | + |
| 263 | + if (!tokenData) { |
| 264 | + // No authorization yet, request calendar write permission directly |
| 265 | + await requestGoogleAuthorization(userAccessToken, 'https://www.googleapis.com/auth/calendar'); |
| 266 | + return; |
| 267 | + } |
| 268 | + |
| 269 | + // Try to create the event |
| 270 | + const response = await fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { |
| 271 | + method: 'POST', |
| 272 | + headers: { |
| 273 | + Authorization: `Bearer ${tokenData.accessToken}`, |
| 274 | + 'Content-Type': 'application/json', |
| 275 | + }, |
| 276 | + body: JSON.stringify(eventDetails), |
| 277 | + }); |
| 278 | + |
| 279 | + if (response.status === 403) { |
| 280 | + // Insufficient permissions, request additional scope |
| 281 | + await requestGoogleAuthorization( |
| 282 | + userAccessToken, |
| 283 | + 'https://www.googleapis.com/auth/calendar' // Full calendar access |
| 284 | + ); |
| 285 | + return; |
| 286 | + } |
| 287 | + |
| 288 | + return response.json(); |
| 289 | +} |
| 290 | +``` |
| 291 | + |
| 292 | +:::tip |
| 293 | +When requesting additional scopes, the user will see a consent screen showing only the new permissions being requested. Their existing permissions will be preserved. |
| 294 | +::: |
| 295 | + |
| 296 | +## Manage token status \{#manage-token-status} |
| 297 | + |
| 298 | +Logto Console provides visibility into token status for each user: |
| 299 | + |
| 300 | +1. Navigate to <CloudLink to="/users">Console > User management</CloudLink>. |
| 301 | +2. Click on a user to view their details. |
| 302 | +3. Scroll to the **Connections** section to see all linked social accounts. |
| 303 | +4. Each connection shows the token status: |
| 304 | + - **Active**: The access token is valid and ready to use. |
| 305 | + - **Expired**: The access token has expired. If a refresh token exists, it will be refreshed automatically on next retrieval. |
| 306 | + - **Inactive**: No tokens are stored for this connection. |
| 307 | + |
| 308 | +## Security best practices \{#security-best-practices} |
| 309 | + |
| 310 | +When building AI agents that access third-party APIs, keep these security practices in mind: |
| 311 | + |
| 312 | +- **Request minimal scopes**: Only request the permissions your agent actually needs. |
| 313 | +- **Use incremental authorization**: Request additional permissions in context, not all at once. |
| 314 | +- **Handle token expiration gracefully**: Always handle cases where tokens may be expired or revoked. |
| 315 | +- **Secure user access tokens**: The user's Logto access token is the key to retrieving third-party tokens. Protect it accordingly. |
| 316 | +- **Audit API access**: Log when your AI agent accesses third-party APIs for troubleshooting and compliance. |
| 317 | + |
| 318 | +## Related resources \{#related-resources} |
| 319 | + |
| 320 | +<Url href="/secret-vault/federated-token-set">Third-party token storage</Url> |
| 321 | +<Url href="/connectors/social-connectors">Social connectors</Url> |
| 322 | +<Url href="/end-user-flows/sign-up-and-sign-in/social-sign-in">Social sign-in</Url> |
| 323 | +<Url href="/end-user-flows/account-settings/by-account-api">Account API</Url> |
0 commit comments