Skip to main content
RapidDev - Software Development Agency
retool-tutorial

How to Handle Authentication Tokens in Retool

Retool handles OAuth 2.0 token refresh automatically when configured in the resource settings — enable 'Refresh token' and provide the token endpoint URL. For bearer token APIs, store tokens as secret environment variables and reference them as {{ environment.variables.API_TOKEN }}. Never display raw tokens in UI components. Use JS Query to decode JWT payloads for debugging.

What you'll learn

  • Configure OAuth 2.0 token refresh in Retool resource settings to prevent expired token errors
  • Understand the difference between per-user OAuth credentials and shared service account credentials
  • Store and access tokens securely using Retool environment/configuration variables
  • Inspect JWT token payloads inside JS Queries without exposing them in the UI
  • Handle token expiration errors gracefully with query error handlers
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read20-25 minRetool Cloud and Self-hostedLast updated March 2026RapidDev Engineering Team
TL;DR

Retool handles OAuth 2.0 token refresh automatically when configured in the resource settings — enable 'Refresh token' and provide the token endpoint URL. For bearer token APIs, store tokens as secret environment variables and reference them as {{ environment.variables.API_TOKEN }}. Never display raw tokens in UI components. Use JS Query to decode JWT payloads for debugging.

Quick facts about this guide
FactValue
ToolRetool
DifficultyBeginner
Time required20-25 min
CompatibilityRetool Cloud and Self-hosted
Last updatedMarch 2026

Token Management in Retool: Resources, OAuth, and JWTs

Authentication tokens are the credentials that Retool uses to communicate with external APIs on behalf of your app. Managing them correctly is the difference between an app that keeps working and one that silently fails when tokens expire.

Retool's resource system handles most token complexity automatically: OAuth 2.0 resources can be configured to auto-refresh access tokens before they expire. For bearer token APIs, tokens are stored encrypted in resource credentials or environment variables.

The most common token issues in Retool: OAuth access tokens expire (typically after 1 hour) and the app starts returning 401 errors; JWT tokens need inspection for debugging; teams unsure whether to use shared vs per-user credentials.

Prerequisites

  • A Retool app connected to at least one REST API or OAuth-based resource
  • Admin access to Retool Settings to configure resources
  • Basic understanding of OAuth 2.0 flow (access token + refresh token)

Step-by-step guide

1

Configure OAuth 2.0 token refresh in resource settings

Navigate to Settings → Resources and open the resource that uses OAuth 2.0. In the authentication section, ensure 'OAuth 2.0' is selected as the auth type. Find the 'Access token URL' field and verify it points to the OAuth provider's token endpoint (e.g., https://api.service.com/oauth/token). Enable 'Refresh token' if the option is available. Set the 'Refresh token URL' (often the same as the access token URL). Retool will automatically use the refresh token to obtain a new access token before the current one expires, preventing 401 errors in production.

Expected result: Resource is configured with OAuth 2.0 and automatic token refresh. Queries continue working after the 1-hour access token expiry.

2

Understand per-user vs shared credentials

Retool resources support two authentication modes: (1) Shared credentials — one set of credentials used by all users of the app. Appropriate for service-to-service APIs where you own a service account. (2) Per-user OAuth — each Retool user authenticates with the external service using their own account. Data operations are scoped to that user's permissions. Per-user OAuth is configured in the resource settings under 'Authentication type' → 'OAuth 2.0 (per user)'. Users see a 'Connect your account' button the first time they use the app.

Expected result: Resource is configured with the appropriate credential sharing mode for the use case.

3

Store bearer tokens as environment/configuration variables

For APIs that use static bearer tokens (not OAuth), store the token as a Retool environment variable, not hardcoded in query bodies. Navigate to Settings → Environment Variables (or Settings → Configuration Variables). Create a new variable named 'EXTERNAL_API_TOKEN', mark it as 'Secret', and enter the token value. In your REST API queries, set the Authorization header to: Bearer {{ environment.variables.EXTERNAL_API_TOKEN }}. Secret variables are encrypted at rest and not visible to non-admin users.

