I need a javascript library to connect to my web-socket server which is implemented using python twisted. I tried Native javascript web-socket client but it doesn’t have the option to pass custom headers as per this link. My web-socket server does authentication by taking auth_token from handshake header as in Oauth2 standard. Is there any javascript library available for web-socket clients which allows to pass custom header while connecting ?
I'm sorry to be the bearer of bad news... but - as mentioned both in the question you are referencing and as you can learn from the standard Websocket API (this isn't an external library, it's what comes with the browser)... you cannot set custom headers for websocket connections.
The WebSocket(url, protocols) constructor takes one or two arguments. The first argument, url, specifies the URL to which to connect. The second, protocols, if present, is either a string or an array of strings. ... Each string in the array is a subprotocol name. The connection will only be established if the server reports that it has selected one of these subprotocols. ...
But, all is not lost.
Since this is YOUR websocket server, you have options:
I'm pretty sure that OAuth2 uses the token as a parameter for a GET or POST request and NOT as a custom header. This means that (maybe) you can pass the token as part of the connection string, i.e.:
websocket = new WebSocket('wss://my.server.com/?access_token=secret_acess_token');
Passing the session token like so might not be ideal and could pose a security risk... so I would go with the second options here:
New websocket connections (unless my browsers are special) are initiated with the same cookies that the main connection was established with - this means that all the cookies and session data from the Http layer is accessible to the websocket layer....
So, It's possible to set a unique cookie - or, even better (assuming your http and websocket share the same codebase and work well together), set an authentication token within a server-side session storage - and use that data to authenticate a connection or to refuse it.
Since I'm no Python expert, here's a quick demo using Ruby's Plezi framework (I'm the author):
require 'plezi'
class DemoCtrl
# this is the Http index page response
def index
response.write "#{cookies[:notice]}\n\n" if cookies[:notice] && (cookies[:notice] = nil).nil?
#returning a string automatically appends it to the response.
"We have cookies where we can place data:\n#{request.cookies.to_s}\n"
end
# the login page
def login
cookies[:my_token] = "a secret token"
cookies[:notice] = "logged in"
redirect_to :index
end
# the logout page
def logout
cookies[:my_token] = nil
cookies[:notice] = "logged out"
redirect_to :index
end
# a Plezi callback, called before a websocket connection is accepted.
# it's great place for authentication.
def pre_connect
puts "Websocket connections gave us cookies where we can place data:\n#{request.cookies.to_s}\n"
return false unless cookies.to_s[:my_token] == "a secret token"
# returning true allows the connection to be established
true
end
def on_message data
puts "echoing #{data}"
response << "echo: #{data}"
end
end
# setup the route to our demo
Plezi.route '/', DemoCtrl
# Plezi will start once the script is finished.
# if you are running this in irb, use:
exit
visit: http://loaclhost:3000/
to try and initiate a websocket, open up the web inspector and run the following script in the console:
ws = new WebSocket("ws://localhost:3000/"); ws.onopen = function(e) { console.log("open"); }; ws.onmessage = function(e) { console.log(e.data);};
ws.send("Go Bears");
This should FAIL, because we didn't authenticate yet...
visit http://loaclhost:3000/login and try again.
Now it should work.
Try http://loaclhost:3000/logout if you feel like it.
Related
So, basically, I have this code :
let socket = new WebSocket('ws://localhost:8080/server.php');
socket.onopen = () => {
console.log('connection established !');
}
I use this code to establish a connection for a Real-Time Quiz. But after going to the Sources page of my inspector, I can see the whole javascript code in my browser, including ws://localhost:8080/server.php. Is it dangerous to show it (unintentionally) ? If someones creates a script and puts in it the same url (not localhost, it's just an example), can he receive/send data to the server ?
yes,it is dangerous. u can:
verify the client http request header for example 'Origin'. make sure the client website is the right client website.
use a TSL websocket service, visit to the server over SSL. So the protocol is changing to: wss://
give the client a request token, put this token in header or in post data, the server verify this token.
check the request times of a client in limited time. make sure a specific client won't request too frequently
Ive been trying to implement the WebSocket protocol from scratch in nodejs, doing so i have a question thats since been unawnsered. What exactly are subprotocols in regards to websockets? The second parameter of the WebSocket constructor is where you specify "subprotocols" -
let socket = new WebSocket("ws://localhost:3000",["http",...]);
Can anybody give me a clear awnser to what purpose they have?
Websockets just define a mechanism to exchange arbitrary messages. What those messages mean, what kind of messages a client can expect at any particular point in time or what messages they are allowed to send is entirely up to the implementing application. So you need an agreement between the server and client about these things. You might say… you need a protocol specification. The subprotocol parameter simply lets clients formally exchange this information. You can just make up any name for any protocol you want. The server can simply check that the client appears to adhere to that protocol during the handshake. You can also use it to request different kinds of protocols from the server, or use it for versioning (e.g. when you introduce my-protocol-v2, but still need to support clients only understanding my-protocol-v1).
Explained on MDN here
Think of a subprotocol as a custom XML schema or doctype declaration.
You're still using XML and its syntax, but you're additionally
restricted by a structure you agreed on. WebSocket subprotocols are
just like that. They do not introduce anything fancy, they just
establish structure. Like a doctype or schema, both parties must agree
on the subprotocol; unlike a doctype or schema, the subprotocol is
implemented on the server and cannot be externally referred to by the
client.
Subprotocols are explained in sections 1.9, 4.2, 11.3.4, and 11.5 of the spec.
A client has to ask for a specific subprotocol. To do so, it will send
something like this as part of the original handshake:
http GET /chat HTTP/1.1 ... Sec-WebSocket-Protocol: soap, wamp
or, equivalently:
... Sec-WebSocket-Protocol: soap Sec-WebSocket-Protocol: wamp
Now the server must pick one of the protocols that the client
suggested and it supports. If there is more than one, send the first
one the client sent. Imagine our server can use both soap and wamp.
Then, in the response handshake, it sends:
Sec-WebSocket-Protocol: soap
The server can't send more than one Sec-Websocket-Protocol header. If
the server doesn't want to use any subprotocol, it shouldn't send any
Sec-WebSocket-Protocol header. Sending a blank header is incorrect.
The client may close the connection if it doesn't get the subprotocol
it wants.
If you want your server to obey certain subprotocols, then naturally
you'll need extra code on the server. Let's imagine we're using a
subprotocol json. In this subprotocol, all data is passed as JSON. If
the client solicits this protocol and the server wants to use it, the
server needs to have a JSON parser. Practically speaking, this will be
part of a library, but the server needs to pass the data around.
Some sample code, copy from https://hpbn.co/websocket/#subprotocol-negotiation, to make it clear.
The client can advertise which protocols it supports to the server as
part of its initial connection handshake:
var ws = new WebSocket('wss://example.com/socket',
['appProtocol', 'appProtocol-v2']);
ws.onopen = function () {
if (ws.protocol == 'appProtocol-v2') {
...
} else {
...
}
}
I am making a chat program.
I am using an Nginx server and NodeJS.
I have setup a websocket via ssl and that works fine.
I have decided to use cookies for authentication.
There are two functions which are crucial:
mconnection.prototype.make_server_https=function(){
console.log('Make server https');
var cthis=this;
var server_https=modules.https.createServer({
key: this.ssl_key,
cert:this.ssl_cert,
ca:this.ssl_ca
},(request,response)=>{
console.log('### CreateServer ###');
console.log('CreateServer, Request:');
console.log(request);
console.log('CreateServer, Response:');
console.log(response);
console.log('######');
and
mconnection.prototype.make_server_websocket=function(){
var server_websocket=new modules.ws.Server({server:this.server_https});
var cookie = require("cookie");
var cthis=this;
//whenever a new client connects with the server.
server_websocket.on('connection', function(client_socket, request){
console.log('### On Connection ###');
console.log('OnConnection, Client Socket:');
console.log(client_socket);
console.log('OnConnection, Request:');
console.log(request);
console.log('######');
If I do state the port number in the client url,function make_server_https gets run and inside there i can access the cookie and set it via the response object.
but in the original url,function make_server_websocket gets run, and there i have access to the client_socket on the server. But there it seems i dont have access to the cookies.
I need to client_websocket to start the connection with this given client. And I need to tie it somehow with the cookies login information.
But i never have both at the same time so i dont get how i could connect them to make the login happen.
I am probably misunderstanding something, any help in the right direction would really be appreciated.
you have to serve you index page from node server using GET then when the request reaches backend you will have response object which can then be used to SET-COOKIE if not set from backend.
And after GET request is complete COOKIE will be added in browser, when next request is made for websocket connection COOKIE will be added to the request in REQUEST HEADERS by the browser which will be available in backend through request object.
And if you decide to use it in login system then you can SET-COOKIE on successfull login.
i got it. its an event called on headers, not on connection. and there i can just push onto the headers.
I am inheriting a backend Express API and a front end React app.
Currently I am using cookie-parser in my POST /login API like so:
res.cookie('something', 'abc123', {
maxAge: COOKIE_MAX_AGE
});
on my front end app, there is a function for checking if an auth token exists:
export function isAuthCookiePresent() {
console.log('ALL COOKIES:', cookies.get());
return (
cookies.get(AUTH_COOKIE_NAME) && cookies.get(AUTH_COOKIE_NAME) !== null
);
}
And as expected I see { something: 'abc123' } in my console logs.
However, when I try logging in this using autodeployed branches in Vercel (https://vercel.com/), the cookie is missing.
I was under the impression that cookies were supposed to be set on the front end? But in the code the cookie is being set on the backend. And I don't see anything in the code that passes it to the front end. I thought I would find something on the front end like that would have a "upon successful login, execute cookies.set("x-auth-token", res.body.token)"
It's odd to me that it works locally at all. Would someone mind explaining how this works? I thought cookies were stored in the browser on the client side. But if that was true, why does cookie-parser even exist in express and why is it being used server side?
However, when I try logging in this using autodeployed branches in Vercel (https://vercel.com/), the cookie is missing.
This is because it appears you are setting the cookie server side, and as far as I know vercel only handles client side and will not let you use express.
I was under the impression that cookies were supposed to be set on the front end? But in the code the cookie is being set on the backend. And I don't see anything in the code that passes it to the front end. I thought I would find something on the front end like that would have a "upon successful login, execute cookies.set("x-auth-token", res.body.token)"
Cookies can actually be set through headers (Set-Cookie: <cookie-name>=<cookie-value>), which is what express's res.cookie does. MDN's article on the Set-Cookie header says:
The Set-Cookie HTTP response header is used to send a cookie from the server to the user agent, so the user agent can send it back to the server later. To send multiple cookies, multiple Set-Cookie headers should be sent in the same response.
It's odd to me that it works locally at all. Would someone mind explaining how this works? I thought cookies were stored in the browser on the client side. But if that was true, why does cookie-parser even exist in express and why is it being used server side?
Cookies are, in fact, stored client-side. They are accessible through client side javascript and backend with the cookie header. The cookie-parser module is needed to parse the name=value syntax sent by the Cookie header (Cookie - HTTP | MDN). It's being used server-side becuase validating cookies in the frontend can let any user give a false "true" value to your if statement that you use to validate cookies.
As an answer to the question: I recommend backend because JWTs have to be signed, and setting and signing them client-side will let anyone sign an arbitrary payload.
Background
I'm using these technologies to secure a WebApi application:
ThinkTecture.IdentityServer3
OWIN (Azure)
Single page app - javascript client
Refer to the Simple OAuth2 Walkthrough sample (github)
In the sample above the .NET console app client requests a token from IdentityServer and uses it to access the WebApi application. This works fine in the sample.
I want to change the .NET console app Client to a javascript single page app Client. I have tried adding a proxy login controller that does the request to IdentityServer on behalf of the javsacript Client and returns the token back to the client in a cookie.
Code
[HttpPost]
[Route("login")]
public HttpResponseMessage Login(LoginRequest request) // Proxy to IdentityServer3
{
var tokenClient = new TokenClient(
"https://localhost:44333/connect/token",
"javascript client",
"client secret");
var tokenResponse = _tokenClient.RequestResourceOwnerPasswordAsync(request.username, request.password, "api1").Result;
var cookie = new CookieHeaderValue("access_token", tokenResponse.AccessToken);
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = "localhost";
cookie.Path = "/";
var response = new HttpResponseMessage();
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return response;
}
I get the access token back successfully in the javascript client, however the API doesn't recognise it.
Question
How should I pass the a token generated by IdentityServer to the javascript application, and how do I use it to access the WebApi?
Let's say your token end point is "https://localhost:44333/connect/token" (as you have mentioned). Hitting that endpoint with a POST request with a body like the following will return a token:
grant_type=password&client_id=youtclientid&client_secret=yourclientsecret&username=yourUserName&password=YourPassword&scope=list_of_requested_scopes
You use a JS variable to store your token, and then in order to use that token to access protected APIs, you have to send it as part of the header in the request, similar to the following: In your request's header, you will have:
Authorization: Bearer eyJ0eXAiOiJKV...
where "eyJ0eXAiOiJKV..." is the token you received in the first step.
Using oidc-client.js or creating something similar on those lines as the size of this js library is large.
Check this link out.
https://github.com/IdentityModel/oidc-client-js/tree/master/sample
and the video
https://vimeo.com/131636653
We are using the IdentityServer infrastructure to get to initial login and then the token(s) id/access for all further communications.