docs: #290 store Spotify API credential using the OptionsService (incomplete)
This commit is contained in:
parent
3aa86a1172
commit
f3cd1af887
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
{
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Click <a href="@Model.SpotifyUrl">ورود</a></p>
|
||||
<p><a href="@Model.SpotifyUrl">ورود</a></p>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
138
GanjooRazor/Utils/EncDecUtil.cs
Normal file
138
GanjooRazor/Utils/EncDecUtil.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user