OAuth2 Authentication for Websockets: Pass Bearer Token via Subprotocols? - websocket

I am trying to figure out what the best way is to pass an oauth bearer token to a websocket endpoint.
This SO answer suggests to send the token in the URL,
however this approach has all the drawbacks of authenticating via the URL. Security implications discussed here
Thus i was wondering what would be the drawbacks to use the subprotocols to pass the token to the server ? i.e. instead of treating the requested subprotocols as a list of constants. Send at least one subprotocol that follows a syntax like for example: authorization-bearer-<token>
The token would end up in a request header.
The server while processing the subprotocols would be able to find and treat the token easily with a bit of custom code.
Since passing subprotocols should be supported by a lot of websocket implementations, this should work for a lot of clients.

This worked for me, I used this WebSocket client library.
You need to send OAUTH token via the Websocket Header, Below is the code, hope this is helpful.
ws = factory.createSocket("wss://yourcompleteendpointURL/");
ws.addHeader("Authorization", "Bearer <yourOAUTHtoken>");
ws.addHeader("Upgrade", "websocket");
ws.addHeader("Connection", "Upgrade");
ws.addHeader("Host", "<YourhostURLasabovegiveupto.com>");
ws.addHeader("Sec-WebSocket-Key", "<Somerandomkey>");
ws.addHeader("Sec-WebSocket-Version", "13");
ws.connect();

Related

Websockets - CSRF with Spring Boot and STOMP

How is CSRF over WebSockets expected to work?
I am sending a CSRF Token as STOMP header on the Connect but the org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor does not seem to find it. I tried to dig into the sources and I seem to understand that the CSRF Token is expected to be supplied with the initial websocket HTTP Handshake and is then stored inside some sort of session or repository where it is taken out. The job seems to be done by org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor.
But - my HTTP Handshake does not supply any CSRF token - Its the same as with any other Header like Authorization - I cant supply any custom header together with the initial Handshake! Sending it as _csrf Query Parameter doesn't help either. It's a GET request and doesn't require CSRF per default anyway.
How is CSRF supposed to work with Websocket STOMP over HTTP?
Inside CsrfTokenHandshakeInterceptor whike processing
There is already an issue open for spring-security which adresses it. There is also a workaround supplied in that issue https://github.com/spring-projects/spring-security/issues/12378
My code example with the workaround can be found here: https://github.com/OlliL/spring-boot-stomp
It could also serve as an example implementation for JWT + CSRF with STOMP Websockets maybe (I am no expert ;))

How to pass bearer token in a request apart from Authorization manager and Header manager

I need to pass the token generated in one request into another request.
In second request,
I cannot pass the Authorization as the header because the API is not designed in a way to pass the token as header, nor Authorization manager is working as I need to pass the body and in Authorization manager I am not able to locate where to pass the body.
Is there any other way apart from Authorization manager or Header manager?
HTTP Authorization Manager generates and sends the relevant Authorization header, the header value differs depending on the protocol which is being used for the authentication/authorization from basic access control to NTLM and Kerberos
HTTP Header Manager allows you to send arbitrary HTTP headers including the aforementioned Authorization one
Unfortunately we cannot suggest how exactly you can pass the token, you need to
check the API contract or documentation, some API implementations have special documentation endpoints
contact the people who "designed" the "API"
capture the request from the real browser using browser developer tools or if it's another application use a sniffer tool like Wireshark or Fiddler

How to secure web api with Identity Server 3

