docs: #290 store Spotify API credential using the OptionsService (incomplete)

This commit is contained in:
Hamid Reza Mohammadi 2022-03-05 21:42:36 +03:30
parent 3aa86a1172
commit f3cd1af887
7 changed files with 152 additions and 15 deletions

View File

@ -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<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", code));
string callbackUrl = $"{_configuration["SiteUrl"]}/Admin/SpotifyCallback";
string callbackUrl = $"{Configuration["SiteUrl"]}/Admin/SpotifyCallback";
nvc.Add(new KeyValuePair<string, string>("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<string, string> options = new Dictionary<string, string>();
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;
}
}

View File

@ -11,7 +11,7 @@
{
<h1>@ViewData["Title"]</h1>
<p>Click <a href="@Model.SpotifyUrl">ورود</a></p>
<p><a href="@Model.SpotifyUrl">ورود</a></p>
}

View File

@ -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";
}
}

View File

@ -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)

View File

@ -0,0 +1,138 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace GanjooRazor.Utils
{
/// <summary>
/// an encryption / decryption utility for strings (used for hiding spotify api credentials)
/// source : https://stackoverflow.com/questions/32972126/creating-decrypt-passwords-with-salt-iv
/// </summary>
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;
}
}
}

View File

@ -21,8 +21,6 @@ namespace GanjooRazor.Utils
Dictionary<string, string> options = new Dictionary<string, string>();
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);

View File

@ -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"