mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
- Redesign ConnectPage with modern dark theme, profile cards with delete buttons, login/register toggle - Add DPAPI-encrypted password saving with "Remember my password" checkbox - Fix permission bit constants to match SCHEMA.md (Member role 0x663) - Add migration 004 to fix existing Member role permissions - Add comprehensive audit logging across all server packages (auth, admin, ws, setup) - Add member_join WebSocket broadcast so new users appear in members list in real-time - Add host URL normalization (strip scheme prefix) for reverse proxy compatibility - Add REST API client, ChatService orchestrator, WebSocket service with reconnection - Add model types (WsEnvelope payloads, API responses), converters, tests
84 lines
2.9 KiB
C#
84 lines
2.9 KiB
C#
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
namespace OwnCord.Client.Services;
|
|
|
|
/// <summary>
|
|
/// Stores auth tokens encrypted with DPAPI (CurrentUser scope) in AppData.
|
|
/// Equivalent security to Windows Credential Manager without requiring WinRT.
|
|
/// </summary>
|
|
public sealed class CredentialService : ICredentialService
|
|
{
|
|
private readonly string _dir;
|
|
|
|
public CredentialService()
|
|
: this(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "OwnCord", "creds")) { }
|
|
|
|
internal CredentialService(string dir) => _dir = dir;
|
|
|
|
public void SaveToken(string host, string username, string token)
|
|
{
|
|
Directory.CreateDirectory(_dir);
|
|
var plain = Encoding.UTF8.GetBytes(token);
|
|
var encrypted = ProtectedData.Protect(plain, GetEntropy(host, username), DataProtectionScope.CurrentUser);
|
|
File.WriteAllBytes(CredPath(host, username, "tok"), encrypted);
|
|
}
|
|
|
|
public string? LoadToken(string host, string username)
|
|
{
|
|
var path = CredPath(host, username, "tok");
|
|
if (!File.Exists(path)) return null;
|
|
try
|
|
{
|
|
var encrypted = File.ReadAllBytes(path);
|
|
var plain = ProtectedData.Unprotect(encrypted, GetEntropy(host, username), DataProtectionScope.CurrentUser);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
catch { return null; }
|
|
}
|
|
|
|
public void DeleteToken(string host, string username)
|
|
{
|
|
var path = CredPath(host, username, "tok");
|
|
if (File.Exists(path)) File.Delete(path);
|
|
}
|
|
|
|
public void SavePassword(string host, string username, string password)
|
|
{
|
|
Directory.CreateDirectory(_dir);
|
|
var plain = Encoding.UTF8.GetBytes(password);
|
|
var encrypted = ProtectedData.Protect(plain, GetEntropy(host, username), DataProtectionScope.CurrentUser);
|
|
File.WriteAllBytes(CredPath(host, username, "pwd"), encrypted);
|
|
}
|
|
|
|
public string? LoadPassword(string host, string username)
|
|
{
|
|
var path = CredPath(host, username, "pwd");
|
|
if (!File.Exists(path)) return null;
|
|
try
|
|
{
|
|
var encrypted = File.ReadAllBytes(path);
|
|
var plain = ProtectedData.Unprotect(encrypted, GetEntropy(host, username), DataProtectionScope.CurrentUser);
|
|
return Encoding.UTF8.GetString(plain);
|
|
}
|
|
catch { return null; }
|
|
}
|
|
|
|
public void DeletePassword(string host, string username)
|
|
{
|
|
var path = CredPath(host, username, "pwd");
|
|
if (File.Exists(path)) File.Delete(path);
|
|
}
|
|
|
|
private string CredPath(string host, string username, string prefix = "tok")
|
|
{
|
|
var key = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes($"{host}:{username}")));
|
|
return Path.Combine(_dir, $"{prefix}_{key}.dat");
|
|
}
|
|
|
|
private static byte[] GetEntropy(string host, string username)
|
|
=> Encoding.UTF8.GetBytes($"owncord:{host}:{username}");
|
|
}
|