How can I use TLS with Paho MQTT over Javascript? - ssl

The code I use currently on my website
var client = null;
var device_is_on = null;
var hostname = "********";
var port = "8003";
var clientId = "mqtt_js_" + parseInt(Math.random() * 100000, 10);
var device_topic = "stat/Device_001/POWER";
var status_topic = "cmnd/Device_001/power";
function connect(){
client = new Paho.MQTT.Client(hostname, Number(port), clientId);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
var options = {
useSSL: true,
userName : "***",
password : "********",
onSuccess: onConnect,
onFailure: onFail
};
client.connect(options);
}
function onConnect(context) {
options = {qos:0}
client.subscribe(device_topic, options);
client.subscribe(status_topic, options);
var payloadd = "6";
message = new Paho.MQTT.Message(payloadd);
message.destinationName = status_topic;
message.retained = true;
client.send(message);
}
function onFail(context) {
}
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
window.alert("Connection Lost!\nPlease Refresh.");
}
}
function onMessageArrived(message) {
if (message.destinationName == device_topic){
var temperature_heading = document.getElementById("device_display");
temperature_heading.innerHTML = "Air Conditioner: " + message.payloadString;
if (message.payloadString == "ON" || message.payloadString == "o"){
device_is_on = true;
} else {
device_is_on = false;
}
}
}
function device_toggle(){
if (device_is_on){
var payload = "off";
device_is_on = false;
} else {
var payload = "on";
device_is_on = true;
}
message = new Paho.MQTT.Message(payload);
message.destinationName = status_topic;
message.retained = true;
client.send(message);
}
What should I put under the "" var options "" section? currently I am getting the error ERR_CERT_AUTHORITY_INVALID in the console of Google Chrome.
Note 1: This code functions perfectly over http but I am converting to https.
Note 2: I use Mosquitto as my MQTT broker.
Help in much appreciated.

It looks like you are using a self signed certificate. This will not be trusted by your browser so it will not connect, raising the error you have shown.
You have 2 options:
Import the certificate into your browser and mark it as trusted (how you do this will vary depending on what browser you are using). This is only really useful for testing/development because normal users should not be importing random certificates as this opens them up to all kinds of security problems.
Get a real trusted certificate for your website and broker. The simplest/cheapest way to do this will be to use letsencrypt. You can then configure mosquitto to use this certificate.

TLS javascript paho client is available: Github paho.mqtt.javascript/issues/88

Related

Django channels without redis

