Verified the creation of JWTs

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 key
master
Griffiths Lott 3 years ago
parent b5f8429a42
commit d41b0810d2
Signed by: gprog
GPG Key ID: AF76DEDF1D66B847
  1. 2
      .env
  2. 8
      TODO.txt
  3. 103
      backend/Controllers/AuthorizationController.cs
  4. 48
      backend/Controllers/CreateAccount.cs
  5. 41
      backend/Controllers/HashPasswordController.cs
  6. 2
      backend/JwtAuthenticationMiddelware.cs
  7. 2
      backend/Models/AuthLevels.cs
  8. 6
      backend/Models/Auths.cs
  9. 4
      backend/Models/Company.cs
  10. 4
      backend/Models/LeafHeader.cs
  11. 6
      backend/Models/LoginDetails.cs
  12. 19
      backend/Models/User.cs
  13. 10
      backend/PPTLDbContext.cs
  14. 9
      backend/Program.cs
  15. 4
      backend/appsettings.Development.json
  16. 5612
      backend/logs/portfolioportal20230319.txt
  17. BIN
      database.tar.xz
  18. 14
      database/BASE_DATA.sql
  19. 6
      database/CREATE_TABLES.sql
  20. 126
      src/components/UserLogin.vue
  21. 20
      src/ecc.js

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

@ -4,4 +4,10 @@
[ ] Normalize appsettings case convention
[ ] TemplateHeaders controler use database, not config
[ ] Clean up imports
[ ] Remove unused packages
[ ] Remove unused packages
[ ] Move database operations into thier own class/service
##FRONTEND
[ ] Create company interface
[ ] Create users interface
[ ] Only allow creation of users for created companies