I'm building an MVC web app that uses the openID Connect hybrid flow to authenticate with Identity Server 3. The MVC web app contains jQuery scripts to get async JSON data from een ApiController. That ApiController is part of the same MVC web app.
I don't want that everyone is able to access the data from the API, so I want to secure the API as well. I added an [authorize] attribute to the ApiController. When requesting the API with a JQuery ajax request I get the following error message:
XMLHttpRequest cannot load
https://localhost:44371/identity/connect/authorize?....etc.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:13079' is therefore not allowed
access. The response had HTTP status code 405.
But, when I do a request to the API method directly in browser, I will be correct redirected to the Login page of Identity Server..
So, what's exactly the problem here? I read something about that requesting the /authorize endpoint is not allowed via 'back-channel', but I don't understand what's the difference between 'front-channel' and 'back-channel'. Is it possible that I mixed up the wrong OAuth flows? Is the Hybrid flow not the correct one maybe?
I also find out that the API is often a seperate app, but is it always neccessary / best-practice to build a seperate API app that for example requires a bearer token?
Please point me in the right direction about this.
The authorize method on your identity server does not allow ajax calls. Even specifying CORS headers is not going to help you in this particular case. Perhaps you could return a forbidden response instead of a redirect and manually redirect the client to the desired location via window.location
You need to allow your IdentityServer to be accessed from other domains, this is done by allowing "Cross Origin Resource Sharing" or CORS for short. In IdentityServer the simplest way to allow this is in your Client configuration for your Javascript Client, see this from the IdentityServer docs on CORS:
One approach to configuing CORS is to use the AllowedCorsOrigins collection on the client configuration. Simply add the origin of the client to the collection and the default configuration in IdentityServer will consult these values to allow cross-origin calls from the origins.
The error you're seeing is the browser telling you that when it asked IdentityServer if it allows requests from your Javscript client, it returned a response basically saying no, because the origin (http://localhost:13079) was not specified in the "Access-Control-Allow-Origin" response header. In fact that header wasn't in the response at all meaning CORS is not enabled.
If you follow the quickstart for adding a JavaScript client from the docs here all the necessary code is detailed there that you need for the Client config and to setup IdentityServer to allow CORS.

EWS Basic/NTLM Authentication

I'm currently trying to send a SOAP request to an Exchange server, but I'm getting a 401 Unauthorized. I repeated the request using Postman, and it looks like it's trying to do NTLM authentication. However, I want to authenticate using Basic authentication by using just a username/password. Is there any way to specify this in the message headers or something? Is this something that's configured on the server end?
If it's not possible, how do I go about implementing NTLM authentication? I'm very new to this, so any help/direction will be much appreciated.
Thanks!

Providing auth header with SockJS

I have a Spring MVC server that provides a bunch of REST endpoints as well as a websocket endpoint. Everything except the login endpoint requires authentication. I'm using JWT to authenticate requests coming from the client.
When the user logs in I'm returning an X-AUTH-TOKEN header, containing the JWT token. This token is then passed in the same header on every request to the server. This all works fine for the REST endpoints, but I can't figure out how to do this on the websocket.
I'm using SockJS, and when I open the connection:
var socket = new SockJS('/socket/updates', null, {});
This causes a GET request to /socket/updates/info?t=xxx which returns a 403 (as everything requires auth by default).
Ideally I'd simply send my X-AUTH-TOKEN header on any XHR requests SockJS makes, but I can't see any way of adding headers looking at the API.
Worst case I can change SockJS to do this, but I'm wondering whether this functionality has been deliberately left out? I know SockJS doesn't support cookies for security reasons but that's not what I'm trying to do.
Also, for the sake of a test I did allow the info endpoint to have anonymous access but then it 403's on a bunch of other endpoints - it feels more elegant to simply pass in auth details on these requests than poke holes in my server security.
Any help appreciated.
Cheers
You cannot set the header from SockJS. Not because SockJS does not have this functionality, but because browser makers don't expose this API to Javascript. See:
https://github.com/sockjs/sockjs-client/issues/196
For a workaround, see JSON Web Token (JWT) with Spring based SockJS / STOMP Web Socket.
client side:
stompClient.connect({headername:header}, function () {
setConnected(true);
stompClient.subscribe(request.topic, function (message) {
output(message.body);
});
});
server side :
StompHeaderAccessor accessor
= MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
String headervalue= accessor.getNativeHeader("your header name").get(0);

Resources