I have a django app based on this tutorial that works perfectly. It uses Redis in the Channel layers
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
The problem I have is that my web hosting provider will not allow Redis (unless I pay ££££).
Every example that I can find uses Redis in this role. Is there an alternative I could use?
there are a few options.
you can run your channel layer on a different service to were the main instance runs. AWS ElastiCache or many other redis hosts out there.
There is also a RabbitMQ channel layer but if your hosting provider charges a lot for reddis i expect they will also charge a lot for this ... https://github.com/CJWorkbench/channels_rabbitmq/
It turned out that channels is a non-starter on an affordable web-hosting platform. So I reverted to using Ajax and long polling. My application is based on this Django Tutorial.
models.py
class Message(models.Model):
room_name = models.CharField(null=False, blank=False, max_length=50)
sender = models.CharField(null=False, blank=False, max_length=50, default='Sender username')
datetime = models.DateTimeField(null=True, auto_now_add=True)
type = models.IntegerField(null=True, blank=True)
text = models.CharField(null=False, blank=False, max_length=250, default='message text')
context = models.TextField(null=True, blank=True)
urls.py
urlpatterns = [
path('<str:partner_pk>/check-message', views.CheckMessage.as_view(), name="check-message"),
path('<str:partner_pk>/send-message/<str:chat_text>', views.SendMessage.as_view(), name="send-message"),
]
views.py
class CheckMessage(View):
"""Duo check message."""
def get(self, request, partner_pk):
"""Render the GET request."""
pair, room_name = sort_pair(partner_pk, request.user.pk)
partner = User.objects.get(pk=partner_pk)
profile = get_object_or_404(Profile, user=request.user)
message = Message.objects.filter(room_name=room_name, sender=partner.username).earliest('datetime')
context = {'type': -1}
context = json.loads(message.context)
context['sender'] = message.sender
context['datetime'] = message.datetime
context['message_type'] = message.type
context['text'] = message.text
context['seat'] = profile.seat
message.delete()
return JsonResponse(context, safe=False)
class SendMessage(View):
def get(self, request, partner_pk, chat_text):
message_type = app.MESSAGE_TYPES['chat']
send_message(request, partner_pk, message_type, text=chat_text, context={})
return JsonResponse({}, safe=False)
chat.js
window.setInterval(checkMessage, 3000);
function checkMessage () {
$.ajax(
{
type:"GET",
url: "check-message",
cache: false,
success: function(message) {
processMessage(message);
}
}
)
}
// Take action when a message is received
function processMessage(context) {
switch (context.message_type) {
case 0:
sendMessage(context)
functionOne()
break;
case 1:
sendMessage(context)
functionTwo()
break;
case 2:
sendMessage(context)
functionThree()
break;
}
}
// Send a message to chat
function sendMessage (context) {
if (context.sender != username) {
var messageObject = {
'username': context.sender,
'text': context.text,
};
displayChat(context);
}
}
// Display a chat message in the chat box.
function displayChat(context) {
if (context.text !== '') {
var today = new Date();
var hours = pad(today.getHours(), 2)
var minutes = pad(today.getMinutes(), 2)
var seconds = pad(today.getSeconds(), 2)
var time = hours + ":" + minutes + ":" + seconds;
var chat_log = document.getElementById("chat-log");
chat_log.value += ('('+time+') '+context.sender + ': ' + context.text + '\n');
chat_log.scrollTop = chat_log.scrollHeight;
}
}
//pad string with leading zeros
function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
// Call submit chat message if the user presses <return>.
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function (e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
// Submit the chat message if the user clicks on 'Send'.
document.querySelector('#chat-message-submit').onclick = function (e) {
var messageField = document.querySelector('#chat-message-input'), text = messageField.value, chat_log = document.getElementById("chat-log");
context = {sender: username, text: messageField.value}
displayChat(context)
sendChat(messageField.value)
chat_log.scrollTop = chat_log.scrollHeight;
messageField.value = '';
};
// Call the send-chat view
function sendChat(chat_text) {
$.ajax(
{
type:"GET",
url: "send-message/"+chat_text,
cache: false,
}
)
}

Question for RawRabbit: sends various messages autonomously

I didn't understand, I have a client that sends various messages autonomously, it doesn't wait for the ack but it has to send them and that's it, but seems that it send only the first one and all the others only when I close the application.
where am I wrong? what should i set?.
var config = new RawRabbitConfiguration()
{
Username = username,
Password = password,
VirtualHost = "/",
Hostnames = new List<string>() { hostname },
AutoCloseConnection = false,
//Ssl = new SslOption() { Enabled = true },
Port = port,
Exchange = new GeneralExchangeConfiguration
{
AutoDelete = false,
Durable = true,
Type = RawRabbit.Configuration.Exchange.ExchangeType.Direct
},
Queue = new GeneralQueueConfiguration
{
Exclusive = false,
AutoDelete = false,
Durable = true
}
};
var options = new RawRabbitOptions() { ClientConfiguration = config };
client = RawRabbitFactory.CreateSingleton(options);
client.SubscribeAsync<MessageModel>(async msg =>
{
return await Task.Run(() => MessageReceived(msg));
},
ctx => ctx.UseSubscribeConfiguration(
cfg => cfg.FromDeclaredQueue(
queue => queue.WithName(queueName))))
.GetAwaiter();
UPDATE: function for sending that I use...
public void SendMessage(MessageModel message, string machineName = null, string exchangeName = null)
{
if (!string.IsNullOrEmpty(machineName))
message.MachineName = machineName;
else if (string.IsNullOrEmpty(message.MachineName))
message.MachineName = this.MachineName;
if (!string.IsNullOrEmpty(LastMessageReceived?.ID))
message.RequestID = LastMessageReceived.ID;
else
message.RequestID = string.Empty;
if (!string.IsNullOrEmpty(LastMessageReceived?.MachineName))
message.MachineNameDest = LastMessageReceived.MachineName;
else if (string.IsNullOrEmpty(message.MachineNameDest))
message.MachineNameDest = string.Empty;
try
{
if (string.IsNullOrEmpty(exchangeName))
client.PublishAsync<MessageModel>(message);
else
client.PublishAsync<MessageModel>(message,
ctx => ctx.UsePublishConfiguration(
cfg => cfg.OnExchange(exchangeName)));
}
catch (Exception ex)
{
OnError?.Invoke(this, ex);
}
LastMessageReceived = null;
}
EDIT:
In what case is the error "Stage Initialized has no additional middlewares registered" generated ?
I cannot understand why this error is generated on "SubscribeAsync" and after does not send messages. :(
Please, help me.

Authentication in Chromecast CAF Receiver application

I have a help URL which is to be authenticated with a token before playing it. How can I add a token header to a receiver CAF application? I searched in the documentation but couldn't find any reference of authentication for a receiver CAF application.
In V2 player we can intercept the request with updateSegmentRequestInfo as shown below but im not sure how to do it with CAF Application. Can someone help?
host.updateSegmentRequestInfo = function(requestInfo) {
console.log("Inside updateSegmentRequestInfo");
requestInfo.withCredentials = true;
requestInfo.headers = {};
requestInfo.headers['token'] = window.token;
console.log("token sent");
};
Set cookies on player load event.
Use this code:
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const castOptions = new cast.framework.CastReceiverOptions();
let playbackConfig = (Object.assign(new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
// Set cookies here.
// No need to pass cookies into header in each segment.
// console.log("content id:", request.media.contentId);
// Set your segment valid hls format : below is example:
// Refer other format:
// https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages#.HlsSegmentFormat
request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.TS;
return request;
});
playbackConfig.manifestRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
};
playbackConfig.segmentRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
};
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
};
castOptions.playbackConfig = playbackConfig;
context.start(castOptions);

