This is the first half of a change that *may* fix
https://github.com/ppy/osu/issues/26338 (it definitely fixes *one case*
where the issue happens, but I'm not sure if it will cover all of them).
As described in the issue thread, using the `jti` claim from the JWT
used for authorisation seemed like a decent idea. However, upon closer
inspection the scheme falls over badly in a specific scenario where:
1. A client instance connects to spectator server using JWT A.
2. At some point, JWT A expires, and is silently rotated by the game in
exchange for JWT B.
The spectator server knows nothing of this, and continues to only
track JWT A, including the old `jti` claim in said JWT.
3. At some later point, the client's connection to one of the spectator
server hubs drops out. A reconnection is automatically attempted,
*but* it is attempted using JWT B.
The spectator server was not aware of JWT B until now, and said JWT
has a different `jti` claim than the old one, so to the spectator
server, it looks like a completely different client connecting, which
boots the user out of their account.
This PR adds a per-session GUID which is sent in a HTTP header on every
connection attempt to spectator server. This GUID will be used instead
of the `jti` claim in JWTs as a persistent identifier of a single user's
single lazer session, which bypasses the failure scenario described
above.
I don't think any stronger primitive than this is required. As far as I
can tell this is as strong a protection as the JWT was (which is to say,
not *very* strong), and doing this removes a lot of weird complexity
that would be otherwise incurred by attempting to have client ferry all
of its newly issued JWTs to the server so that it can be aware of them.
(Or local date, in the case of non-deployed builds).
Came up when I was looking at https://github.com/ppy/osu-web/pull/11240
and found that we were still hardcoding this.
Thankfully, this *should not* cause issues, since there don't seem to be
any (documented or undocumented) API response version checks for
versions newer than 20220705 in osu-web master.
For clarity and possible debugging needs, the API response version is
also logged.
Closes https://github.com/ppy/osu/issues/26824... I think?
Can be reproduced via something like
diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs
index 485274f349..e6e93ab4c7 100644
--- a/osu.Game/Online/API/OAuth.cs
+++ b/osu.Game/Online/API/OAuth.cs
@@ -151,6 +151,11 @@ internal string RequestAccessToken()
{
if (!ensureAccessToken()) return null;
+ for (int i = 0; i < 10000; ++i)
+ {
+ _ = Token.Value.AccessToken;
+ }
+
return Token.Value.AccessToken;
}
The cause is `SecondFactorAuthForm` calling `Logout()`, which calls
`OAuth.Clear()`, _while_ the `APIAccess` connect loop is checking if
`authentication.HasValidAccessToken` is true, which happens to
internally check `Token.Value.AccessToken`, which the clearing of
tokens can brutally interrupt.
This is a prerequisite for https://github.com/ppy/osu/pull/25480.
The `WebSocketNotificationsClient` was tightly coupled to chat specifics
making it difficult to use in the second factor verification flow.
This commit's goal is to separate the websocket connection and message
handling concerns from specific chat logic concerns.
Them being together always bothered me and led to the abject failure
that is `APIUser` and its sprawl. Now that I'm about to add a flag that
is unique to `/me` for verification purposes, I'm not repeating the
errors of the past by adding yet another flag to `APIUser` that is never
present outside of a single usage context.