mirror of
https://github.com/weechat/weechat.git
synced 2026-06-29 22:36:38 +02:00
relay/api: support passing auth in sub protocol header
The API for connecting to WebSockets in browsers unfortunately doesn't support setting any Authorization header. This means that before this commit it was impossible to connect to the API relay from a web browser. The only thing that can be set apart from the URL is the Sec-WebSocket-Protocol header. Therefore this allows you to send the auth token in this header. This is a weird way to send auth, but it seems to be the best one that makes it possible for browsers to connect. Kubernetes also does it this way: https://github.com/kubernetes/kubernetes/pull/47740 Here is a post describing the different ways to make it possible for a browser to authenticate against a websocket connection, and it also recommends doing it this way: https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api/77060459#77060459 Note that when this header is used to pass auth, the client also needs to specify the `api.weechat` sub protocol. This is because the client and server have to agree on a sub protocol when this header is specified, and in order to not send the fake protocol used for auth back to the client, we require specifying the protocol `api.weechat`, which the server then returns to the client. This is only necessary when the Sec-WebSocket-Protocol header is used. If the Authorization header is used for auth as before, nothing changes.
This commit is contained in:
committed by
Sébastien Helleu
parent
eaace4acdb
commit
bd7c503e7b
@@ -560,15 +560,18 @@ relay_http_add_to_body (struct t_relay_http_request *request,
|
||||
int
|
||||
relay_http_get_auth_status (struct t_relay_client *client)
|
||||
{
|
||||
const char *auth, *client_totp, *pos;
|
||||
const char *auth, *sec_websocket_protocol, *client_totp, *pos;
|
||||
char *relay_password, *totp_secret, *info_totp_args, *info_totp;
|
||||
char *user_pass;
|
||||
int rc, length, totp_ok;
|
||||
char **protocol_array;
|
||||
int rc, i, length, protocol_count, use_base64url, totp_ok;
|
||||
|
||||
rc = 0;
|
||||
relay_password = NULL;
|
||||
protocol_array = NULL;
|
||||
totp_secret = NULL;
|
||||
user_pass = NULL;
|
||||
use_base64url = 0;
|
||||
|
||||
relay_password = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_password),
|
||||
@@ -589,13 +592,43 @@ relay_http_get_auth_status (struct t_relay_client *client)
|
||||
if (relay_password[0])
|
||||
{
|
||||
auth = weechat_hashtable_get (client->http_req->headers, "authorization");
|
||||
if (!auth || (weechat_strncasecmp (auth, "basic ", 6) != 0))
|
||||
|
||||
if (auth)
|
||||
{
|
||||
rc = -1;
|
||||
goto end;
|
||||
if (weechat_strncasecmp (auth, "basic ", 6) != 0)
|
||||
{
|
||||
rc = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pos = auth + 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
sec_websocket_protocol = weechat_hashtable_get (
|
||||
client->http_req->headers, "sec-websocket-protocol");
|
||||
protocol_array = weechat_string_split (sec_websocket_protocol,
|
||||
",", " ", 0, 0, &protocol_count);
|
||||
|
||||
pos = NULL;
|
||||
for (i = 0; i < protocol_count; i++)
|
||||
{
|
||||
if (strncmp (protocol_array[i],
|
||||
"base64url.bearer.authorization.weechat.", 39) == 0)
|
||||
{
|
||||
pos = protocol_array[i] + 39;
|
||||
use_base64url = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pos)
|
||||
{
|
||||
rc = -1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
pos = auth + 6;
|
||||
while (pos[0] == ' ')
|
||||
{
|
||||
pos++;
|
||||
@@ -608,7 +641,8 @@ relay_http_get_auth_status (struct t_relay_client *client)
|
||||
rc = -8;
|
||||
goto end;
|
||||
}
|
||||
length = weechat_string_base_decode ("64", pos, user_pass);
|
||||
length = weechat_string_base_decode ((use_base64url) ? "64url" : "64",
|
||||
pos, user_pass);
|
||||
if (length < 0)
|
||||
{
|
||||
rc = -2;
|
||||
@@ -616,7 +650,9 @@ relay_http_get_auth_status (struct t_relay_client *client)
|
||||
}
|
||||
if (strncmp (user_pass, "plain:", 6) == 0)
|
||||
{
|
||||
switch (relay_auth_check_password_plain (client, user_pass + 6, relay_password))
|
||||
switch (relay_auth_check_password_plain (client,
|
||||
user_pass + 6,
|
||||
relay_password))
|
||||
{
|
||||
case 0: /* password OK */
|
||||
break;
|
||||
@@ -631,7 +667,8 @@ relay_http_get_auth_status (struct t_relay_client *client)
|
||||
}
|
||||
else if (strncmp (user_pass, "hash:", 5) == 0)
|
||||
{
|
||||
switch (relay_auth_password_hash (client, user_pass + 5, relay_password))
|
||||
switch (relay_auth_password_hash (client, user_pass + 5,
|
||||
relay_password))
|
||||
{
|
||||
case 0: /* password OK */
|
||||
break;
|
||||
@@ -692,6 +729,7 @@ relay_http_get_auth_status (struct t_relay_client *client)
|
||||
}
|
||||
|
||||
end:
|
||||
weechat_string_free_split (protocol_array);
|
||||
free (relay_password);
|
||||
free (totp_secret);
|
||||
free (user_pass);
|
||||
|
||||
@@ -400,10 +400,12 @@ relay_websocket_parse_extensions (const char *extensions,
|
||||
char *
|
||||
relay_websocket_build_handshake (struct t_relay_http_request *request)
|
||||
{
|
||||
const char *sec_websocket_key;
|
||||
const char *sec_websocket_key, *sec_websocket_protocol_request;
|
||||
char *key, sec_websocket_accept[128], handshake[4096], hash[160 / 8];
|
||||
char **extensions, str_window_bits[128], sec_websocket_extensions[1024];
|
||||
int length, hash_size;
|
||||
char **extensions, **protocol_array, str_window_bits[128];
|
||||
char sec_websocket_extensions[1024];
|
||||
char sec_websocket_protocol[1024];
|
||||
int i, length, hash_size, protocol_count;
|
||||
|
||||
if (!request)
|
||||
return NULL;
|
||||
@@ -481,6 +483,24 @@ relay_websocket_build_handshake (struct t_relay_http_request *request)
|
||||
sec_websocket_extensions[0] = '\0';
|
||||
}
|
||||
|
||||
sec_websocket_protocol_request = weechat_hashtable_get (
|
||||
request->headers, "sec-websocket-protocol");
|
||||
protocol_array = weechat_string_split (sec_websocket_protocol_request,
|
||||
",", " ", 0, 0, &protocol_count);
|
||||
|
||||
sec_websocket_protocol[0] = '\0';
|
||||
for (i = 0; i < protocol_count; i++)
|
||||
{
|
||||
if (strcmp (protocol_array[i], WEBSOCKET_SUB_PROTOCOL_API_WEECHAT) == 0)
|
||||
{
|
||||
snprintf (
|
||||
sec_websocket_protocol, sizeof (sec_websocket_protocol),
|
||||
"Sec-WebSocket-Protocol: %s\r\n",
|
||||
WEBSOCKET_SUB_PROTOCOL_API_WEECHAT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* build the handshake (it will be sent as-is to client) */
|
||||
snprintf (handshake, sizeof (handshake),
|
||||
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||
@@ -488,9 +508,11 @@ relay_websocket_build_handshake (struct t_relay_http_request *request)
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: %s\r\n"
|
||||
"%s"
|
||||
"%s"
|
||||
"\r\n",
|
||||
sec_websocket_accept,
|
||||
sec_websocket_extensions);
|
||||
sec_websocket_extensions,
|
||||
sec_websocket_protocol);
|
||||
|
||||
return strdup (handshake);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#define WEBSOCKET_FRAME_OPCODE_PING 0x09
|
||||
#define WEBSOCKET_FRAME_OPCODE_PONG 0x0A
|
||||
|
||||
#define WEBSOCKET_SUB_PROTOCOL_API_WEECHAT "api.weechat"
|
||||
|
||||
struct t_relay_client;
|
||||
struct t_relay_http_request;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user