typescript
1// REST API resource header configuration:
2// Header: Authorization
3// Value: Bearer {{ environment.variables.EXTERNAL_API_TOKEN }}
4
5// Or in a JS Query making a fetch call (avoid this — use resource queries):
6// fetch(url, {
7// headers: {
8// 'Authorization': `Bearer ${environment.variables.EXTERNAL_API_TOKEN}`
9// }
10// });

Expected result: API token is stored securely as a secret environment variable. Queries use it without exposing it in the codebase.

4

Inspect JWT payloads for debugging in JS Queries

When debugging authentication issues, you may need to inspect a JWT token's payload. Write a JS Query to decode the JWT payload (the middle section, base64-encoded). Never log or display the full JWT in UI components — only decode it in the DevTools console or a temporary debug query. The decoded payload shows the token's claims: user ID, expiry time, scopes, and audience.

typescript
1// JS Query: inspectJWT (for debugging only — remove from production)
2// Decodes the JWT payload without verifying signature
3
4const token = environment.variables.JWT_TOKEN;
5
6if (!token) {
7 console.log('No JWT token found in environment variables');
8 return null;
9}
10
11try {
12 // JWT has three sections: header.payload.signature
13 const parts = token.split('.');
14 if (parts.length !== 3) throw new Error('Not a valid JWT format');
15
16 // Decode the payload (middle section)
17 const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
18
19 const now = Math.floor(Date.now() / 1000);
20 const expiresIn = payload.exp - now;
21
22 console.log('JWT payload:', JSON.stringify(payload, null, 2));
23 console.log(`Token expires in: ${expiresIn} seconds (${(expiresIn / 60).toFixed(1)} minutes)`);
24
25 return {
26 sub: payload.sub,
27 exp: new Date(payload.exp * 1000).toISOString(),
28 expiresInSeconds: expiresIn,
29 isExpired: expiresIn < 0,
30 scopes: payload.scope || payload.scopes,
31 };
32} catch (err) {
33 console.error('Failed to decode JWT:', err.message);
34 return null;
35}

Expected result: JWT payload is decoded and logged to the browser console, showing expiry time and claims.

5

Handle token expiration errors with query error handlers

Even with refresh token configuration, token errors can occur. Add an 'On failure' event handler to critical API queries that checks for 401 errors. If the error is authentication-related, show a user-friendly message and provide a 'Reconnect' button that triggers the OAuth re-authorization flow. For apps with shared credentials, the error handler can also alert an admin via utils.showNotification().

typescript
1// Query 'On failure' event handler (JS Query)
2// Triggered when an API query returns a 401 or 403 error
3
4const error = query1.error;
5const statusCode = error?.statusCode || error?.response?.status;
6
7if (statusCode === 401) {
8 utils.showNotification({
9 title: 'Session expired',
10 description: 'Your session has expired. Please reconnect your account.',
11 notificationType: 'warning',
12 duration: 10,
13 });
14 // Show reconnect UI
15 await reconnectBanner.setValue(true);
16} else if (statusCode === 403) {
17 utils.showNotification({
18 title: 'Access denied',
19 description: 'You do not have permission to perform this action.',
20 notificationType: 'error',
21 });
22} else {
23 utils.showNotification({
24 title: 'API error',
25 description: `Request failed: ${error?.message || 'Unknown error'}`,
26 notificationType: 'error',
27 });
28}

Expected result: Token expiration errors display user-friendly messages instead of raw error objects.

Complete working example

JS Query: validateAndRefreshToken
1// JS Query: validateAndRefreshToken
2// Run on page load to check token validity before user starts working
3// Uses inspectJWT logic to check expiry, shows warning if near expiry
4
5const token = environment.variables.API_TOKEN;
6
7if (!token) {
8 utils.showNotification({
9 title: 'Configuration error',
10 description: 'API token not configured. Contact your administrator.',
11 notificationType: 'error',
12 duration: 0, // Persistent notification
13 });
14 return;
15}
16
17// Check if token is a JWT and inspect expiry
18try {
19 const parts = token.split('.');
20 if (parts.length === 3) {
21 const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
22 const now = Math.floor(Date.now() / 1000);
23 const expiresIn = payload.exp - now;
24
25 if (expiresIn < 0) {
26 utils.showNotification({
27 title: 'Token expired',
28 description: 'The API token has expired. Please update it in Settings → Resources.',
29 notificationType: 'error',
30 duration: 0,
31 });
32 } else if (expiresIn < 3600) {
33 // Warn if token expires within 1 hour
34 utils.showNotification({
35 title: 'Token expiring soon',
36 description: `API token expires in ${Math.floor(expiresIn / 60)} minutes.`,
37 notificationType: 'warning',
38 duration: 8,
39 });
40 }
41 }
42} catch {
43 // Not a JWT — static API key, no expiry check needed
44}

