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