|
1 | | -using AdysTech.CredentialManager; |
| 1 | +using System.Runtime.InteropServices; |
| 2 | +using System.Text; |
2 | 3 |
|
3 | 4 | namespace QuotaBarWindows.Services; |
4 | 5 |
|
5 | 6 | /// <summary> |
6 | | -/// Stores and retrieves auth tokens securely via Windows Credential Manager. |
| 7 | +/// Stores and retrieves auth tokens securely via Windows Credential Manager (P/Invoke). |
| 8 | +/// No third-party NuGet dependency required. |
7 | 9 | /// </summary> |
8 | 10 | public static class CredentialService |
9 | 11 | { |
10 | 12 | private const string AppPrefix = "QuotaBar_"; |
11 | 13 |
|
12 | 14 | public static void SaveToken(string accountId, string token) |
13 | 15 | { |
14 | | - var credential = new NetworkCredential(AppPrefix + accountId, token); |
15 | | - CredentialManager.SaveCredentials(AppPrefix + accountId, credential); |
| 16 | + var targetName = AppPrefix + accountId; |
| 17 | + var tokenBytes = Encoding.UTF8.GetBytes(token); |
| 18 | + var blobPtr = Marshal.AllocHGlobal(tokenBytes.Length); |
| 19 | + Marshal.Copy(tokenBytes, 0, blobPtr, tokenBytes.Length); |
| 20 | + |
| 21 | + var cred = new NativeMethods.CREDENTIAL |
| 22 | + { |
| 23 | + Type = 1, // CRED_TYPE_GENERIC |
| 24 | + TargetName = targetName, |
| 25 | + CredentialBlobSize = tokenBytes.Length, |
| 26 | + CredentialBlob = blobPtr, |
| 27 | + Persist = 2, // CRED_PERSIST_LOCAL_MACHINE |
| 28 | + UserName = targetName |
| 29 | + }; |
| 30 | + |
| 31 | + try { NativeMethods.CredWrite(ref cred, 0); } |
| 32 | + finally { Marshal.FreeHGlobal(blobPtr); } |
16 | 33 | } |
17 | 34 |
|
18 | 35 | public static string? GetToken(string accountId) |
19 | 36 | { |
| 37 | + if (!NativeMethods.CredRead(AppPrefix + accountId, 1, 0, out var credPtr)) |
| 38 | + return null; |
| 39 | + |
20 | 40 | try |
21 | 41 | { |
22 | | - var credential = CredentialManager.GetCredentials(AppPrefix + accountId); |
23 | | - return credential?.Password; |
| 42 | + var cred = Marshal.PtrToStructure<NativeMethods.CREDENTIAL>(credPtr); |
| 43 | + if (cred.CredentialBlobSize == 0) return null; |
| 44 | + var bytes = new byte[cred.CredentialBlobSize]; |
| 45 | + Marshal.Copy(cred.CredentialBlob, bytes, 0, cred.CredentialBlobSize); |
| 46 | + return Encoding.UTF8.GetString(bytes); |
24 | 47 | } |
25 | | - catch |
| 48 | + finally |
26 | 49 | { |
27 | | - return null; |
| 50 | + NativeMethods.CredFree(credPtr); |
28 | 51 | } |
29 | 52 | } |
30 | 53 |
|
31 | 54 | public static void DeleteToken(string accountId) |
32 | 55 | { |
33 | | - try |
| 56 | + NativeMethods.CredDelete(AppPrefix + accountId, 1, 0); |
| 57 | + } |
| 58 | + |
| 59 | + private static class NativeMethods |
| 60 | + { |
| 61 | + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] |
| 62 | + public struct CREDENTIAL |
34 | 63 | { |
35 | | - CredentialManager.RemoveCredentials(AppPrefix + accountId); |
| 64 | + public int Flags; |
| 65 | + public int Type; |
| 66 | + public string TargetName; |
| 67 | + public string? Comment; |
| 68 | + public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; |
| 69 | + public int CredentialBlobSize; |
| 70 | + public IntPtr CredentialBlob; |
| 71 | + public int Persist; |
| 72 | + public int AttributeCount; |
| 73 | + public IntPtr Attributes; |
| 74 | + public string? TargetAlias; |
| 75 | + public string? UserName; |
36 | 76 | } |
37 | | - catch { /* ignore if not found */ } |
| 77 | + |
| 78 | + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
| 79 | + public static extern bool CredWrite([In] ref CREDENTIAL userCredential, [In] uint flags); |
| 80 | + |
| 81 | + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
| 82 | + public static extern bool CredRead(string target, int type, int flags, out IntPtr credentialPtr); |
| 83 | + |
| 84 | + [DllImport("advapi32.dll", SetLastError = true)] |
| 85 | + public static extern void CredFree([In] IntPtr buffer); |
| 86 | + |
| 87 | + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
| 88 | + public static extern bool CredDelete(string target, int type, int flags); |
38 | 89 | } |
39 | 90 | } |
0 commit comments