So I completely understand how to use getIceServers via your demo, but what's the best practice for implementing on the server side / compiled client-side?
"This token should only be implemented in a secure environment, such as a server-side application or a compiled client-side application."
Do the list of IceServers expire at some point? Should I request new IceServers on each page request or do I cache the list for X amount of time?
The Ice Server credentials expire after about 10 seconds. Because you want to keep your XirSys secret token secure (so no one can hack your account's connection allotment), you'll want to make a backend/server side curl request for the ice servers. It's assumed that your app uses its own authentication. I.e., it'll reject any non-authenticated requests to https://yourdomain.com/ajax/get-ice-servers.
So ... whenever you need to create a PeerConnection object, get a list of Ice servers through an Ajax call ...
var pc = RTCPeerConnection(
getIceServers(),
{optional: []}
);
where ...
function getIceServers() {
var result = jQuery.ajax({
async: false,
url: "https://" + yourDomain + ".com/ajax/get-ice-servers"
}).responseText;
return JSON.parse(result);
}
Note you'll want a synchronous ajax request so the getIceServers() function returns the result before RTCPeerConnection is instantiated.
Also note that if you start a webRTC connection automatically on page load, then you could probably just use the iceServers result from the server curl request.
Related
I know this question has been asked a bunch of times, but I tried most of the answers and still can't get it to work.
I have a Golang API with net/http package and a JS frontend. I have a function
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
Path: "/",
})
return val
}
This function is called when the user logs in, and I expect it to be sent to all the other endpoints. This works as expected with Postman. However, when it comes to the browser, I can't seem to get it to remember the cookie or even send it to other endpoints.
An example of JS using an endpoint
async function getDataWithQuery(query, schema){
let raw = `{"query":"${query}", "schema":"${schema}"}`;
let requestOptions = {
method: 'POST',
body: raw,
redirect: 'follow',
};
try{
let dataJson = await fetch("http://localhost:8080/query/", requestOptions)
data = await dataJson.json();
}catch(error){
console.log(error);
}
return data;
}
I tried answers like setting SameSite attribute in Golang, or using credential: "include" in JS with no luck.
Thanks to the discussion in the comments, I found some hints about the problem.
Saving cookies (both API and frontend on the same host)
I used document.cookie to save the cookie. I set the options by hand since calling res.cookie on the response of the API fetch only returned the value. An example is document.cookie = `goCookie=${res.cookie}; path=/; domain=localhost;.
Sending cookies
This has been answered before in previous questions and answered again in the comments. The problem was that I used credential:'include' instead of the correct credentials:'include' (plural).
CORS and cookies
In case the API and the frontend are not on the same host you will have to modify both the API and the frontend.
frontend
The cookie has to have the domain of the API since it's the API that requires it, not the frontend. So, for security reasons, you can't set a cookie for a domain (API) from another domain (frontend). A solution would be redirect the user to an API endpoint that returns Set-Cookie header in the response header. This solution signals the browser to register that cookie with the domain attached to it (the API's domain, since the API sent it).
Also, you still need to include credentials:'include' in the frontend.
API
You will need to set a few headers. The ones I set are
w.Header().Set("Access-Control-Allow-Origin", frontendOrigin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, withCredentials")
w.Header().Set("Access-Control-Allow-Methods", method) // use the endpoint's method: POST, GET, OPTIONS
You need to expose the endpoint where the frontend will redirect the user and set the cookie in the response. Instead of setting the domain of the API by hand, you can omit it, the browser will fill it with the domain automatically.
To handle the CORS and let JS send the cookie successfully, you will have to set the SameSite=None and Secure attributes in the cookie and serve the API over https (I used ngrok to make it simple).
Like so
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
SameSite: http.SameSiteNoneMode,
Secure: true,
Path: "/",
})
// rest of the code
}
I recommend you also read the difference between using localStorage and document.cookie, it was one of the problems I had.
Hope this helps.
I have a Google Assistant Action that uses Cloud Functions and in that Action I need to send GET/POST requests to my private server.
We're using Thingworx platform for development and it is hosted on a private cloud behind a firewall.
I've also tried Postman to send these requests and it worked perfectly, but when I use the same on my Google Assistant Action, it just doesn't work and all I get is an empty response.
My request code is below:
function callMeAPI(agent) {
var request = require("request");
var options = { method: 'GET',
url: 'https://{IP:Port}/Thingworx/Things/{Path}',
qs: { appKey: 'AppKey Used Here' },
headers:
{ 'cache-control': 'no-cache',
Accept: 'application/json' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
agent.add("body");
});
So, are there any ports and IPs/URLs we need to add to our firewall rules to add for requests originating from Cloud Functions to go through the firewall onto our Thingworx Application?
You should not make any assumptions about the IP address of outbound traffic originating from Cloud Functions. The originating IP could change over time, depending on Google infrastructure, and also based on which region your function was deployed to.
If you need to ensure that access to your network originated from your function, you should implement some sort of authentication in the request so that your own application can validate the request. Typically, there is some shared secret that only both sides know that indicate they are aware of each other.
I'm trying to query a Firebase database from a Service Worker using the Fetch API. However it doesn't work as expected as I can't get authenticated correctly.
Basically what I'm trying to do is from origin https://myproject.firebaseapp.com inside a Service Worker I do a call like this :
var fetchOptions = {};
fetchOptions.credentials = 'include';
var url = options.messageUrl;
var request = new Request('https://myproject.firebaseio.com/user/foobar.json', fetchOptions);
messagePromise = fetch(request).then(function(response) {
return response.json();
});
I'm getting this error :
Fetch API cannot load https://myproject.firebaseio.com/user/foobar.json. Response to preflight request doesn't pass access control check: Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'https://myproject.firebaseapp.com' is therefore not allowed access.
Any idea of a way to fix it? How one should do to query/update the Firebase database from a SW?
I've read https://jakearchibald.com/2014/using-serviceworker-today/ and one of the gotcha was exactly that problem, the fact that Fetch request do not send authentification.
Ideally it would be great to be able to use the Firebase JS API inside a SW but this doesn't seem to work as well.
Firebase doesn't store authentication info as a cookie or in anything that would be sent along in the credentials, so there's no need to send them in your fetch request. Instead, you'll need to pull the token from Firebase Auth:
firebase.auth().currentUser.getToken(true).then(function(token) {
// token is the value you'll need to remember for later
});
Once you've got the token, you should be able to add it as a query parameter to the REST request e.g. ?auth={THE_TOKEN}. This will allow you to make your authenticated request in the Service Worker.
How can check, on server side route, if user is logged?
I would add check on 'before', but Metor.user() don't work here.
thanks in advance.
p.s. I have found How to get Meteor.user() to return on the server side?, but not work on iron-router
I'm afraid that this is not possible. I guess that the problem comes from the fact that you're trying to connect to the server with two different protocols - both literally and in logically - so there is no obvious way to relate this two actions.
There is, however, a pretty simple solution that may suit your needs. You'll need to develop a simple system of privileges tokens, or secret keys, or whatever you call them. First, create a server method
var Secrets = new Meteor.Collection("secrets"); // only on server!!!
Meteor.methods({
getSecretKey: function () {
if (!this.userId)
// check if the user has privileges
throw Meteor.Error(403);
return Secrets.insert({_id: Random.id(), user: this.userId});
},
});
Then, you can now use it on the client to get the secretKey which attach to your AJAX request (or something), either within the HTTP header or in the URL itself. Fear not!
They will all be encrypted if you're using HTTPS.
On the server side you can now retrieve the secretKey from the incoming request and check if it is present in the Secrets collection. You'll know then if the user is granted certain privileges or not.
Also you may want to remove your secret keys from the collection after some time for safety reasons.
If what you're looking to do is to authenticate the Meteor.user making the request, I'm currently doing this within the context of IronRouter.route(). The request must be made with a valid user ID and auth token in the header. I call this function from within Router.route(), which then gives me access to this.user:
###
Verify the request is being made by an actively logged in user
#context: IronRouter.Router.route()
###
authenticate = ->
# Get the auth info from header
userId = this.request.headers['x-user-id']
loginToken = this.request.headers['x-auth-token']
# Get the user from the database
if userId and loginToken
user = Meteor.users.findOne {'_id': userId, 'services.resume.loginTokens.token': loginToken}
# Return an error if the login token does not match any belonging to the user
if not user
respond.call this, {success: false, message: "You must be logged in to do this."}, 401
# Attach the user to the context so they can be accessed at this.user within route
this.user = user
###
Respond to an HTTP request
#context: IronRouter.Router.route()
###
respond = (body, statusCode=200, headers={'Content-Type':'text/json'}) ->
this.response.writeHead statusCode, headers
this.response.write(JSON.stringify(body))
this.response.end()
This code was heavily inspired by RestStop and RestStop2. It's part of a meteor package for writing REST APIs in Meteor 0.9.0+ (built on top of Iron Router). You can check out the complete source code here:
https://github.com/krose72205/meteor-restivus
I'm trying to get a client/server program exchanging http messages over ssl. To start, I created client and server programs that successfully exchange http requests using DefaultHttpRequest. The code that sends the request looks something like this:
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "https://localhost:8443");
ChannelBuffer buf = ChannelBuffers.copiedBuffer(line, "UTF-8");
request.setContent(buf);
request.setHeader(HttpHeaders.Names.HOST, host);
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
request.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml");
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, Integer.toString(buf.capacity()));
ChannelFuture writeFuture = channel.write(request);
The client pipeline factory contains this:
pipeline.addLast("decoder", new HttpResponseDecoder());
pipeline.addLast("encoder", new HttpRequestEncoder());
// and then business logic.
...
The server pipeline factory contains this:
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
// and then business logic.
....
So far so good. Client sends, server receives and decodes the request. The messageReceived method on my handler is called with the correct data.
In order to enable the SSL, I've taken some code from the SecureChat example and added to both client and server pipeline factories:
For the server:
SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
pipeline.addLast("ssl", new SslHandler(engine));
// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
For the client:
SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
pipeline.addLast("ssl", new SslHandler(engine));
// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
Now when I send the request from the client, nothing seems to happen on the server. When I start up the applications, the server seems to connect (channelConnected is called), but when I send the message none of the data gets to the server (messageReceived is never called).
Is there something obviously wrong with what I am doing? Is this the way that https should work? Or is there a different method for sending http requests over ssl?
Thanks,
Weezn
You need to call SslHandler.handshake() on the client side. Check the example again its in there.
Oops, it seems like I copied and pasted too much from the SecureChat example.
Removing the DelimiterBasedFrameDecoder seems to fix the issue.