@ -9,17 +9,19 @@ using Microsoft.IdentityModel.Tokens;
namespace backend.Controllers
{
[Route("api/login")]
[Route("api/auth")]
[ApiController]
public class AuthorizationController : ControllerBase
{
private readonly IConfiguration _config;
private readonly ILogger _logger;
private readonly PortfolioPortalDbContext _dbContext;
public AuthorizationController(IConfiguration config, ILogger<AuthorizationController> logger)
public AuthorizationController(IConfiguration config, ILogger<AuthorizationController> logger, PortfolioPortalDbContext dbContext)
{
_config = config;
_logger = logger;
_dbContext = dbContext;
}
/// <summary>
@ -34,11 +36,9 @@ namespace backend.Controllers
// Log the request (password is censored)
_logger.LogDebug($"Login request with body: {requestBody.ToString()}");
User? user;
using (var db = new PortfolioPortalDbContext())
{
// Check for a user with that email in the database
user = db.Users.FirstOrDefault(u => u.Email.ToLower() == requestBody.email.ToLower());
}
// Check for a user with that email in the database
user = _dbContext.Users.FirstOrDefault(u => u.Email.ToLower() == requestBody.email.ToLower());
if (user != null)
{
_logger.LogDebug($"Found user: {user}");
@ -57,7 +57,7 @@ namespace backend.Controllers
// Get the user authorizations
List<AuthWithLevel> auths = GetUserAuths(user);
if (auths.Count == 0) {
_logger.LogWarning($"{user.Username} has not authorization levels!");
_logger.LogWarning($"{user.Username} has no authorization levels!");
return Unauthorized("No authorizations.");
}
@ -89,7 +89,7 @@ namespace backend.Controllers
}
// No User found | It's best not to distinguish between invalid pw and unknown un for security reasons
return Unauthorized("Invalid username or password.");
return Unauthorized("Incorrect username or password!");
}
/// <summary>
@ -145,16 +145,12 @@ namespace backend.Controllers
/// </summary>
/// <param name="user">The user whose failed attempt count is being reset.</param>
private async Task ResetFailedAttempts(User user)
{
// Create a new database context using the PortfolioPortalDbContext class
await using (var db = new PortfolioPortalDbContext())
{
// Set the user's failed attempt count to zero
user.FailedAttempts = 0;
// Save changes to the database asynchronously
await db.SaveChangesAsync();
}
{
// Set the user's failed attempt count to zero
user.FailedAttempts = 0;
// Save changes to the database asynchronously
await _dbContext.SaveChangesAsync();
}
@ -167,31 +163,28 @@ namespace backend.Controllers
{
List<AuthWithLevel> authsWithLevels;
// Create a new database context using the PortfolioPortalDbContext class
using (var db = new PortfolioPortalDbContext())
{
// Reset the user's failed attempt count to zero
user.FailedAttempts = 0;
// Reset the user's failed attempt count to zero
user.FailedAttempts = 0;
// Save changes to the database
db.SaveChanges();
// Save changes to the database
_dbContext.SaveChanges();
// Retrieve the authorization levels for the specified user from the database
authsWithLevels = (from auth in db.Auths
join level in db.AuthLevels on auth.AuthLevelId equals level.AuthLevelId into authLevelGroup
from authLevel in authLevelGroup.DefaultIfEmpty()
where auth.UserId == user.UserId
select new AuthWithLevel
{
Auth = auth,
AuthLevel = authLevel
}).ToList();
// Retrieve the authorization levels for the specified user from the database
authsWithLevels = (from auth in _dbContext.Auths
join level in _dbContext.AuthLevels on auth.AuthLevelId equals level.AuthLevelsId into authLevelGroup
from authLevel in authLevelGroup.DefaultIfEmpty()
where auth.UserId == user.UserId
//FIXME Just take the AuthLevel
select new AuthWithLevel
{
Auth = auth,
AuthLevel = authLevel
}).ToList();
// Log a message indicating the user's authorization levels
_logger.LogDebug($"{user.Username} auths: {authsWithLevels}");
}
// Log a message indicating the user's authorization levels
_logger.LogDebug($"{user.Username} auths: {authsWithLevels}");
// Return a list of Auth objects representing the user's authorization levels
return authsWithLevels;
}
@ -203,27 +196,23 @@ namespace backend.Controllers
/// <param name="user">The user whose login attempt failed.</param>
/// <returns>A boolean value indicating whether the user's account is currently enabled.</returns>
private bool RecordFailedAttempt(User user)
{
// Create a new database context using the PortfolioPortalDbContext class
using (var db = new PortfolioPortalDbContext())
{
// Increment the user's failed attempt count
user.FailedAttempts += 1;
{
// Increment the user's failed attempt count
user.FailedAttempts += 1;
// Log a message indicating that the user's login attempt failed, and how many failed attempts they have made so far
_logger.LogInformation($"{user.Username} failed authentication! ({user.FailedAttempts}/5)");
// Log a message indicating that the user's login attempt failed, and how many failed attempts they have made so far
_logger.LogInformation($"{user.Username} failed authentication! ({user.FailedAttempts}/5)");
// If the user has made too many failed attempts, lock their account
if (user.FailedAttempts > 4)
{
_logger.LogWarning($"{user.Username} has had their account locked for too many failed attempts!");
user.IsEnabled = false;
}
// Save changes to the database
db.SaveChanges();
// If the user has made too many failed attempts, lock their account
if (user.FailedAttempts > 4)
{
_logger.LogWarning($"{user.Username} has had their account locked for too many failed attempts!");
user.IsEnabled = false;
}
// Save changes to the database
_dbContext.SaveChanges();
// Return a boolean value indicating whether the user's account is currently enabled
return user.IsEnabled;
}

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

@ -112,7 +112,7 @@ public class JwtAuthenticationMiddleware
}
catch
{
_logger.LogWarning($"Failed to verify signature!");
_logger.LogError($"Failed to verify signature!");
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}

@ -3,7 +3,7 @@ namespace Models
public class AuthLevels
{
public int AuthLevelId { get; set; }
public Byte AuthLevelsId { get; set; }
public string AuthLevel { get; set; }
public string Description { get; set; }
}

@ -3,9 +3,9 @@ namespace Models
public class Auth
{
public int AuthId { get; set; }
public int UserId { get; set; }
public int AuthLevelId { get; set; }
public Int16 AuthId { get; set; }
public Int16 UserId { get; set; }
public Byte AuthLevelId { get; set; }
}
public class AuthWithLevel

@ -2,9 +2,9 @@ namespace Models
{
public class Company
{
public int CompanyId { get; set; }
public Int16 CompanyId { get; set; }
public string CompanyName { get; set; }
public int MainContact { get; set; }
public Int16 MainContact { get; set; }
public string State { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset Updated { get; set; }

@ -2,14 +2,14 @@ namespace Models
{
public partial class LeafHeader
{
public long LeafHeaderId { get; set; }
public int LeafHeaderId { get; set; }
public string LeafHeaderLeafHeader { get; set; }
public string HeaderRegex { get; set; }
public bool LeafHeaderRequired { get; set; }
public bool Nullable { get; set; }
public string ValueRegex { get; set; }
public bool Active { get; set; }
public int Version { get; set; }
public Byte Version { get; set; }
public DateTimeOffset Created { get; set; }
}
}

@ -10,8 +10,10 @@ namespace Models
public string email;
// Plain text password, must be hashed (Argon2d) and compared against database
public string password;
// This is a Ed25519 public key which can be used to verify that
// a header was signed by a client. It will be included in the JWT
///<summary>
///This is a Ed25519 public key (base64 encoded byte array).
///Can be used to verify that a header was signed by a client.
///It will be included in the JWT </summary>
public string clientVerificationKey;
/// <summary>

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

@ -1,10 +1,14 @@
using Microsoft.EntityFrameworkCore;
using Models;
using Microsoft.Data.SqlClient;
namespace Models
{
public class PortfolioPortalDbContext : DbContext
{
public PortfolioPortalDbContext(DbContextOptions<PortfolioPortalDbContext> options) : base(options) {}
public DbSet<Portfolio> Portfolios { get; set; }
public DbSet<PortfolioData> PortfolioData { get; set; }
public DbSet<AuthLevels> AuthLevels { get; set; }
@ -13,12 +17,6 @@ namespace Models
public DbSet<User> Users { get; set; }
public DbSet<LeafHeader> LeafHeaders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//FIXME: This couldn
optionsBuilder.UseSqlServer("Server=localhost,3341;Database=PortfolioPortalTest;User Id=SA;Password=LEAF-portal-test-enviroment1;");
}
}
}

@ -1,11 +1,12 @@
using Models;
using Serilog;
using Microsoft.EntityFrameworkCore;
//TODO Make this configurable from appsettings
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/portfolioportal.txt", rollingInterval: RollingInterval.Day)
//.WriteTo.File("logs/portfolioportal.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Log.Information("Starting Portfolio Portal ASP.NET Core Backend");
@ -14,10 +15,14 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers()
.AddNewtonsoftJson();
builder.Services.AddDbContext<PortfolioPortalDbContext>();
builder.Logging.AddSerilog();
var conString = builder.Configuration.GetConnectionString("PortfolioPortalConnection");
Log.Information($"Connection string: {conString}");
builder.Services.AddDbContext<PortfolioPortalDbContext>(options =>
options.UseSqlServer(conString)
.EnableSensitiveDataLogging());
var app = builder.Build();

@ -6,13 +6,13 @@
}
},
"ConnectionStrings": {
"TestConnectionString" : "Server=localhost,3341;Database=PortfolioPortalTest;User Id=SA;Password=LEAF-portal-test-enviroment1;"
"PortfolioPortalConnection": "Server=glott.me,3341;Database=PortfolioPortalTest;User Id=SA;Password=LEAF-portal-test-enviroment1;trusted_connection=false;Encrypt=False;Persist Security Info=False"
},
"JwtSettings": {
"issuer": "LEAFPPTL-Test",
"audience": "PortfolioPartner-Test",
"SecretKey": "DEV-KEY-PortPortal",
"expirtyTime": 60
"expiryTime": 60
},
"AllowedHosts": "*",
"UploadSavePath": "../Uploaded/",

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -92,11 +92,11 @@ INSERT INTO PortfolioPortalTest.dbo.AuthLevels
VALUES('ADMIN','Admin account with no limitations.');
GO
-- My user
INSERT INTO PortfolioPortalTest.dbo.Users (Username, Email, PasswordHash, Created, Updated, CompanyID)
VALUES('glott', 'glott@leafnow.com', '$argon2i$v=19$m=65536,t=6,p=3$CAteY16ifJBZHtVjNLu/hA$plypuMy1aPww8ZfY2uGwnYk2Vzy7NvhvJy7TvgtpcPw', getdate(), getdate(), 1);
INSERT INTO PortfolioPortalTest.dbo.Users (Username, Email, PasswordHash, Created, Updated, CompanyID)
VALUES('pmolchan', 'pmolchan@leafnow.com', '$argon2i$v=19$m=65536,t=6,p=3$gE140D569YZvi+/CZbEZsg$+qunnN3wTfZx9PK++0Nv6AisvSWJ0f6lfT/UUDBzKTU', getdate(), getdate(), 1);
-- INSERT INTO PortfolioPortalTest.dbo.Users (Username, Email, PasswordHash, Created, Updated, CompanyID)
-- VALUES('glott', 'glott@leafnow.com', '$argon2i$v=19$m=65536,t=6,p=3$CAteY16ifJBZHtVjNLu/hA$plypuMy1aPww8ZfY2uGwnYk2Vzy7NvhvJy7TvgtpcPw', getdate(), getdate(), 1);
-- INSERT INTO PortfolioPortalTest.dbo.Users (Username, Email, PasswordHash, Created, Updated, CompanyID)
-- VALUES('pmolchan', 'pmolchan@leafnow.com', '$argon2i$v=19$m=65536,t=6,p=3$gE140D569YZvi+/CZbEZsg$+qunnN3wTfZx9PK++0Nv6AisvSWJ0f6lfT/UUDBzKTU', getdate(), getdate(), 1);
-- Auth my user
INSERT INTO PortfolioPortalTest.dbo.Auths (UserID, AuthLevelID)
VALUES(1, 1);
-- -- Auth my user
-- INSERT INTO PortfolioPortalTest.dbo.Auths (UserID, AuthLevelID)
-- VALUES(1, 1);

@ -40,11 +40,11 @@ CREATE TABLE Users (
PasswordHash VARCHAR(100) NOT NULL,
Created DATETIMEOFFSET NOT NULL DEFAULT GETDATE(),
Updated DATETIMEOFFSET,
PasswordUpdated DATETIMEOFFSET,
CompanyID SMALLINT FOREIGN KEY REFERENCES Company(CompanyID) NOT NULL,
PasswordUpdated DATETIMEOFFSET DEFAULT GETDATE(),
CompanyID SMALLINT FOREIGN KEY REFERENCES Company(CompanyID),--//TODO should company be optional?
LastLogin DATETIMEOFFSET,
FailedAttempts INT NOT NULL DEFAULT 0,
IsEnabled BIT NOT NULL DEFAULT 0
IsEnabled BIT NOT NULL DEFAULT 1
);
-- Link the company and users tables
ALTER TABLE Company

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

@ -22,7 +22,23 @@ export class ECCManager {
*/
constructor(privateKey, publicKey) {
this.CSK = privateKey;
this.CVK = publicKey;
this._CVK = publicKey;
}
/**
* Getter function for the CVK property. Converts the Uint8Array to a string and encodes it to base64.
* @returns {string} The CVK property as a base64 encoded string.
*/
get CVK() {
// Convert the Uint8Array to a string
const byteString = String.fromCharCode.apply(null, this._CVK);
// Encode the string to base64
return window.btoa(byteString);
}
set CVK(byteArray) {
this._CVK = byteArray;
}
/**
@ -50,7 +66,7 @@ export class ECCManager {
const signature = await ed.sign(payloadBytes, this.CSK);
//FIXME depricated function | Encode the signature using base64
const base64Signature = btoa(String.fromCharCode.apply(null, signature));
const base64Signature = window.btoa(String.fromCharCode.apply(null, signature));
// Append the X-Payload-Signature header to the given set of HTTP headers, with the value set to the base64-encoded signature
currentHeaders.append("X-Payload-Signature", base64Signature);

Loading…
Cancel
Save