Preventing strangers accessing WEBRTC pages - grails

I am creating a sample application to tryout WEBRTC. I came across a tutorial that explains the process. I tried copying the code and it seems to work.
Question is:
I ONLY want a few people to access the page that containing the stream. How can i prevent other unauthorised users from accessing my page. A tutorial that walks through the process would be ideal.
the language i am using to develop is Grails.

You can use authentication plugin for the grails to deny unauthorized request. You can even use Socket.io over Node.js to prevent unauthorized users where you can check "userid":
// socket.io nodejs side code
io.sockets.on('connection', function (socket) {
var userid = socket.handshake.query.userid;
// verify userid
if (typeof objUserArrays[userid] == 'undefined') {
// don't broadcast messages; so that user can NEVER join any room.
return;
}
});
and browser side code:
var socket = io.connect('http://your-domain.com/?userid=something');
For last snippet; you can check meeting.js's openSignalingChannel method:
meeting.openSignalingChannel = function(callback) {
return io.connect('http://your-domain.com/?userid=something').on('message', callback);
};

Related

Workaround for missing "Web Push" on Safari for my PWA

I am developing a PWA that requires Push-Notifications. Sadly IOS/Safari does not support https://w3c.github.io/push-api/#pushmanager-interface for now, so I think i might have to wrap a native APP around in some way.
In Android (before their "Trusted Web Activities" was a thing) you could use a WebView to basically display a headless Chrome-View in your App. Whats the equivalent in IOS and how does the interaction between push-notifications and the Webapp (the browser need to jump to a specific page) work?
One more thing I need is integration with our companys Mobile Device Management, which is Microsoft Intune. Having integrated MDMs in Android in the past i Know that this might be a major pain in the a**, so i'm considering to build the wrapper myself, for maximum flexibility. Another option would be something like Ionic, not sure now.
This may not necessarily work in your situation, but I had the exact same issue with a PWA for Safari and I solved it by just using long polling. It will allow you to get around all of the limitations with Safari and I was able to redirect and load sections within our SPA.
async function subscribe() {
let response = await fetch("/subscribe");
if (response.status == 502) {
// Status 502 is a connection timeout error,
// may happen when the connection was pending for too long,
// and the remote server or a proxy closed it
// let's reconnect
await subscribe();
} else if (response.status != 200) {
// An error - let's show it
showMessage(response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Get and show the message
let message = await response.text();
showMessage(message);
// Call subscribe() again to get the next message
await subscribe();
}
}
subscribe();
https://javascript.info/long-polling

communication between service worker instances

I'm trying to establish a communication channel between an installing service worker and an activated service worker.
I've tried to do the following:
on the installing service worker:
if ((self.registration.active == null) ||
(self.registration.active.state != "activated")) {
return;
}
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event){
if (event.data.error) {
console.log("got error from active");
}
else {
console.log("got answer from active");
}
};
self.registration.active.postMessage({message: 'hey', port: messageChannel.port2}, [messageChannel.port2]);
on the active service worker:
self.addEventListener('message', function(event) {
console.log('received message');
});
This isn't working, I'm getting nothing...
Ideas?
Here's how I ended up implementing this.
Each serviceWorker at startup (code at the worker's global scope) connects to a broadcast channel like so:
var comChannel = new BroadcastChannel('SWCom');
comChannel.addEventListener('message', event => {
handleMessageEvent(event);
});
This channel is shared only between service workers.
To post a message to other SW, a SW can just broadcast on the channel comChannel.postMessage('hey there'); and any registered listener will be invoked.
One complication I had, not really related to the channel, is that I had a hard time identifying each SW life cycle state. If I want to communicate between SW it can't really serve any purpose if I don't know who's whom within each one of them. A SW cannot currently get a ref to its own ServiveWorker object, there's an open issue about it in the standard - https://github.com/w3c/ServiceWorker/issues/1077
In my usecase, I bypassed this limitation by performing the communication upon install (fits my needs...), like so:
self.addEventListener('install', function(event) {
if (self.registration.active != null) {
// if we got here it means this is a new SW that's
// starting install and there's an active one running.
// So we can send a message on the broadcast channel,
// whomever answers should be the active SW.
}
// ..
// installation code
// ..
}
One thing to note - I'm not sure this is well implemented.
I believe there are other states a SW can be at (redundant, deleted?, others?), so maybe there can be more then two ServiceWorkers alive, and then the assumption on the identity of the answering side on the channel might be wrong...
Jake provides some excellent examples of messaging that you may be able to derive a solution from. https://gist.github.com/jakearchibald/a1ca502713f41dfb77172be4523a9a4c
You may need to use the page itself as a proxy for sending/receiving messages between Service workers:
[SW1] <== message ==> [Page JS] <== message ==> [SW2]

Intercept responses from WebSocket connection in UIWebView

I have an app which has a UIWebView inside of it with a loaded website. This website has a chart in it which is periodicly updated with data from remote server via websockets (socket.io).
Im new to websockets technology but Im trying to somehow intercept the chart data that the website is receiving from server via it.
Till now I have managed to catch http requests sent by the website of such address format: “http://website-address/socket.io/?auth_token=...”
I have the socket.io library for iOS but don’t know how to use it to somehow spoof the website connection and acquire the data downloaded by the website. Can anyone help? Is it even possible?
Switch to WKWebView if you can. Using javascript bridge is much easier there. That said, with UIWebView, you'd need to inject a script that adds a handler for events received by the socket that you are trying to listen to. You can either create an io variable by yourself but apparently the server needs auth token. If you cannot create an auth token, you can only do this if you have access to the io variable created by the website.
Then for adding a handler, you'll need to know what the event name is, that delivers the chart data. You can snoop around the website and see if you can find that. If you cannot all bets are off. Once we register a handler and get the data, we need to pass this back to your native code. This is where WKWebView would keep it clean by letting you add message handlers that can deliver messages from js to native code. For UIWebView you'll have to create a custom url scheme and spoof a navigation request to pass the data. Lets assume your custom url scheme is 'myApp'. Then the script you'd need to inject would be:
<script>
/* if you can access/create the auth token
var socket = io('http://website-address/socket.io/?auth_token=');
*/
var socket = getioReferenceCreatedByWebsite();
socket.on('<eventName>',function(){
window.location = 'myApp://<data>';
};
</script>
In your native code:
...
webView.delegate = self;
[webView stringByEvaluatingJavaScriptFromString:#"<theAboveJSAsAString>"];
....
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if(request.URL.scheme == #"myApp"){
NSString *data = request.URL.path;
//handle the data
return NO;
}
return YES;
}
In regards to Santhosh R answer. I had the problem he mentioned where I could not get a reference to the websocket object as it was caught up in a closure.
I solved this by adding in a preload script which wraps the native Websocket object to store any instantiated websocket objects in an array and then return the newly created websocket object.
Here is the code.
in your WebView element add in a preload attribute.
<webview id="myWebview" src="http://exmple.com" preload="./interceptor.js"></webview>
and then in inteceptor.js
window.NativeWebsocket = WebSocket;
window.WebSocket = function(url, protocols){
window.interceptedWebsockets = [];
var ws = new NativeWebsocket(url, protocols);
interceptedWebsockets.push(ws);
return ws;
}
Then, inside your WebView context you can an access array of instantiated websocket objects using window.interceptedWebsockets

How to trigger Couchbase to syn with iOS when document changed from backend

I cloned a project from https://github.com/couchbaselabs/Grocery-Sync-iOS. And I have created the grocery-sync bucket in Couchbase admin. I have added several documents using iPhone simulator, and it worked becuase the documents did appear in the database. But when I try to change the title of a document, the iOS is not updating the new title of the changed document. And I tried to using the provided node.js module to change this document. Below is the node.js code for change specific document's text.
var couchbase = require("couchbase");
// Connect to Couchbase Server
var cluster = new couchbase.Cluster('127.0.0.1');
var bucket = cluster.openBucket('grocery-sync', function(err) {
if (err) {
// Failed to make a connection to the Couchbase cluster.
throw err;
}
bucket.get('-v-bZh-EUrlr0_ev-rqC8br', function(err, result) {
if (err) {
// Failed to retrieve key
throw err;
}
console.log(doc);
var doc = result.value;
// Store a document
doc.text = "Random beer from Norway";
bucket.replace('-v-bZh-EUrlr0_ev-rqC8br', doc, function(err, result) {
if (err) {
// Failed to replace key
throw err;
}
console.log(result);
// Success!
process.exit(0);
});
});
});
The node.js code did work, because I can see the changes in the database. But the iOS app is not updating any changes. the kCBLReplicationChangeNotification never gets called. I tried to rerun the simulator, the value is still the old value. So how to update changes when you change the exist document and make the iOS update it's new value? It seems the sync feature is only working one-way.
If you want changes to be replicated to Couchbase Lite instances, you have to go through the Sync Gateway. You can't directly write in the associated bucket.
So the recommended way to do this is to use Sync Gateway's REST API.
To replicate the data from the client side to the Couchbase backend server that you have, the Sync Gateway is the component that you need to have set up.
Take a look at the Sync Gateway REST API as it would allow for HTTP access to your remote Couchbase Server database.
Can also take a look at the Couchbase blog on the Sync Gateway for more in depth discussions too.

SignalR, Messages are to be pushed by the Server without any event triggered by the client

There are running examples of SignalR, but in those, i have seen that the process is started by the client i.e. every piece of code contains following similar lines
$.connection.hub.start().done(function () {
$('#mybutton').click(function () {
notifier.server.doLongOperation();
});
});
The process on server starts on $('#mybutton').click and then responds.
Is my understanding correct? If yes then is it possible to start the process by Server? I mean Server will push messages to all clients without any triggering from the client side.
This didn't work
var context = GlobalHost.ConnectionManager.GetHubContext<Broadcast>();
context.Clients.All.Send(message);
My bad, method name on client side was incorrect. Problem solved
Yes it is possible to send server initiated "messages" from the server to clients. Note that you have to call a method on the client. Note that it's a RPC/Remoting type of communication.
On the server you'd have a code like this:
Clients.All.Say("Hello World!");
where the client needs to define a function:
myHub.client.say = function (message) {
console.log(message);
});
see the SignalR documentation

Resources