Skip to content

WebSocket 'token' request param authorization not working in the broker while using webSocketServiceEnabled=true  #16626

Description

@acortes-okode

As always, thanks a lot for your work!

Describe the bug
Using the token request param in a WebSocket URL gives an unauthorized response, it seems is not working as intended in the documentation. I am only able to authorize using the Authorization header on the handshake HTTP request using a nodejs websocket client implementation but this is not possible when using the browser WebSocket API.

I think this was the reason the token request param was implemented in this commit by using a request wrapper. But I'm trying to send a token using this param and the AuthenticationFilter it's telling me that the request is unauthorized:

2022-07-15T14:11:23,010+0200 [pulsar-web-56-15] WARN  org.apache.pulsar.broker.web.AuthenticationFilter - [127.0.0.1] Failed to authenticate HTTP request: Authentication required
2022-07-15T14:11:23,052+0200 [pulsar-web-56-15] INFO  org.eclipse.jetty.server.RequestLog - 127.0.0.1 - - [15/jul./2022:14:11:22 +0200] "GET /ws/v2/consumer/persistent/public/default/test-topic/test-sub HTTP/1.1" 401 606 "-" "-" 64

For me it seems like the AuthenticationFilter is being executed before the request wrapper is applied, or directly not using the wrapped request. I've tried to download and execute the code to test this but its my first time trying to use Pulsar and I don't know still how to do it.

I've found an opened issue about WebSocket token authentication but seems very old compared with the commit that implemented the use of the token request parameter and I'm not sure why it was not closed once implementation was made. I've opened a new one because of that but, of course, feel free to close or manage this issue in the best way.

To Reproduce
To reproduce this, I have set a standalone Pulsar configuration following the steps on https://pulsar.apache.org/ja/docs/standalone/ with version 2.10.1 and modified the standalone.conf in order to set up JWT authentication/authorization checking:

Following are the properties I have modified on standalone.conf file:

# Enable authentication
authenticationEnabled=true

# Authentication provider name list, which is comma separated list of class names
authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderToken

# Enforce authorization
authorizationEnabled=true

# Authorization provider fully qualified class-name
authorizationProvider=org.apache.pulsar.broker.authorization.MultiRolesTokenAuthorizationProvider

superUserRoles=superuser

brokerClientAuthenticationPlugin=org.apache.pulsar.client.impl.auth.AuthenticationToken
brokerClientAuthenticationParameters={"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJzdXBlcnVzZXIiLCJ0ZXN0Il19.fwFySHYsYES_j4ggOwShLJFsYiLBP9Ng0note_bex8Q"}

tokenSecretKey=data:;base64,dGVzdHNlY3JldFRvb29Mb29vb25nVG9CZVJlbWVtYmVyZWRGb3JUaGVTYWtlT2ZTaW1wbGljaXR5VW5leHBlY3RlZFdoYXRldmVyMQ==

tokenAuthClaim=roles

Then, I've just opened a WebSocket connection against ws://localhost:8080/ws/v2/consumer/persistent/public/default/test-topic/test-sub?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJzdXBlcnVzZXIiXX0.t76ZTaEfMTONmznsi8DAQyJ1dtAcmlw1KVq5BisGEpw and received the unauthorized response.

As seen in a comment in the related issue, I've tested then the WebSocket connection using a nodejs application with the websocket library that allows me to put an Authorization header on the HTTP requests and it worked fine:

#!/usr/bin/env node
console.log('Starting websocket client...');

var WebSocketClient = require('websocket').client;

var client = new WebSocketClient();

client.on('connectFailed', function (error) {
    console.log('Connect Error: ' + error.toString());
});

client.on('connect', function (connection) {
    console.log('WebSocket Client Connected');
    connection.on('error', function (error) {
        console.log("Connection Error: " + error.toString());
    });
    connection.on('close', function () {
        console.log('echo-protocol Connection Closed');
    });
    connection.on('message', function (message) {
        console.log('Received message:', message);
    });
});

client.connect(
    'ws://localhost:8080/ws/v2/consumer/persistent/public/default/test-topic/test-sub',
    null,
    null,
    {
        'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJzdXBlcnVzZXIiXX0.t76ZTaEfMTONmznsi8DAQyJ1dtAcmlw1KVq5BisGEpw'
    }
);

As stated above, seems like the AuthenticationFilter is being executed without the wrapped request and thus is not retrieving the value of the token request param when checking the Authorization header.

Expected behavior
WebSocket connections should be authorized correctly when passing the token request parameter on the connection URL since WebSocket browser implementation does not allow to use custom HTTP headers (and it seems is the common way authorization is implemented, at least until WebSocket browser implementation is evolved to support sending HTTP headers).

Thanks for all your support!

Metadata

Metadata

Assignees

Labels

type/bugThe PR fixed a bug or issue reported a bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions