diff --git a/lib/handlers/tunnelbroker-device-token-handler.react.js b/lib/handlers/tunnelbroker-device-token-handler.react.js
new file mode 100644
--- /dev/null
+++ b/lib/handlers/tunnelbroker-device-token-handler.react.js
@@ -0,0 +1,52 @@
+// @flow
+
+import * as React from 'react';
+
+import { setTBDeviceTokenActionTypes } from '../actions/tunnelbroker-actions.js';
+import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
+import {
+ messageToTunnelbrokerTypes,
+ type SetDeviceToken,
+} from '../types/tunnelbroker/message-to-tunnelbroker-types.js';
+import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
+
+function TunnelbrokerDeviceTokenHandler(): React.Node {
+ const dispatchActionPromise = useDispatchActionPromise();
+
+ const tunnelbrokerDeviceToken = useSelector(
+ state => state.tunnelbrokerDeviceToken,
+ );
+ const { socketState, sendMessageToTunnelbroker } = useTunnelbroker();
+
+ React.useEffect(() => {
+ if (
+ !socketState.isAuthorized ||
+ !tunnelbrokerDeviceToken.localToken ||
+ tunnelbrokerDeviceToken.localToken ===
+ tunnelbrokerDeviceToken.tunnelbrokerToken
+ ) {
+ return;
+ }
+
+ const message: SetDeviceToken = {
+ type: messageToTunnelbrokerTypes.SET_DEVICE_TOKEN,
+ deviceToken: tunnelbrokerDeviceToken.localToken,
+ };
+ const promise = (async () => {
+ await sendMessageToTunnelbroker(JSON.stringify(message));
+ return { deviceToken: tunnelbrokerDeviceToken.localToken };
+ })();
+ void dispatchActionPromise(setTBDeviceTokenActionTypes, promise);
+ }, [
+ dispatchActionPromise,
+ sendMessageToTunnelbroker,
+ socketState,
+ socketState.isAuthorized,
+ tunnelbrokerDeviceToken,
+ ]);
+
+ return null;
+}
+
+export { TunnelbrokerDeviceTokenHandler };
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -35,6 +35,7 @@
import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js';
import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js';
+import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js';
import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js';
import { IdentitySearchProvider } from 'lib/identity-search/identity-search-context.js';
import { CallKeyserverEndpointProvider } from 'lib/keyserver-conn/call-keyserver-endpoint-provider.react.js';
@@ -286,6 +287,7 @@
+
>
);
let navigation;
diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -23,6 +23,7 @@
import PlatformDetailsSynchronizer from 'lib/components/platform-details-synchronizer.react.js';
import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js';
import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
+import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js';
import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js';
import { IdentitySearchProvider } from 'lib/identity-search/identity-search-context.js';
import {
@@ -248,6 +249,7 @@
+
{content}