We're developing a web application that will fetch some data over a websocket. We serve it from CloudFront over SSL, the back-end is on AWS. We authenticate the users with Cognito for signing in with the application, and we would like to use the Cognito token to set up authentication for the websocket as well. Also, we want the token to be part of the first connection attempt, so that we don't open a connection to anyone, and then wait for some magic message containing auth, that could probably lead to DDoS attacks.
The first thought was to add the token to an authorization header, but the websocket standard doesn't support adding headers.
Second, we thought about adding an X-Authorization cookie with the token, that way the cookie would be sent as part of the request to open a socket. This failed (probably) because in development, the cookie domain is set to "localhost", and will not be sent to the websocket url of aa.bb.com.
Our next move is to append the token to the URL as a query parameter, and it seems to be working.
Now, my question is, is this safe enough, or should we consider something like a two-step approach, first get a sign-in token from another endpoint, then use that one as a query parameter when opening the websocket ?
As long as your traffic is over SSL whatever solution works has the same security as the SSL no matter what method is used i.e. GET, POST...
Related
We have a Google http(S) LB in front of a Google Compute VM, and we are routing a subdomain to the backend which exposes only a wss endpoint. I couldn't find any example for javascript code how to use Authentication with Google IAP and OIDC Tokens.
Does Google IAP support query parameters for the authentication ?
I found this entry:
Bearer authentication for websocket
Thanks for any advice
There is no method in the JavaScript WebSockets API to customize WebSocket headers from JavaScript, you’re limited to the “implicit” auth (i.e. Basic or cookies) that are sent from the browser. Further, it’s common to have the server that handles WebSockets be completely separate from the one handling “normal” HTTP requests. This can make shared authorization headers difficult or impossible. One way to attain this is using a “ticket”-based authentication system.
When the client-side code decides to open a WebSocket, it contacts
the HTTP server to obtain an authorization “ticket”.
The server generates the ticket. It typically contains some sort of
user/account ID, the IP of the client requesting the ticket, a
timestamp, and any other sort of internal record keeping you might
need.
The server stores this ticket (i.e. in a database or cache), and
returns it to the client.
The client opens the WebSocket connection, and sends along this
“ticket” as part of an initial handshake.
The server can then compare this ticket, check source IPs, verify
that the ticket hasn’t been re-used and hasn’t expired, and do any
other sort of permission checking. If all goes well, the WebSocket
connection is now verified.
Refer to the link for websocket security and related stack posts HTTP headers in websockets client API and Websocket authentication.
I'm working on a javascript application using a REST API.
Authentication is made with JWT tokens stored in cookies
Right now, this scenario is implemented:
user sign in with credentials. Client calls POST /token to authenticate
server responds with a HTTP-only cookie containing the token.
once authenticated, client makes another request to get all user data (GET /me)
I would like to make this process as fast as possible and reduce the number of server requests as much as possible. I thought about combining /token and /me calls and get token and user data in the same request.
That could be done in different ways :
in the claims of the token, but client won't be able to use it as it's in a HTTP-only cookie.
in another, non HTTP-only, cookie but that will be sent uselessly with every future request, so I don't like this solution
in the response body when server sends the cookie after authentication but I have the feeling that it goes against the REST principles as we send user data from an authentication endpoint.
Is there a way to make this process more simple while respecting standard processes and REST principles?
I personnally use Cookies as a storage, but not in HTTPonly mode. In this case, the simplest is to encode the information you need inside the token.
Are you forced to use HTTP-only cookies? Is it an option for you to change it (in fact, for that you must master the authorization server)?
Another thing : using GET to pass credentials isn't safe as you probably pass your credentials in the URL, which can be fetched from server logs. Prefer POST (and HTTPS of course).
Few pointers about JWT and their storage stategies:
Tokens vs Cookies
Where to store the tokens?
Threats of token theft
I have two web-servers responding on two different ports. I have the main web server that serves a website with a lot of javascript behind HTTP digest authentication, the secondary webserver only executes CGIs that can be directly accessed, always by using HTTP digest, or that can accessed with CORS by the ajax requests related to the main webserver. Both servers share the same users credentials.
My problem is that I don't want the browser to prompt for credentials when the javascript is making requests to the secondary webserver.
I came out with the idea that I could add some special header in the ajax request to the secondary web-server, and if this header is present I can ignore the HTTP authentication. Since the servers share the users credentials, if the user is able to log in into the main web-server, he'll be able to login into the second one as well.
Using a fixed header is of course useless. So the question is: is there a mechanism in CORS to tell the secondary webserver that the user is already authenticated in the first one? Something like a safe way to exchange tokens in cookies?
If it is only the ports that are different cookies will be shared across these origins. So if you know for sure that a cookie is set once the user accesses origin 1, they will be included in requests to origin 2, as long as (assuming you're using XMLHttpRequest) withCredentials is set to true.
Now of course those cookies should contain some authentication data that you verify before you let them bypass HTTP authentication.
And hopefully you're using HTTPS so that the credentials are safe from network attackers.
I'm not sure this has anything to do with CORS. What you need is a single-sign-on solution for the two different servers. You could implement a full-blown OAuth solution, or write a simple one yourself.
For a simple token-based authentication, you'd do the following:
When the user logs into your website, send down a expiring token (over SSL) that grants the user access to the web service.
Take the token and do a GET request to a non-authenticated endpoint in the web service. If the token is valid and non-expired, send an authentication token back to the browser.
As long as your web service implements HTTP authentication properly, the cookie will be set and the service calls won't prompt for credentials.
I am trying to implement OAuth with my javascript client application.
I already have the server up and running.
My workflow is as following:
Open app
Check if token is present
Validate the token
If not present or not valid go to oauth server for the token
Get back to app and repeat 2 and 3
I everything is ok show my app
I am not sure how to implement point 2. I understand that I need to make another call to the server for this but where is the validation endpoint?
I only have /authorize, /user and /logout
Also how should the request be formed? Do I attach token as a GET parameter and thats all?
What if somebody intercepts valid the token?
Depends on your application, but since you have a javascript web application, that most probably can't keep it's credentials secret, you'll need to implement the Implicit Grant as stated in the OAuth 2.0 spec. If however your application CAN keep it's credentials secret, you should implement the Client Credentials Grant (server side application) because it's more secure.
I am not sure how to implement point 2
You should store your access token somewhere in your web application, for instance in localStorage.
The first time a user will access your web application, the user will have NO access token and NO session with your authorization server. Your web application could see if you have an access token by doing something like:
if (!localStorage.getItem('accessToken') {
window.location.replace('https://your-authorization-server/authorize?response_type=token&client_id=<CLIENT_ID>&redirect_uri=<CALLBACK_URL>&scope=<SCOPE>');
}
If there's no access token, you'll need to redirect to your authorization server so that the user can log in with the authorization server. After the user has logged in, the authorization server will redirect the user back to your web application (after the user has granted permission to whatever resource your web application likes to access on behalf of said user) with a valid access token. This access token must be exposed as a hash fragment named access_token, i.e:
https://web-app.com#access_token=123456789X
Now your web application can extract the access token and store it somewhere.
I understand that I need to make another call to the server for this
but where is the validation endpoint? I only have /authorize, /user
and /logout
Your authorization server requires a token validation endpoint. This endpoint will be used by your web application to validate said token (the one you stored somewhere, for instance localStorage). When this endpoint is called with an access token and it turns out the token is valid, the user can continue, otherwise the user will be redirected to the authorization server. If the user already has a session with the authorization server, the user will be redirected back immediately with a new access token, otherwise the user needs to authenticate (login) first.
Also how should the request be formed? Do I attach token as a GET
parameter and thats all?
Some send a GET request with the access token as a url query parameter, others send a POST request with the access token as the payload of the request body.
What if somebody intercepts the valid token?
Always use https and your access token should be valid for a limited amount of time.
Something to keep in mind is that because your application can't keep it's credentials secret, you need to Implement the Implicit Grant, this means you immediately receive the access token when the authorization server authorized the web application based on Client Id and domain. As opposed to Client Credentials Grant where you first receive an Authorization Code that needs to be exchanged for an access token. When using Implicit Grant you can NOT use Refresh Tokens. This means the user NEEDS to go through the entire flow with the authorization server to obtain a new access token. But this isn't really a big deal, because the user will already be logged in with the authorization server, resulting in an immediate redirect, so when implemented correctly in the web application, the user won't notice.
This is just covering it in broad strokes, it really helped me (and I advise you) to read the OAuth 2.0 spec. Hope this helps you out a bit, OAuth is a complex subject!
The Problem:
Serving a secure API to a client side app using only a local authentication strategy. The red arrows are part of the knowledge gap.
Context:
That is --- client.example.com is making a POST to api.example.com/login where on success client.example.com can gain access to a GET service like api.example.com/secret.
An idea!
Implimentation of OAuth 2.0 with hybrid grant type sitting in front of API.
Why hybrid?
It wouldn't be an Implicit Grant Flow aka Client-Side Web Applications Flow because there is no redirection to API server too grant access token. (i.e.) "Is it ok for so-and-so to access your data?"
It wouldn't be a Resource Owner Password Flow because a Client ID and Client Secret are passed along with the request so it's assumed the client app is server-side.
OK... so what about a little bit of both?
What if we used a CRSF token on page load of client-side app, and POST it with user credentials too OAuth 2.0 authentication endpoint to exchange for access token? You would authenticate each subsequent request with the access token and CRSF token after a successful login.
A good Node.js OAuth 2.0 library I found:
https://github.com/ammmir/node-oauth2-provider
Help Me!
I can not find a working example of an authentication measure that solves this problem! Point me in the right direction?
Ultimately, the goal here is too authenticate a client side app to a REST api using CORS with a local strategy --- i.e. username & password --- even if the convention above isn't possible.
To Accommodate Bounty:
This is a client side app, so let's stay trendy.
I'm looking for a working example using the Node.js OAuth 2.0 seed above for the API/Auth server and a front end framework like Angular.js or Backbone.js to make requests.
The example should match the context described above.
I'm working on an app with a pretty similar architecture though the services are .NET Web API rather than Node and we're using DotNetOpenAuth for the OAuth provider. Rather than the hybrid approach you're suggesting we're doing the following:
x.com serves up a login page
login page POSTs back credentials to x.com
server side logic at x.com combines client_id and client_secret with the credentials to submit a token request (resource owner password credentials grant that you've
mentioned above) receiving back both a temporary access token and a
refresh token
the refresh token is encrypted into a cookie issued by x.com
both the cookie (with encrypted refresh token) and the temporary access token are then sent to the browser
the client app (angular in my case) can now use the access token to hit api.x.com for services (It appears you're well aware of the limitations of CORS... we hacked a version of angular's $resource to facilitate this but it wasn't pretty since we wanted to use all HTTP verbs and support IE9)
when the access token expires, the client side app can request a new access token from x.com
server-side, x.com decrypts the cookie to get at the refresh token and issues another oauth call for a new access token
This is fairly high-level but hopefully gives you a sense for how to tackle your situation. In my case, and it appears in yours, we didn't want to use session state or a database to store the refresh token but obviously exposing that to the browser introduces security concerns so the encryption of the refresh token is important (among other security considerations) and the use of the cookie eliminates the need for session state or other persistent storage on x.com.
Not an answer running for the prize. Just my 2 cents :)
On my web server,
I do my authentication through a rest call with login/password with basic authentication over https. This call delivers a key to the client (a one page web app).
Then every subsequent REST call is signed with the key. The server checks that the signature is correct and everything still happen in https.
This mechanism is quite used I believe.
I don't see the issue with cross domain. I have a single source anf if I need something from another source, I'd use JSONP.
I use nginx as an https->http forwarder.
Not sure how it competes with an OAuth2 solution.
I've built this example using Node and PassportJS to show how to authenticate the users with Facebook or Local Strategy. Both sides are on different domains as you described and it requires CORS enabled.
GitHub: https://github.com/pablodenadai/Corsnection
Live demo: http://corsnection-client.herokuapp.com/
I can't promise that I have time to write working example but I can show you 2 paths :)
The biggest deal is CORS. After you solve that problem it is easy to use $http service. So, first and probably easiest may be to configure reverse proxy in x.com webserver which points to api.x.com. I wrote article here
Second approach is better, and created for exactly this purpose, to authorise specific domain to use your resource. It involves a bit of coding in api.x.com so you don't have to change anything in new web applications served in other domains. You simply need to authorise CORS requests in api.x.com service.
Create table in database where you can manage list of authorised domains
Add in that table record "x.com"
in api.x.com add request filter/interceptor what ever tech term you use for method which should be invoked after request is handled and add in response Access-Control-Allow-Origin: x.com if request comes from x.com (in other words check in request header refer value match to any value in table above and put that value in Access-Control-Allow-Origin response header).
That is all :) After this if you know how to use $http or jQuey.ajax you will be able to POST/PUT/DELETE/... any request to api.x.com from any authorised domain in just few minutes.
I very similar idea using vinilla js web app and cross domain authentication to GAE backend or OpenID connect.
The web app is run on CDN. When click login link, it goes to respective login server and redirect back to the web app (with XSRF security token and HTTPS only cookie). Login server accept cross domain request with credentials. XSRF token has to be set (in header) with every request. cookie is set by the browser. Since it is HTTP only cookie, JS cannot read it. The technique is very secure.
Once login, you can get secure assess from login server.
For detail description, you can find here and open source repo here.