Documentation
Mobile
Web
TV

All but React Native are in beta.

Desktop App
docs

Impersonate Tool

Configure an authorized user-search API and attach the current impersonation headers to requests sent to your backend. Select a test user, verify the header in Network, then stop impersonation and confirm the header is removed.

Your backend must authenticate the operator and authorize the target user. Adding a header does not grant permission.

what your users see, in 72 seconds

The film shows the React Native tool.

Search example users

1 / 6

This interactive tool uses fictional users. A real app needs a configured user-search and impersonation flow authorized by your backend.

9:41
Loading live demo…
Mock users, real component — the same code that renders in your app. Step through the tour, or just start tapping.

Installation

pub
flutter pub add buoy_impersonate

Unlike other Buoy tools, Impersonate requires configuration because it needs to integrate with your user search API. Using the buoy umbrella? The dial entry is already registered — still call registerBuoyImpersonate(...) to wire search.


Quick Start

dart
import 'package:buoy_impersonate/buoy_impersonate.dart';

registerBuoyImpersonate(
  onSearchUsers: (query) async => api.searchUsers(query), // List<ImpersonateUser>
);
import 'package:buoy_impersonate/buoy_impersonate.dart';

registerBuoyImpersonate(
  onSearchUsers: (query) async => api.searchUsers(query), // List<ImpersonateUser>
);

The snippets assume your app defines api.searchUsers and a Dio instance named dio. Add the interceptor only to the client for your authorized backend, not to a shared client that also sends requests to third-party services:

dart
import 'package:dio/dio.dart';

dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
  options.headers.addAll(BuoyImpersonate.instance.impersonationHeaders);
  handler.next(options);
}));
import 'package:dio/dio.dart';

dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
  options.headers.addAll(BuoyImpersonate.instance.impersonationHeaders);
  handler.next(options);
}));

How It Works

When impersonation is active, impersonationHeaders contains:

x-impersonate-user-id: user_123
x-impersonate-user-id: user_123

Your backend checks for this header and returns data for the specified user instead of the authenticated admin. Your backend decides what the headers mean — Buoy just makes toggling them one tap.

Security Note: Your backend should validate that the authenticated user has admin/impersonation permissions before honoring this header.


Features

Search for users by email, name, or ID. Results display in a clean card format showing user details and metadata. When viewing an active impersonation, the card shows a control to stop directly from the search results.

Impersonation History

Quick-switch between recently impersonated users. History persists across app restarts. Each history entry shows when the user was last impersonated.

Data Clearing

Pass optional clearing callbacks so switching users clears stale client state:

CallbackTypical use
onClearReactQueryInvalidate Riverpod/query-style caches
onClearReduxReset a global store
onClearAsyncStorageClear shared_preferences (preserve Buoy keys yourself if needed)
onClearMMKVClear an MMKV / secure backend

Unlike React Native, Flutter has no runtime auto-detection — “configured” means you passed the callback.

Floating Banner

A floating banner appears when impersonation is active, to identify the selected user. Toggle it in Settings.


Configuration

onSearchUsers should return List<ImpersonateUser>:

dart
class ImpersonateUser {
  final String id;           // Sent in the impersonation header
  final String? displayName; // Shown in UI (falls back to email, then id)
  final String? email;
  final String? avatarUrl;
  final Map<String, Object?>? metadata;
}
class ImpersonateUser {
  final String id;           // Sent in the impersonation header
  final String? displayName; // Shown in UI (falls back to email, then id)
  final String? email;
  final String? avatarUrl;
  final Map<String, Object?>? metadata;
}

Defaults

Customize the header key and related defaults:

dart
registerBuoyImpersonate(
  onSearchUsers: api.searchUsers,
  defaults: const ImpersonateDefaults(
    headerKey: 'x-admin-impersonate',
  ),
);
registerBuoyImpersonate(
  onSearchUsers: api.searchUsers,
  defaults: const ImpersonateDefaults(
    headerKey: 'x-admin-impersonate',
  ),
);

Read the live map anywhere with BuoyImpersonate.instance.impersonationHeaders.


What's Next


FAQ

How do I test a Flutter app as another user without logging out?

Register buoy_impersonate with your user-search callback, pick a user in the tool, and attach BuoyImpersonate.instance.impersonationHeaders in your HTTP client — a dio interceptor, for example. The state persists across launches and mirrors live to Buoy Desktop.

Does impersonation bypass my authentication?

No. Dart has no global fetch to patch, so you decide where the headers go, and your backend decides what impersonation means and who is allowed to use it. Buoy is the switch, not the authority.