Open watfordjc opened 3 years ago
There are several things needed to connect to a Discord call:
wss
) connection established to the gateway.RTP header + Encrypted Opus Audio
.Encrypted Opus Audio
uses the encryption key received via Voice WSS and a 24 byte nonce equal to the 12 bytes of the RTP header
and 12 NUL
bytes.If the call is between two users (i.e. it is a DM Call or Group DM Call, rather than joining a Voice Channel), I'm not sure at which point in the above flow the user initiating the call should wait for the other user(s). Do you go through the entire process resulting in the channel having a voice session associated with it and hope someone else in the channel then joins the voice session, or do you wait for someone else to join the voice session before creating the voice connection?
I haven't done anything with OAuth2 other than the typical user thing of clicking/tapping/scanning something to use it to sign in somewhere.
There are 4 things involved in the OAuth2 flow, although I've added a fifth and sixth one to aid in comprehension:
code
. It gives the authorization code to The Client via The Client's Redirection URI(s).code
or a refresh_token
for an access_token
and refresh_token
.I had written a really long comment about how things would/could work, how I was thinking of implementing it, and the like, but my computer crashed and the textarea wasn't restored when I reopened my Web browser. As a result, this comment will likely contain less content than it would have without the crash.
The first stage in the OAuth2 flow is obtaining an authorization code from the Authorization Server. This is done by making an HTTP GET request in the User-Agent to the Authorization Server, with The Human then authenticating (e.g. by username/password/2FA) and granting/denying permissions. The Authorization Server then redirects to The Client's Redirection URI, with a code
query parameter.
There are two query parameters that can change the authorization code grant flow. The first is the state
query parameter which is somehow used for CSRF protection. The second is the (Discord) prompt
query parameter, which can change how The Human gives permissions if The Client has already been given permissions.
There are several steps involved in obtaining an access token. Although an OAuth2 provider can implement different methods, I am going with the most functional/secure one that Discord offers:
code
in the redirect data.code
for an access_token
and refresh_token
.access_token
expires, and exchanges the refresh_token
for a new access_token
and refresh_token
.I am going to skip step two for now because I don't (currently) see a need to remember the users that have logged into Discord in Game Chat Lite:
Visibility.Collapsed
) WebView2 control and navigates to the Authorization Server's URL.getToken(code)
.code
query parameter/value), which is a URI for a Server Sent Events stream. The Client updates The User-Agent as it makes progress, and The User-Agent relays that information (and any issues with the SSE connection) to Game Chat Lite via Web messages.code
for an access_token
and refresh_token
.access_token
, token_type
, expires_in
, refresh_token
, and scope
.access_token
expires. It asks The Client to connect to The Token Server and exchange the refresh_token
for a new access_token
and refresh_token
. The Client relays the JSON to Game Chat Lite via SSE.If The User-Agent is only going to be following redirects without any input from The Human, there is no point actually showing The User-Agent. With suitable event handlers it is possible to update The Human on the progress throughout.
Server Sent Events (SSE) are a one-way communications method between a Web server and a Web client. The client connects to a URL for an SSE stream, and the server periodically sends events (a simple key: value
text-based structure).
There are other ways of performing such communication, but I have no "I always do it that way" option. Yesterday, when I was thinking of similar things I'd used over the last few years, I listed FireBase push notifications, jQuery JSON fetching and setTimeout()
HTTP 200/304 polling, and FireBase cloud database synchronisation. Server Sent Events means I've done things four different ways, and WebSockets would add a fifth way.
As my Web servers use HTTP/2, and the SSE stream is coded in PHP, there is a potential latency issue due to output buffering. There are two things needed for NGINX + PHP-FPM to treat the document as non-buffered output (something like chunked output, but not because HTTP/2):
while (ob_get_level()) { ob_end_flush(); }
will disable all levels of output buffering for the PHP script.echo
s/print
s an event, call flush();
.location
block for the SSE file, fastcgi_buffering off;
.There can also be an issue with timeouts:
fastcgi_read_timeout 60s;
. If no output is sent from PHP-FPM to NGINX for 60 seconds, NGINX closes the connection.max_execution_time = 30
. If a PHP script spends more than 30 seconds executing, the script is killed with an error.
sleep()
and file_get_contents()
are not counted in the execution time.fopen()
separately has a default setting of default_socket_timeout = 60
.code
be valid a maximum of 10 minutes.code
is reused, all tokens issued for that code SHOULD be revoked and the OAuth2 process needs to start from the beginning.refresh_token
may be revoked when it is used. A communication issue could mean a new access_token
was issued but isn't available, requiring the OAuth2 process be started from the beginning.
Feature Branch
Current feature branch for this issue: not created yet.
Progress
Background
The other day in a Discord call I asked what the audio was like and was told I didn't sound like I usually do. Whilst changing settings to try and improve the audio quality, I noticed that Discord was using 25-60% of my CPU (4 core/thread Intel J4105, Odyssey X86 SBC).
That raised a question: could I create an app for Discord calls that uses less system resources, particularly less CPU?
There are several things needed to connect to a Discord call that are covered in a comment to this issue.
This issue is related to the first thing required: obtaining an OAuth2 access token for our Discord user.