Common mistakes when handling Authentication Tokens in Retool

Why it's a problem: Storing an API bearer token in a query header hardcoded as 'Authorization: Bearer eyJhb...'

How to avoid: Use a Retool secret environment variable: Authorization: Bearer {{ environment.variables.API_TOKEN }}. Hard-coded tokens are exposed in query definitions and visible to anyone with app edit access.

Why it's a problem: Not enabling refresh token in OAuth resource config, causing apps to fail after the access token expires

How to avoid: In the resource settings, enable 'Refresh token' and configure the refresh token URL. Retool will automatically exchange the refresh token for a new access token before expiry.

Why it's a problem: Using the JWT decode technique to make security decisions (e.g., checking if user is admin based on decoded claims)

How to avoid: JWT signature is not verified in the browser-side decode. Only use it for debugging. All authorization decisions must be enforced server-side in your API, not client-side based on decoded token claims.

Best practices

  • Always store API tokens as secret configuration variables — never hardcode them in query bodies or UI components
  • Enable OAuth refresh token configuration in Retool resources to prevent auth interruptions from token expiry
  • Use per-user OAuth credentials for services where user-level audit trails and permission scoping are required
  • Add 'On failure' handlers to critical queries that check for 401/403 responses and provide actionable user messages
  • Rotate static API tokens regularly and update them in Retool's environment variables without code changes
  • Never expose token values in Text components, table cells, or any part of the UI

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Retool app connecting to a REST API that uses OAuth 2.0 with short-lived access tokens (1 hour TTL) and refresh tokens. I need: (1) instructions for configuring token refresh in Retool resource settings, (2) a JS Query that decodes the JWT payload to check expiry time, (3) an 'On failure' query event handler for 401 errors that shows a reconnect notification, (4) best practice for storing the refresh token securely in Retool. Walk through the complete token lifecycle management setup.

Retool Prompt

Configure Retool resource for OAuth 2.0 token refresh: Access Token URL, Refresh Token URL, and grant type settings. Write a JS Query 'inspectJWT' that decodes {{ environment.variables.JWT_TOKEN }} using atob(token.split('.')[1]) and returns { sub, exp, isExpired, expiresInSeconds }. Show the query 'On failure' handler for 401 status codes that displays a notification and sets reconnectBanner.setValue(true).

Frequently asked questions

Can I see OAuth tokens that Retool is using internally in resource queries?

No — for security, Retool does not expose the actual OAuth access tokens to app developers or end users. You can verify that OAuth is configured and working by testing the resource connection in Settings → Resources. If you need to debug token issues, check the Debug Panel's Network tab for HTTP 401 responses from your API.

What happens in Retool when an OAuth access token expires and there is no refresh token?

Queries using that resource will return a 401 Unauthorized error. Users of per-user OAuth resources will see a prompt to re-authorize their account. For shared credential resources, an admin must manually regenerate the token in the resource settings. This is why enabling refresh token support is critical for production apps.

Can I pass a Retool user's identity token to my backend API for user-level authorization?

Yes — use {{ retoolContext.currentUser.email }} or {{ retoolContext.currentUser.id }} in REST API query headers or parameters. You can also configure your Retool resource to forward the user's identity using the 'Forward user context' option in some resource types. For more secure identity forwarding, implement custom OIDC token passing through an environment variable set by SSO.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Learning is great. Shipping is faster with help.

Our engineers have built 600+ apps on Retool and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.