Merge pull request #1004 from crystallpunk-14/ed-11-03-2025-discord-auth

Discord Auth expanse
This commit is contained in:
Ed
2025-04-15 22:50:08 +03:00
committed by GitHub
parent 61edcf69df
commit c61a76fa69
4 changed files with 111 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ public sealed class DiscordAuthManager
[Dependency] private readonly IStateManager _stateManager = default!;
public string AuthUrl { get; private set; } = "";
public string ErrorMessage { get; private set; } = "";
public void Initialize()
{
@@ -22,6 +23,7 @@ public sealed class DiscordAuthManager
if (_stateManager.CurrentState is DiscordAuthState)
return;
AuthUrl = msg.AuthUrl;
ErrorMessage = msg.ErrorMessage;
_stateManager.RequestStateChange<DiscordAuthState>();
}
}

View File

@@ -21,6 +21,7 @@ public sealed partial class DiscordAuthGui : Control
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
var link = _discordAuthManager.AuthUrl;
var errorMessage = _discordAuthManager.ErrorMessage;
AuthLinkEdit.SetText(link);
DLinkEdit.SetText(DiscordLink);
@@ -42,5 +43,10 @@ public sealed partial class DiscordAuthGui : Control
{
uriOpener.OpenUri(DiscordLink);
};
if (errorMessage != "")
{
InfoLabel.Text = errorMessage;
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
@@ -5,6 +6,7 @@ using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Database;
using Content.Shared._CP14.Discord;
using Content.Shared.CCVar;
using Robust.Server.Player;
@@ -12,7 +14,6 @@ using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server._CP14.Discord;
@@ -21,13 +22,28 @@ public sealed class DiscordAuthManager
[Dependency] private readonly IServerNetManager _netMgr = default!;
[Dependency] private readonly IPlayerManager _playerMgr = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IServerDbManager _db = default!;
private ISawmill _sawmill = default!;
private readonly HttpClient _httpClient = new();
private bool _enabled = false;
private bool _enabled;
private string _apiUrl = string.Empty;
private string _apiKey = string.Empty;
private string _discordGuild = "1221923073759121468"; //CrystallEdge server required
private HashSet<string> _blockedGuilds = new()
{
"1361786483073093673", //Testing one
"1346922008000204891",
"1186566619858731038",
"1355279097906855968",
"1352009516941705216",
"1359476387190145034",
"1294276016117911594",
"1278755078315970620",
};
public event EventHandler<ICommonSession>? PlayerVerified;
public void Initialize()
@@ -47,7 +63,7 @@ public sealed class DiscordAuthManager
private async void OnAuthCheck(MsgDiscordAuthCheck msg)
{
var verified = await IsVerified(msg.MsgChannel.UserId);
if (!verified)
if (!verified.Verified)
return;
var session = _playerMgr.GetSessionById(msg.MsgChannel.UserId);
@@ -68,7 +84,7 @@ public sealed class DiscordAuthManager
if (args.NewStatus == SessionStatus.Connected)
{
var verified = await IsVerified(args.Session.UserId);
if (verified)
if (verified.Verified)
{
PlayerVerified?.Invoke(this, args.Session);
return;
@@ -76,11 +92,12 @@ public sealed class DiscordAuthManager
var message = new MsgDiscordAuthRequired();
message.AuthUrl = await GenerateLink(args.Session.UserId) ?? string.Empty;
message.ErrorMessage = verified.ErrorMessage;
args.Session.Channel.SendMessage(message);
}
}
public async Task<bool> IsVerified(NetUserId userId, CancellationToken cancel = default)
public async Task<AuthData> IsVerified(NetUserId userId, CancellationToken cancel = default)
{
_sawmill.Debug($"Player {userId} check Discord verification");
@@ -93,8 +110,64 @@ public sealed class DiscordAuthManager
var response = await _httpClient.SendAsync(request, cancel);
_sawmill.Debug($"{await response.Content.ReadAsStringAsync(cancel)}");
_sawmill.Debug($"{(int) response.StatusCode}");
return response.StatusCode == HttpStatusCode.OK;
_sawmill.Debug($"{(int)response.StatusCode}");
var verified = response.StatusCode == HttpStatusCode.OK;
var guildsVerified = await CheckGuilds(userId, cancel);
if (!verified)
return new AuthData { Verified = false, ErrorMessage = Loc.GetString("cp14-discord-info")};
return guildsVerified;
}
private async Task<AuthData> CheckGuilds(NetUserId userId, CancellationToken cancel = default)
{
var isWhitelisted = await _db.GetWhitelistStatusAsync(userId);
if (isWhitelisted)
{
return new AuthData { Verified = true };
}
_sawmill.Debug($"Checking guilds for {userId}");
var requestUrl = $"{_apiUrl}/api/guilds?method=uid&id={userId}";
_sawmill.Debug($"Guilds request url:{requestUrl}");
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
var response = await _httpClient.SendAsync(request, cancel);
_sawmill.Debug($"Guilds response: {await response.Content.ReadAsStringAsync(cancel)}");
_sawmill.Debug($"(int) response.StatusCode: {(int)response.StatusCode}");
if (!response.IsSuccessStatusCode)
{
_sawmill.Debug($"Player {userId} guilds check failed: !response.IsSuccessStatusCode");
return new AuthData { Verified = false, ErrorMessage = "Unexpected error: !response.IsSuccessStatusCode" };
}
var guilds = await response.Content.ReadFromJsonAsync<DiscordGuildsResponse>(cancel);
if (guilds is null)
{
_sawmill.Debug($"Player {userId} guilds check failed: guilds is null");
return new AuthData { Verified = false, ErrorMessage = "Unexpected error: guilds is null" };
}
foreach (var guild in guilds.Guilds)
{
if (_blockedGuilds.Contains(guild.Id))
{
var errorMessage = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String("RXJyb3IgMjcwMQ=="));
return new AuthData { Verified = false, ErrorMessage = errorMessage };
}
}
if (guilds.Guilds.All(guild => guild.Id != _discordGuild))
{
_sawmill.Debug($"Player {userId} is not in required guild {_discordGuild}");
return new AuthData { Verified = false, ErrorMessage = "You are not a member of the CrystallEdge server." };
}
return new AuthData { Verified = true };
}
public async Task<string?> GenerateLink(NetUserId userId, CancellationToken cancel = default)
@@ -119,7 +192,8 @@ public sealed class DiscordAuthManager
}
catch (Exception e)
{
_sawmill.Error($"Unexpected error verifying user via auth service. Error: {e.Message}. Stack: \n{e.StackTrace}");
_sawmill.Error(
$"Unexpected error verifying user via auth service. Error: {e.Message}. Stack: \n{e.StackTrace}");
return null;
}
}
@@ -129,4 +203,22 @@ public sealed class DiscordAuthManager
[JsonPropertyName("link")]
public string Link { get; set; } = string.Empty;
}
private sealed class DiscordGuildsResponse
{
[JsonPropertyName("guilds")]
public DiscordGuild[] Guilds { get; set; } = [];
}
private sealed class DiscordGuild
{
[JsonPropertyName("id")]
public string Id { get; set; } = null!;
}
public sealed class AuthData
{
public bool Verified { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
}
}

View File

@@ -8,14 +8,17 @@ public sealed class MsgDiscordAuthRequired : NetMessage
{
public override MsgGroups MsgGroup => MsgGroups.Command;
public string AuthUrl { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
{
ErrorMessage = buffer.ReadString();
AuthUrl = buffer.ReadString();
}
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
{
buffer.Write(ErrorMessage);
buffer.Write(AuthUrl);
}
}