diff --git a/example/aws_iot_cognito.dart b/example/aws_iot_cognito.dart deleted file mode 100644 index 0d53168..0000000 --- a/example/aws_iot_cognito.dart +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Package : mqtt_client - * Author : S. Hamblett - * Date : 21/04/2022 - * Copyright : S.Hamblett - * - */ - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:mqtt_client/mqtt_server_client.dart'; -import 'package:mqtt_client/mqtt_client.dart'; -//HTTP import 'package:http/http.dart'; -import 'package:sigv4/sigv4.dart'; - -/// An example of connecting to the AWS IoT Core MQTT broker and publishing to a devices topic. -/// This example uses MQTT over Websockets with AWS IAM Credentials -/// This is a proven working example, but it requires some preparation. You will need to get Cognito credentials from somewhere, and your IAM policies set up properly. -/// The first two functions are helpers, please look at the main() for the client setup -/// More instructions can be found at https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html and -/// https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html, please read this -/// before setting up and running this example. - -/// Note the dependency on the http package has been removed from the client, as such lines below -/// depending on this are commented out. If you wish to run this example please re add package http -/// at version 1.2.1 to the pubspec.yaml and uncomment lines starting with HTTP. - -// This function is based on the one from package flutter-aws-iot, but adapted slightly -String getWebSocketURL( - {required String accessKey, - required String secretKey, - required String sessionToken, - required String region, - required String scheme, - required String endpoint, - required String urlPath}) { - const serviceName = 'iotdevicegateway'; - const awsS4Request = 'aws4_request'; - const aws4HmacSha256 = 'AWS4-HMAC-SHA256'; - var now = Sigv4.generateDatetime(); - - var creds = [ - accessKey, - now.substring(0, 8), - region, - serviceName, - awsS4Request, - ]; - - var queryParams = { - 'X-Amz-Algorithm': aws4HmacSha256, - 'X-Amz-Credential': creds.join('/'), - 'X-Amz-Date': now, - 'X-Amz-SignedHeaders': 'host', - }; - - var canonicalQueryString = Sigv4.buildCanonicalQueryString(queryParams); - - var request = Sigv4.buildCanonicalRequest( - 'GET', - urlPath, - queryParams, - {'host': endpoint}, - '', - ); - - var hashedCanonicalRequest = Sigv4.hashPayload(request); - var stringToSign = Sigv4.buildStringToSign( - now, - Sigv4.buildCredentialScope(now, region, serviceName), - hashedCanonicalRequest, - ); - - var signingKey = Sigv4.calculateSigningKey( - secretKey, - now, - region, - serviceName, - ); - - var signature = Sigv4.calculateSignature(signingKey, stringToSign); - - var finalParams = - '$canonicalQueryString&X-Amz-Signature=$signature&X-Amz-Security-Token=${Uri.encodeComponent(sessionToken)}'; - - return '$scheme$endpoint$urlPath?$finalParams'; -} - -Future attachPolicy( - {required String accessKey, - required String secretKey, - required String sessionToken, - required String identityId, - required String iotApiUrl, - required String region, - required String policyName}) async { - final sigv4Client = Sigv4Client( - keyId: accessKey, - accessKey: secretKey, - sessionToken: sessionToken, - region: region, - serviceName: 'execute-api'); - - final body = json.encode({'target': identityId}); - - final request = - sigv4Client.request('$iotApiUrl/$policyName', method: 'PUT', body: body); - - //HTTP remove the line below - print(request); - //HTTP var result = await put(request.url, headers: request.headers, body: body); - - //HTTP if (result.statusCode != 200) { - //HTTP print('Error attaching IoT Policy ${result.body}'); - //HTTP } - - //HTTP return result.statusCode == 200; - //HTTP remove the line below - return true; -} - -Future main() async { - // Your AWS region - const region = ''; - // Your AWS IoT Core endpoint url - const baseUrl = '.iot.$region.amazonaws.com'; - const scheme = 'wss://'; - const urlPath = '/mqtt'; - // AWS IoT MQTT default port for websockets - const port = 443; - // Your AWS IoT Core control API endpoint (https://docs.aws.amazon.com/general/latest/gr/iot-core.html#iot-core-control-plane-endpoints) - const iotApiUrl = 'https://iot.$region.amazonaws.com/target-policies'; - // The AWS IOT Core policy name that you want to attach to the identity - const policyName = ''; - - // The necessary AWS credentials to make a connection. - // Obtaining them is not part of this example, but you can get the below credentials via any cognito/amplify library like amazon_cognito_identity_dart_2 or amplify_auth_cognito. - String accessKey = ''; - String secretKey = ''; - String sessionToken = ''; - String identityId = ''; - - // PLEASE READ CAREFULLY - // This attaches an iot policy to an identity id to allow iot core access - // When using Cognito Federated identity pools, there are AUTHENTICATED and UNAUTHENTICATED (guest) identities (https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html). - // You MUST attach a policy for an AUTHENTICATED user to allow access to iot core (regular cognito or federated id) - // You CAN attach a policy to an UNAUTHENTICATED user for control, but this is not necessary - // Make sure that the the credentials that call this API have the right IAM permissions for AttachPolicy (https://docs.aws.amazon.com/iot/latest/apireference/API_AttachPolicy.html) - if (!await attachPolicy( - accessKey: accessKey, - secretKey: secretKey, - sessionToken: sessionToken, - identityId: identityId, - iotApiUrl: iotApiUrl, - region: region, - policyName: policyName)) { - print('MQTT client setup error - attachPolicy failed'); - exit(-1); - } - - // Transform the url into a Websocket url using SigV4 signing - String signedUrl = getWebSocketURL( - accessKey: accessKey, - secretKey: secretKey, - sessionToken: sessionToken, - region: region, - scheme: scheme, - endpoint: baseUrl, - urlPath: urlPath); - - // Create the client with the signed url - MqttServerClient client = MqttServerClient.withPort( - signedUrl, identityId, port, - maxConnectionAttempts: 2); - - // Set the protocol to V3.1.1 for AWS IoT Core, if you fail to do this you will not receive a connect ack with the response code - client.setProtocolV311(); - // logging if you wish - client.logging(on: false); - client.useWebSocket = true; - client.secure = false; - client.autoReconnect = true; - client.disconnectOnNoResponsePeriod = 90; - client.keepAlivePeriod = 30; - - final MqttConnectMessage connMess = - MqttConnectMessage().withClientIdentifier(identityId); - - client.connectionMessage = connMess; - - // Connect the client - try { - print('MQTT client connecting to AWS IoT using cognito....'); - await client.connect(); - } on Exception catch (e) { - print('MQTT client exception - $e'); - client.disconnect(); - exit(-1); - } - - if (client.connectionStatus!.state == MqttConnectionState.connected) { - print('MQTT client connected to AWS IoT'); - - // Publish to a topic of your choice - const topic = '/test/topic'; - final builder = MqttClientPayloadBuilder(); - builder.addString('Hello World'); - // Important: AWS IoT Core can only handle QOS of 0 or 1. QOS 2 (exactlyOnce) will fail! - client.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!); - - // Subscribe to the same topic - client.subscribe(topic, MqttQos.atLeastOnce); - // Print incoming messages from another client on this topic - client.updates!.listen((List> c) { - final recMess = c[0].payload as MqttPublishMessage; - final pt = - MqttPublishPayload.bytesToStringAsString(recMess.payload.message); - print( - 'EXAMPLE::Change notification:: topic is <${c[0].topic}>, payload is <-- $pt -->'); - print(''); - }); - } else { - print( - 'ERROR MQTT client connection failed - disconnecting, state is ${client.connectionStatus!.state}'); - client.disconnect(); - } - - print('Sleeping....'); - await MqttUtilities.asyncSleep(10); - - print('Disconnecting'); - client.disconnect(); - - return 0; -} diff --git a/example/aws_iot_cognito_amplify.dart b/example/aws_iot_cognito_amplify.dart index 3900f39..1645b60 100644 --- a/example/aws_iot_cognito_amplify.dart +++ b/example/aws_iot_cognito_amplify.dart @@ -14,8 +14,7 @@ import 'package:aws_signature_v4/aws_signature_v4.dart'; import 'package:mqtt_client/mqtt_server_client.dart'; import 'package:mqtt_client/mqtt_client.dart'; -//HTTP import 'package:http/http.dart'; -import 'package:sigv4/sigv4.dart'; +import 'package:http/http.dart'; /// An example of connecting to the AWS IoT Core MQTT broker and publishing to a devices topic. /// This example uses MQTT over Websockets with AWS IAM Credentials @@ -25,10 +24,6 @@ import 'package:sigv4/sigv4.dart'; /// https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html, please read this /// before setting up and running this example. -/// Note the dependency on the http package has been removed from the client, as such lines below -/// depending on this are commented out. If you wish to run this example please re add package http -/// at version 1.2.1 to the pubspec.yaml and uncomment lines starting with HTTP. - // This function is based on the one from package flutter-aws-iot, but adapted slightly String getWebSocketURL( {required String accessKey, @@ -71,31 +66,40 @@ Future attachPolicy( required String iotApiUrl, required String region, required String policyName}) async { - final sigv4Client = Sigv4Client( - keyId: accessKey, - accessKey: secretKey, - sessionToken: sessionToken, - region: region, - serviceName: 'execute-api'); + final creds = AWSCredentials(accessKey, secretKey, sessionToken); + final signer = AWSSigV4Signer( + credentialsProvider: AWSCredentialsProvider(creds), + ); + + final scope = AWSCredentialScope(region: region, service: AWSService.iot); final body = json.encode({'target': identityId}); - //HTTP remove the two lines below. - print(sigv4Client); - print(body); + final request = AWSHttpRequest( + method: AWSHttpMethod.put, + uri: Uri.parse('$iotApiUrl/$policyName'), + headers: { + 'Content-Type': 'application/json', + }, + body: body.codeUnits, + ); - //HTTPfinal request = - //HTTPsigv4Client.request('$iotApiUrl/$policyName', method: 'PUT', body: body); + final signedRequest = await signer.sign( + request, + credentialScope: scope, + ); - //HTTP var result = await put(request.url, headers: request.headers, body: body); + final result = await put( + signedRequest.uri, + headers: signedRequest.headers, + body: signedRequest.body, + ); - //HTTPf (result.statusCode != 200) { - //HTTPprint('Error attaching IoT Policy ${result.body}'); - //HTTP} + if (result.statusCode != 200) { + print('Error attaching IoT Policy ${result.body}'); + } - //HTTPreturn result.statusCode == 200; - //HTTP remove the line below - return true; + return result.statusCode == 200; } Future main() async { diff --git a/example/mqtt_client_universal.dart b/example/mqtt_client_universal.dart index 7adb078..dcaa354 100644 --- a/example/mqtt_client_universal.dart +++ b/example/mqtt_client_universal.dart @@ -8,7 +8,7 @@ // The following scheme can be used conditionally import either the server or browser client // automatically. // -// import 'server.dart' if (dart.library.html) 'browser.dart' as mqttsetup; +// import 'server.dart' if (dart.library.js_interop) 'browser.dart' as mqttsetup; // ... // var client = mqttsetup.setup(serverAddress, uniqueID, port); // diff --git a/lib/mqtt_browser_client.dart b/lib/mqtt_browser_client.dart index 72537a5..98035bb 100644 --- a/lib/mqtt_browser_client.dart +++ b/lib/mqtt_browser_client.dart @@ -8,10 +8,11 @@ library mqtt_browser_client; import 'dart:async'; -import 'package:universal_html/html.dart'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:event_bus/event_bus.dart' as events; import 'package:typed_data/typed_data.dart' as typed; +import 'package:web/web.dart'; import 'mqtt_client.dart'; part 'src/mqtt_browser_client.dart'; diff --git a/lib/src/connectionhandling/browser/mqtt_client_mqtt_browser_ws_connection.dart b/lib/src/connectionhandling/browser/mqtt_client_mqtt_browser_ws_connection.dart index da7c671..adc3fdd 100644 --- a/lib/src/connectionhandling/browser/mqtt_client_mqtt_browser_ws_connection.dart +++ b/lib/src/connectionhandling/browser/mqtt_client_mqtt_browser_ws_connection.dart @@ -49,7 +49,8 @@ class MqttBrowserWsConnection extends MqttBrowserConnection { MqttLogger.log('MqttBrowserWsConnection::connect - WS URL is $uriString'); try { // Connect and save the socket. - final client = WebSocket(uriString, protocols); + final client = + WebSocket(uriString, protocols.map((e) => e.toJS).toList().toJS); this.client = client; client.binaryType = 'arraybuffer'; messageStream = MqttByteBuffer(typed.Uint8Buffer()); @@ -121,7 +122,8 @@ class MqttBrowserWsConnection extends MqttBrowserConnection { 'MqttBrowserWsConnection::connectAuto - WS URL is $uriString'); try { // Connect and save the socket. - final client = WebSocket(uriString, protocols); + final client = + WebSocket(uriString, protocols.map((e) => e.toJS).toList().toJS); this.client = client; client.binaryType = 'arraybuffer'; messageStream = MqttByteBuffer(typed.Uint8Buffer()); @@ -221,6 +223,6 @@ class MqttBrowserWsConnection extends MqttBrowserConnection { final messageBytes = message.read(message.length); var buffer = messageBytes.buffer; var bData = ByteData.view(buffer); - client?.sendTypedData(bData); + client?.send(bData.jsify()!); } } diff --git a/pubspec.yaml b/pubspec.yaml index e9ee7e6..17e7a95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: mqtt_client description: A server and browser based MQTT client for Dart supporting normal, secure sockets and websockets. -version: 10.5.0 +version: 11.0.0 repository: https://github.com/shamblett/mqtt_client homepage: https://github.com/shamblett/mqtt_client @@ -8,15 +8,15 @@ funding: - https://www.darticulate.com/#funding environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: typed_data: '^1.3.2' event_bus: '^2.0.0' path: '^1.9.0' - universal_html: '^2.2.4' crypto: '^3.0.3' meta: '^1.15.0' + web: '>=0.5.0 <2.0.0' dev_dependencies: test: '^1.25.8' @@ -24,8 +24,8 @@ dev_dependencies: build_runner: '^2.4.11' build_test: '^2.2.2' build_web_compilers: '^4.0.10' - sigv4: '^5.0.0' mocktail: '^1.0.4' + http: '^1.2.2' aws_signature_v4: '^0.6.0' aws_common: '^0.7.0'