using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using rockfishCore.Models; using rockfishCore.Util; using System.Linq; using System; //required to inject configuration in constructor using Microsoft.Extensions.Configuration; namespace rockfishCore.Controllers { //Authentication controller public class AuthController : Controller { private readonly rockfishContext _context; private readonly IConfiguration _configuration; public AuthController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs { _context = context; _configuration = configuration; } //AUTHENTICATE CREDS //RETURN JWT [HttpPost("/authenticate")] public JsonResult PostCreds(string login, string password) //if was a json body then //public JsonResult PostCreds([FromBody] string login, [FromBody] string password) { int nFailedAuthDelay = 10000; if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password)) { //Make a failed pw wait System.Threading.Thread.Sleep(nFailedAuthDelay); return Json(new { msg = "authentication failed", error = 1 }); } //DANGER DANGER - this assumes all logins are different...boo bad code //FIXME //BUGBUG //TODO //ETC /*HOW TO FIX: because of the salt during login it must assume multiple users with the same login and fetch each in turn, get the salt, hash the entered password and compare to the password stored password So, instead of singleordefault must be assumed to be a collection of users returned in this code: */ var user = _context.User.SingleOrDefault(m => m.Login == login); if (user == null) { //Make a failed pw wait System.Threading.Thread.Sleep(nFailedAuthDelay); return Json(new { msg = "authentication failed", error = 1 }); } // string pwnew=Hasher.hash(user.Salt,"2df5cc611ee485d4aa897350daa045caa4015147ae34c6b7b363f1def605d305"); string hashed = Hasher.hash(user.Salt, password); if (hashed == user.Password) { //get teh secret from appsettings.json var secret = _configuration.GetSection("JWT").GetValue("secret"); byte[] secretKey = System.Text.Encoding.ASCII.GetBytes(secret); var iat = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds(); var exp = new DateTimeOffset(DateTime.Now.AddDays(30)).ToUnixTimeSeconds(); //Generate a download token and store it with the user account and return it for the client Guid g = Guid.NewGuid(); string dlkey = Convert.ToBase64String(g.ToByteArray()); dlkey = dlkey.Replace("=", ""); dlkey = dlkey.Replace("+", ""); user.DlKey = dlkey; user.DlKeyExp=exp; _context.User.Update(user); _context.SaveChanges(); var payload = new Dictionary() { { "iat", iat.ToString() }, { "exp", exp.ToString() }, { "iss", "rockfishCore" }, { "id", user.Id.ToString() } }; //NOTE: probably don't need Jose.JWT as am using Microsoft jwt stuff to validate routes so it should also be able to //issue tokens as well, but it looked cmplex and this works so unless need to remove in future keeping it. string token = Jose.JWT.Encode(payload, secretKey, Jose.JwsAlgorithm.HS256); //string jsonDecoded = Jose.JWT.Decode(token, secretKey); return Json(new { ok = 1, issued = iat, expires = exp, token = token, dlkey = dlkey, name = user.Name, id = user.Id }); } else { //Make a failed pw wait System.Threading.Thread.Sleep(nFailedAuthDelay); return Json(new { msg = "authentication failed", error = 1 }); } } //------------------------------------------------------ }//eoc }//eons