diff --git a/GanjooRazor/Areas/Admin/Pages/SpotifyCallback.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/SpotifyCallback.cshtml.cs index c116de56..7edc08ec 100644 --- a/GanjooRazor/Areas/Admin/Pages/SpotifyCallback.cshtml.cs +++ b/GanjooRazor/Areas/Admin/Pages/SpotifyCallback.cshtml.cs @@ -18,7 +18,7 @@ namespace GanjooRazor.Areas.Admin.Pages public SpotifyCallbackModel(IHttpClientFactory clientFactory, IConfiguration configuration) { _clientFactory = clientFactory; - _configuration = configuration; + Configuration = configuration; } public IActionResult OnGet(string code = "code", string state = "none") { @@ -39,7 +39,7 @@ namespace GanjooRazor.Areas.Admin.Pages nvc.Add(new KeyValuePair("grant_type", "authorization_code")); nvc.Add(new KeyValuePair("code", code)); - string callbackUrl = $"{_configuration["SiteUrl"]}/Admin/SpotifyCallback"; + string callbackUrl = $"{Configuration["SiteUrl"]}/Admin/SpotifyCallback"; nvc.Add(new KeyValuePair("redirect_uri", callbackUrl)); @@ -48,7 +48,7 @@ namespace GanjooRazor.Areas.Admin.Pages var request = new HttpRequestMessage(HttpMethod.Post, "https://accounts.spotify.com/api/token"); request.Content = formContent; - string authValue = Convert.ToBase64String(new ASCIIEncoding().GetBytes($"{SpotifyOptions.Options["client_id"]}:{SpotifyOptions.Options["client_secret"]}")); + string authValue = Convert.ToBase64String(new ASCIIEncoding().GetBytes($"{Configuration.GetSection("Spotify")["client_id"]}:{Configuration.GetSection("Spotify")["client_secret"]}")); request.Headers.Add("Authorization", $"Basic {authValue}"); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) @@ -62,8 +62,6 @@ namespace GanjooRazor.Areas.Admin.Pages Dictionary options = new Dictionary(); - options.Add("client_id", SpotifyOptions.Options["client_id"]); - options.Add("client_secret", SpotifyOptions.Options["client_secret"]); options.Add("access_token", access_token); options.Add("refresh_token", refresh_token); SpotifyOptions.Options = options; @@ -86,6 +84,6 @@ namespace GanjooRazor.Areas.Admin.Pages private readonly IHttpClientFactory _clientFactory; - private readonly IConfiguration _configuration; + private readonly IConfiguration Configuration; } } diff --git a/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml b/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml index 4e3b11fa..070b84cc 100644 --- a/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml +++ b/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml @@ -11,7 +11,7 @@ {

@ViewData["Title"]

-

Click ورود

+

ورود

} diff --git a/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml.cs index 27fcd162..960f4e02 100644 --- a/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml.cs +++ b/GanjooRazor/Areas/Admin/Pages/SpotifyLogin.cshtml.cs @@ -1,5 +1,4 @@ using System.Net; -using GanjooRazor.Utils; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Configuration; @@ -8,11 +7,11 @@ namespace GanjooRazor.Areas.Admin.Pages { public class SpotifyLoginModel : PageModel { - private readonly IConfiguration _configuration; + private readonly IConfiguration Configuration; public SpotifyLoginModel(IConfiguration configuration) { - _configuration = configuration; + Configuration = configuration; } public IActionResult OnGet() @@ -31,8 +30,8 @@ namespace GanjooRazor.Areas.Admin.Pages get { return - $"https://accounts.spotify.com/authorize?client_id={SpotifyOptions.Options["client_id"]}&response_type=code&redirect_uri=" + - WebUtility.UrlEncode($"{_configuration["SiteUrl"]}/Admin/SpotifyCallback") + + $"https://accounts.spotify.com/authorize?client_id={Configuration.GetSection("Spotify")["client_id"]}&response_type=code&redirect_uri=" + + WebUtility.UrlEncode($"{Configuration["SiteUrl"]}/Admin/SpotifyCallback") + "&scope=&state=34fFs29kd09"; } } diff --git a/GanjooRazor/Pages/Spotify.cshtml.cs b/GanjooRazor/Pages/Spotify.cshtml.cs index e618430f..0f077de3 100644 --- a/GanjooRazor/Pages/Spotify.cshtml.cs +++ b/GanjooRazor/Pages/Spotify.cshtml.cs @@ -241,7 +241,7 @@ namespace GanjooRazor.Pages var request = new HttpRequestMessage(HttpMethod.Post, "https://accounts.spotify.com/api/token"); request.Content = formContent; - string authValue = Convert.ToBase64String(new ASCIIEncoding().GetBytes($"{SpotifyOptions.Options["client_id"]}:{SpotifyOptions.Options["client_secret"]}")); + string authValue = Convert.ToBase64String(new ASCIIEncoding().GetBytes($"{Configuration.GetSection("Spotify")["client_id"]}:{Configuration.GetSection("Spotify")["client_secret"]}")); request.Headers.Add("Authorization", $"Basic {authValue}"); var response = await _httpClient.SendAsync(request); if (response.IsSuccessStatusCode) diff --git a/GanjooRazor/Utils/EncDecUtil.cs b/GanjooRazor/Utils/EncDecUtil.cs new file mode 100644 index 00000000..7781837d --- /dev/null +++ b/GanjooRazor/Utils/EncDecUtil.cs @@ -0,0 +1,138 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace GanjooRazor.Utils +{ + /// + /// an encryption / decryption utility for strings (used for hiding spotify api credentials) + /// source : https://stackoverflow.com/questions/32972126/creating-decrypt-passwords-with-salt-iv + /// + internal static class EncDecUtil + { + public static string Encrypt(string text, string pwd) + { + byte[] originalBytes = Encoding.UTF8.GetBytes(text); + byte[] passwordBytes = Encoding.UTF8.GetBytes(pwd); + + // Hash the password with SHA256 + passwordBytes = SHA256.Create().ComputeHash(passwordBytes); + + // Generating salt bytes + byte[] saltBytes = _GetRandomBytes(); + + // Appending salt bytes to original bytes + byte[] bytesToBeEncrypted = new byte[saltBytes.Length + originalBytes.Length]; + for (int i = 0; i < saltBytes.Length; i++) + { + bytesToBeEncrypted[i] = saltBytes[i]; + } + for (int i = 0; i < originalBytes.Length; i++) + { + bytesToBeEncrypted[i + saltBytes.Length] = originalBytes[i]; + } + + byte[] encryptedBytes = _AES_Encrypt(bytesToBeEncrypted, passwordBytes); + + return Convert.ToBase64String(encryptedBytes); + } + + public static string Decrypt(string decryptedText, string pwd) + { + byte[] bytesToBeDecrypted = Convert.FromBase64String(decryptedText); + byte[] passwordBytes = Encoding.UTF8.GetBytes(pwd); + + // Hash the password with SHA256 + passwordBytes = SHA256.Create().ComputeHash(passwordBytes); + + byte[] decryptedBytes = _AES_Decrypt(bytesToBeDecrypted, passwordBytes); + + // Getting the size of salt + int _saltSize = 4; + + // Removing salt bytes, retrieving original bytes + byte[] originalBytes = new byte[decryptedBytes.Length - _saltSize]; + for (int i = _saltSize; i < decryptedBytes.Length; i++) + { + originalBytes[i - _saltSize] = decryptedBytes[i]; + } + + return Encoding.UTF8.GetString(originalBytes); + } + + private static byte[] _GetRandomBytes() + { + int _saltSize = 4; + byte[] ba = new byte[_saltSize]; + RandomNumberGenerator.Create().GetBytes(ba); + return ba; + } + + private static byte[] _AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes) + { + byte[] encryptedBytes = null; + + // Set your salt here, change it to meet your flavor: + // The salt bytes must be at least 8 bytes. + byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + + using (MemoryStream ms = new MemoryStream()) + { + using (var AES = Aes.Create("AesManaged")) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length); + cs.Close(); + } + encryptedBytes = ms.ToArray(); + } + } + + return encryptedBytes; + } + + private static byte[] _AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes) + { + byte[] decryptedBytes = null; + + // Set your salt here, change it to meet your flavor: + // The salt bytes must be at least 8 bytes. + byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + + using (MemoryStream ms = new MemoryStream()) + { + using (var AES = Aes.Create("AesManaged")) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length); + cs.Close(); + } + decryptedBytes = ms.ToArray(); + } + } + + return decryptedBytes; + } + } +} diff --git a/GanjooRazor/Utils/SpotifyOptions.cs b/GanjooRazor/Utils/SpotifyOptions.cs index 86982232..11516b10 100644 --- a/GanjooRazor/Utils/SpotifyOptions.cs +++ b/GanjooRazor/Utils/SpotifyOptions.cs @@ -21,8 +21,6 @@ namespace GanjooRazor.Utils Dictionary options = new Dictionary(); options.Add("access_token", ""); options.Add("refresh_token", ""); - options.Add("client_id", ""); - options.Add("client_secret", ""); if (System.IO.File.Exists(OptionFilePath)) { string[] lines = System.IO.File.ReadAllLines(OptionFilePath); diff --git a/GanjooRazor/appsettings.json b/GanjooRazor/appsettings.json index f3dde9a2..a85b3b94 100644 --- a/GanjooRazor/appsettings.json +++ b/GanjooRazor/appsettings.json @@ -12,6 +12,10 @@ "GlobalAPIRoot": "https://api.ganjoor.net", "SiteUrl": "http://localhost:33081", "MockSpotify": "False", + "Spotify": { + "client_id": "", + "client_secret": "" + }, "AggressiveCacheEnabled": "False", "ReadOnlyMode": "False", "MaintenanceMode": "False"