Identityserver4 doesn't work by ip address

I've used IdentityServer4 with asp net core Web, all works fine when debug in localhost:50481, but when I use myipaddress:50481 on the same computer and debug mode, it failed. I do not use a temporary credential, instead, I created a RSA cert:
.AddSigningCredential(Config.GetSigningCertificate())
public static RsaSecurityKey GetSigningCertificate()
{
var filename = Path.Combine(Directory.GetCurrentDirectory(), "certificateKey.rsa");
if (File.Exists(filename))
{
var keyFile = File.ReadAllText(filename);
var tempKey = JsonConvert.DeserializeObject<TemporaryRsaKey>(keyFile, new JsonSerializerSettings() { ContractResolver = new RsaKeyContractResolver() });
return CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId);
}
else
{
var key = CreateRsaSecurityKey();
RSAParameters parameters;
if (key.Rsa != null)
parameters = key.Rsa.ExportParameters(includePrivateParameters: true);
else
parameters = key.Parameters;
var tempKey = new TemporaryRsaKey
{
Parameters = parameters,
KeyId = key.KeyId
};
File.WriteAllText(filename, JsonConvert.SerializeObject(tempKey, new JsonSerializerSettings() { ContractResolver = new RsaKeyContractResolver() }));
return CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId);
}
}
I also checked the jwks of localhost and ipaddress, they are matched.
When I publish the project to local IIS, localhost does not work too, present a 500 Internal error.
all the url in my app is "http://localhost:50481"
I have to say this is a stupid mistake, I have not notice the authConfig,
let config;
if (window.location.hostname === 'localhost') {
config = configForDevelopment;
} else {
config = configForProduction;
}
when I use ip address, the config is switch to prod, change localhost to my ip address make sense.
hope it could others.

Single page application client and server routing

I've got the following code to handle client side navigation using HTML5 pushstate (classic combination of crossroadsjs and historyjs):
History = window.History;
History.Adapter.bind(window, 'statechange', function () {
var state = History.getState();
console.log(state);
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else
{
if (state.hash.length > 1) {
var fullHash = state.hash;
var hashPath = fullHash.slice(0, fullHash.indexOf('?'));
return crossroads.parse(hashPath);
}
}});
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
crossroads.parse('/');
$('body').on('click', 'a', function(e) {
var title, urlPath;
urlPath = $(this).attr('href');
if (urlPath.slice(0, 1) == '#'){
return true;
}
e.preventDefault();
title = $(this).text().trim();
return History.pushState({ urlPath: urlPath }, title, urlPath);
});
It works really well. Now, to handle url bookmarking and sharing, I added and express server to handle all requests. All it does is to redirect to index.html (a sort of catchall rule):
var env = require('./env');
var fallback = require('express-history-api-fallback');
var express = require('express');
var app = express();
var config = env.config();
var root = __dirname + '/dist';
app.use(express.static(root));
app.use(fallback('index.html', { root: root }));
var port = process.env.PORT || 9090;
var server = app.listen(port, function () {
console.log('Server started at: http://localhost:' + port);
console.log(config);
});
The problem I am facing is that it successfully redirects to index.html but it doesn't load the correct route on the client side. So a request to www.mysite.com or www.mysite.com/anotherpage will always load the home page route.
I am obviously missing some code to intercept that and load the appropriate route on the client side. I just don't know what to do.
Found where the bug was:
crossroads.parse('/');
This was always redirecting to the "home" route. I just had to refactor the code a bit:
History.Adapter.bind(window, 'statechange', this.routeCrossRoads);
routeCrossRoads() {
var state = History.getState();
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else {
if (state.hash.length > 1) {
var fullHash = state.hash;
var pos = fullHash.indexOf('?');
if (pos > 0) {
var hashPath = fullHash.slice(0, pos);
return crossroads.parse(hashPath);
}
else {
return crossroads.parse(fullHash);
}
}
else {
return crossroads.parse('/');
}
}
}