From 549f9df5c622372eacab503f162ed9ab537e5c44 Mon Sep 17 00:00:00 2001 From: flemming-it Date: Thu, 28 May 2026 16:37:08 +0200 Subject: [PATCH] feat(client): authToken parameter for Bearer auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `HubClient` accepts an optional `authToken: String?` that attaches `authorization: Bearer ` to every gRPC + gRPC- Web call as default CallOptions metadata. Operators with `auth.tokens:` configured on the hub now have a one-line client-side wire-up: final client = HubClient( endpoint: HubEndpoint(host: '...', port: 50051), authToken: 'secret-from-1password-or-vault', ); Without `authToken`, calls go unauthenticated — the hub either accepts (anonymous mode) or rejects with `UNAUTHENTICATED` (16) when auth is required. dart analyze + dart test green (4 tests). Signed-off-by: flemming-it --- lib/src/hub_client.dart | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/src/hub_client.dart b/lib/src/hub_client.dart index b0e1000..8cf22f8 100644 --- a/lib/src/hub_client.dart +++ b/lib/src/hub_client.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; +import 'package:grpc/grpc.dart' as grpc; import 'package:grpc/grpc_connection_interface.dart'; import 'channel_factory_io.dart' @@ -92,12 +93,32 @@ class HubClient { final grpc_hub.HubClient _hub; final grpc_hub.HubAdminClient _admin; - HubClient._(this.endpoint, this._channel) - : _hub = grpc_hub.HubClient(_channel), - _admin = grpc_hub.HubAdminClient(_channel); + HubClient._(this.endpoint, this._channel, grpc.CallOptions? defaultOptions) + : _hub = grpc_hub.HubClient(_channel, options: defaultOptions), + _admin = grpc_hub.HubAdminClient(_channel, options: defaultOptions); - factory HubClient({HubEndpoint endpoint = const HubEndpoint()}) { - return HubClient._(endpoint, channel_factory.createChannel(endpoint)); + /// Construct a client. + /// + /// Supply [authToken] when the hub is configured with + /// `auth.tokens:` in operator config — the value is sent as + /// `authorization: Bearer ` metadata on every + /// gRPC call. Without a token, calls are unauthenticated + /// (the hub may reject them with `UNAUTHENTICATED` (16) when + /// auth is required). + factory HubClient({ + HubEndpoint endpoint = const HubEndpoint(), + String? authToken, + }) { + final defaultOptions = (authToken == null || authToken.isEmpty) + ? null + : grpc.CallOptions( + metadata: {'authorization': 'Bearer $authToken'}, + ); + return HubClient._( + endpoint, + channel_factory.createChannel(endpoint), + defaultOptions, + ); } /// Liveness probe. Returns true when the hub responds with