Updated/fixed database interface (requires full model testing) Some changes to db schema (create statement may be wrong) Reworked front end login page (not styling), still no routing Changes to how frontend packages ECC keymaster
parent
b5f8429a42
commit
d41b0810d2
@ -1,4 +1,4 @@ |
||||
VUE_APP_TEMPLATE_HEADERS_ENDPOINT=http://192.168.0.171:5252/api/template |
||||
VUE_APP_TEMPLATE_ENDPOINT=http://192.168.0.171:5252/api/template |
||||
VUE_APP_LOGIN_ENDPOINT=http://192.168.0.171:5252/api/login |
||||
VUE_APP_LOGIN_ENDPOINT=http://192.168.0.171:5252/api/auth |
||||
VUE_APP_PORTFOLIO_GPG_PUBLIC="-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmDMEY/+uFRYJKwYBBAHaRw8BAQdATSNmZMGFcu39sYJkOw6YefrarbnuE045+l2N\n92sDHhy0ekxFQUYgUG9ydGZvbGlvIFBvcnRhbCAoTEVBRiBDb21tZXJjaWFsIENh\ncGl0YWwgfCBQb3J0Zm9saW8gUG9ydGFsIEVuY3J5cHRpb24gJiBTaWduaW5nIEtl\neSkgPHBvcnRmb2xpby1wb3J0YWxAbGVhZm5vdy5jb20+iJMEExYKADsWIQTy4hw/\n4UStInnbNSZAWHYAralXlAUCY/+uFQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIe\nBwIXgAAKCRBAWHYAralXlDVYAP9NliQN6DYRb5KB39N5aeIJVM0CUHL8zbwIPkP8\nWQf7zAEAryZ3xvHCJ7XZu+10CBW8GRCTmJt43KqXlQ096xqXnAa4OARj/64VEgor\nBgEEAZdVAQUBAQdAZ7Y+o3YYmOZjahzgJNCkmIekdAQg1kgLLAFrL5zVV0cDAQgH\niHgEGBYKACAWIQTy4hw/4UStInnbNSZAWHYAralXlAUCY/+uFQIbDAAKCRBAWHYA\nralXlN9rAQCXEZ/4OzlNqrK/9gIO9rDRnx8c8Q2ES6ELXc25NnSqTwEA2GXARD2O\nZztuZHC1yowe9WdkLKXhlSqqdtGNdQuKRwQ=\n=eXTg\n-----END PGP PUBLIC KEY BLOCK-----" |
||||
@ -0,0 +1,48 @@ |
||||
// using Microsoft.AspNetCore.Mvc; |
||||
// using Microsoft.EntityFrameworkCore; |
||||
// using Models; |
||||
|
||||
// namespace backend.Controllers |
||||
// { |
||||
// //FIXME: This needs to be behind authorization in production |
||||
// [Route("api/createaccount")] |
||||
// [ApiController] |
||||
// public class CreateAccountController : ControllerBase |
||||
// { |
||||
|
||||
// private readonly IConfiguration _config; |
||||
// private readonly ILogger _logger; |
||||
|
||||
// public CreateAccountController(IConfiguration config, ILogger<CreateAccountController> logger) |
||||
// { |
||||
// _config = config; |
||||
// _logger = logger; |
||||
// } |
||||
|
||||
// [HttpPost] |
||||
// async public Task<ActionResult> CreateAccount([FromBody] dynamic accountInfo) |
||||
// { //TODO Create a model for this request |
||||
// User newUser = new User(accountInfo.username, accountInfo.email, accountInfo.password, null); |
||||
// _logger.LogDebug($"New user to be added: {newUser}"); |
||||
// using (var db = new PortfolioPortalDbContext()) |
||||
// { |
||||
// var addTask = await db.AddAsync(newUser); |
||||
// await db.SaveChangesAsync(); |
||||
// var newUserDb = await db.Users.FirstOrDefaultAsync(u => u.Email == newUser.Email); |
||||
// // Add auths |
||||
|
||||
// if (addTask.State == EntityState.Added) |
||||
// { |
||||
// _logger.LogInformation($"User user added to database: {newUserDb}."); |
||||
// return Ok($"Account created for {accountInfo.email}."); |
||||
// } |
||||
// else |
||||
// { |
||||
// _logger.LogError($"Failed to add new user to database: {newUser}!"); |
||||
// return BadRequest("Failed to add account to database."); |
||||
// } |
||||
// } |
||||
// } |
||||
// } |
||||
// } |
||||
|
||||
@ -0,0 +1,41 @@ |
||||
using Microsoft.AspNetCore.Mvc; |
||||
using Hasher; |
||||
using Models; |
||||
using System.IdentityModel.Tokens.Jwt; |
||||
using System.Security.Claims; |
||||
using System.Text; |
||||
using Microsoft.IdentityModel.Tokens; |
||||
|
||||
|
||||
namespace backend.Controllers |
||||
{ |
||||
///<summary> |
||||
/// For generating Argon2d hashes in test. Do not use in prod or with front end |
||||
///</summary> |
||||
[Route("api/pwhash")] |
||||
[ApiController] |
||||
public class PasswordHashController : ControllerBase |
||||
{ |
||||
private readonly IConfiguration _config; |
||||
private readonly ILogger _logger; |
||||
|
||||
public PasswordHashController(IConfiguration config, ILogger<PasswordHashController> logger) |
||||
{ |
||||
_config = config; |
||||
_logger = logger; |
||||
} |
||||
///<summary> |
||||
/// Takes a plaintext password in the form {"password": "yourpasswordhere"}, |
||||
/// generates an Argon2d hash for it (using random salt), and returns the hash |
||||
///</summary> |
||||
[HttpPost] |
||||
public ActionResult HashPassword([FromBody] dynamic plainTextPassword) |
||||
{ |
||||
string password = (string) plainTextPassword.password; |
||||
string hashedPW = Argon2dHasher.GenerateArgon2Hash(password, null); |
||||
return Ok($"Password hash: '{hashedPW}'"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,19 +1,26 @@ |
||||
using Newtonsoft.Json; |
||||
using Hasher; |
||||
|
||||
namespace Models |
||||
{ |
||||
|
||||
|
||||
public class User |
||||
{ |
||||
public int UserId { get; set; } |
||||
// Nullable fields have defaults in the database |
||||
public short UserId { get; set; } |
||||
public string Username { get; set; } |
||||
public string Email { get; set; } |
||||
public string PasswordHash { get; set; } |
||||
public string Created { get; set; } |
||||
public string Updated { get; set; } |
||||
public DateTimeOffset Created { get; set; } |
||||
public DateTimeOffset Updated { get; set; } |
||||
public DateTimeOffset PasswordUpdated { get; set; } |
||||
public int CompanyId { get; set; } |
||||
public DateTimeOffset LastLogin { get; set; } |
||||
public short? CompanyId { get; set; } //May need to change to not nullable |
||||
public DateTimeOffset? LastLogin { get; set; } |
||||
public bool IsEnabled { get; set; } |
||||
public int FailedAttempts { get; set; } |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,93 +1,73 @@ |
||||
<template> |
||||
<div class="login container"> |
||||
<form @submit.prevent="login"> |
||||
<div class="form-group"> |
||||
<label for="username">Email:</label> |
||||
<input type="text" class="form-control" id="email" v-model="email"> |
||||
<div> |
||||
<h1>Login</h1> |
||||
<form @submit.prevent="handleLogin"> |
||||
<div> |
||||
<label for="email">Email:</label> |
||||
<input type="email" id="email" v-model="email" required /> |
||||
</div> |
||||
<div class="form-group"> |
||||
<div> |
||||
<label for="password">Password:</label> |
||||
<input type="password" class="form-control" id="password"> |
||||
<input type="password" id="password" v-model="password" required /> |
||||
</div> |
||||
<div class="form-group"> |
||||
<a href="#">Forgot password?</a> |
||||
</div> |
||||
<button type="submit" class="btn btn-primary">Log In</button> |
||||
<button type="submit">Login</button> |
||||
</form> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { store } from '@/store'; |
||||
import { ECCManager } from '@/ecc.js'; |
||||
import { ECCManager } from "../ecc.js"; |
||||
|
||||
export default { |
||||
data() { |
||||
return { |
||||
email: '', |
||||
keyManager: ECCManager.create() |
||||
email: "", |
||||
password: "" |
||||
}; |
||||
}, |
||||
methods: { |
||||
async login() { |
||||
store.userEmail = this.email; |
||||
// Store the resolved eccManager in the store |
||||
store.eccManager = await this.keyManager; |
||||
const clientVerificationKey = eccManager.CCK; |
||||
|
||||
let passwordInput = document.getElementById('password'); |
||||
let passwordValue = passwordInput.value; |
||||
// Create http post with un, pw, and verification key |
||||
const payload = { |
||||
email: this.email, |
||||
password: passwordValue, |
||||
client_verification_key: clientVerificationKey, |
||||
async handleLogin() { |
||||
if (!this.email || !this.password) { |
||||
alert("Email and password are required"); |
||||
return; |
||||
} |
||||
const payloadString = JSON.stringify(payload); |
||||
//FIXME: Dont need to send/sign headers here |
||||
// const headers = new Headers; |
||||
// // Sign the payload (good practice) |
||||
// headers = await store.eccManager.addPayloadSignature(headers, payloadString) |
||||
// headers = await store.eccManager.addTimeStampHeaders(headers) |
||||
|
||||
const loginUrl = process.env.VUE_APP_LOGIN_ENDPOINT; |
||||
|
||||
fetch(loginUrl, { |
||||
method: 'POST', |
||||
//FIXME probably don't need headers: headers, |
||||
body: payloadString |
||||
}) |
||||
.then(response => { |
||||
if (response.ok) { |
||||
//TODO Handle successful auth response |
||||
console.log('Login successful!'); |
||||
console.log('resp: ' ,response) |
||||
} else { |
||||
//TODO Handle error auth response |
||||
console.error('Login failed.'); |
||||
console.log('resp: ', response) |
||||
//try { |
||||
const eccManager = await ECCManager.create(); |
||||
const loginDetails = { |
||||
email: this.email, |
||||
password: this.password, |
||||
clientVerificationKey: eccManager.CVK |
||||
}; |
||||
console.debug(`Client Key: ${eccManager.CVK}`) |
||||
console.debug(`Req body: ${JSON.stringify(loginDetails)}`) |
||||
// Replace this URL with your API endpoint URL |
||||
const apiURL = process.env.VUE_APP_LOGIN_ENDPOINT; |
||||
const response = await fetch(apiURL, { |
||||
method: "POST", |
||||
headers: { |
||||
"Content-Type": "application/json" |
||||
}, |
||||
body: JSON.stringify(loginDetails) |
||||
}); |
||||
|
||||
if (!response.ok) { |
||||
throw new Error("Login failed"); |
||||
} |
||||
}) |
||||
.catch(error => console.error(error)); |
||||
|
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.login { |
||||
margin: 0 auto; |
||||
width: 50%; |
||||
text-align: center; |
||||
} |
||||
.container { |
||||
max-width: 600px; |
||||
margin-top: 100px; |
||||
margin-bottom: 100px; |
||||
padding: 20px; |
||||
background-color: #c9e6b8; |
||||
border-radius: 5px; |
||||
box-shadow: 0 2px 4px rgba(0,0,0,.3); |
||||
} |
||||
</style> |
||||
const responseData = await response.json(); |
||||
|
||||
// Store the private key and JWT in a secure storage, e.g., Vuex store or sessionStorage |
||||
sessionStorage.setItem("privateKey", eccManager.CSK); |
||||
sessionStorage.setItem("jwt", responseData.jwt); |
||||
|
||||
// Redirect to the desired page after successful login |
||||
// You can replace '/' with the desired route |
||||
this.$router.push("/"); |
||||
//} catch (error) { |
||||
// alert(`Login failed: ${error}`); |
||||
// } |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
Loading…
Reference in new issue