Skip to content

Commit

Permalink
Basic register and login working
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew committed Nov 13, 2024
1 parent 9a7d92a commit 3f5566a
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 39 deletions.
46 changes: 38 additions & 8 deletions Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using SecureDesignProject.Extensions;
using SecureDesignProject.Models;
using SecureDesignProject.Services;


namespace SecureDesignProject.Controllers;

public class AuthController : Controller
public class AuthController(ILogger<HomeController> logger, AuthService authService) : Controller
{
private readonly ILogger<HomeController> _logger;

public AuthController(ILogger<HomeController> logger)
{
_logger = logger;
}

public IActionResult Login()
{
return View();
Expand All @@ -23,6 +19,40 @@ public IActionResult Register()
return View();
}

[HttpPost]
public IActionResult Login([FromForm] LoginDetails loginDetails)
{
var loginResult = authService.AttemptLogin(loginDetails);

if (!loginResult.success)
{
TempData["errorMsg"] = "Invalid email or password";

return View();
}

Response.SetSessionKeyCookie(loginResult.sessionKey);

return RedirectToAction("Index", "Home");
}

[HttpPost]
public IActionResult Register([FromForm] RegisterDetails registerDetails)
{
var createAccountResult = authService.AttemptCreateAccount(registerDetails);

if (!createAccountResult.success)
{
TempData["errorMsg"] = "Could not create account with that email.";

return View();
}

Response.SetSessionKeyCookie(createAccountResult.SessionKey);

return RedirectToAction("Index", "Home");
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
Expand Down
15 changes: 15 additions & 0 deletions Extensions/HttpExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace SecureDesignProject.Extensions;

public static class HttpExtensions
{
public static void SetSessionKeyCookie(this HttpResponse response, byte[] sessionKey)
{
var options = new CookieOptions
{
Expires = DateTime.Now.AddMinutes(60),
HttpOnly = true
};
response.Cookies.Append("session_key", Convert.ToHexString(sessionKey), options);

}
}
6 changes: 6 additions & 0 deletions Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace SecureDesignProject.Extensions;

public static class StringExtensions
{
public static bool IsNullOrWhiteSpace(this string? value) => string.IsNullOrWhiteSpace(value);
}
45 changes: 45 additions & 0 deletions Middleware/AuthMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using SecureDesignProject.Extensions;
using SecureDesignProject.Services;

namespace SecureDesignProject.Middleware;


public class AuthMiddleware(RequestDelegate next, AuthService authService)
{
public async Task InvokeAsync(HttpContext context)
{
// skip check and call next middleware if /auth path
if (context.Request.Path.StartsWithSegments("/auth"))
{
await next(context);

return;
}

// redirect to login page if invalid token
var sessionKey = Convert.FromHexString(context.Request.Cookies["session_key"] ?? "");
if (sessionKey.Length == 0)
{
context.Response.Redirect("/auth/login");
return;
}

if (!authService.IsValidSession(sessionKey!))
{
context.Response.Redirect("/auth/login");
return;
}

// Call the next delegate/middleware in the pipeline.
await next(context);
}
}

public static class AuthMiddlewareExtensions
{
public static IApplicationBuilder UseAuthMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<AuthMiddleware>();
}
}
4 changes: 2 additions & 2 deletions Migrations/1_create_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ GO
CREATE TABLE Accounts(
AccountId UNIQUEIDENTIFIER NOT NULL,
Email NVARCHAR(MAX) NOT NULL UNIQUE,
HashedPassword VARCHAR(MAX) NOT NULL,
HashedPassword BINARY(256) NOT NULL,

PRIMARY KEY (AccountId)
);
Expand Down Expand Up @@ -81,7 +81,7 @@ CREATE TABLE Sessions(
SessionId UNIQUEIDENTIFIER NOT NULL,
AccountId UNIQUEIDENTIFIER NOT NULL,

Token VARCHAR(MAX) NOT NULL,
SessionKey BINARY(256) NOT NULL,
IsValid BIT NOT NULL,

Created DATETIME NOT NULL,
Expand Down
2 changes: 2 additions & 0 deletions Migrations/2_add_seperate_salt_col.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE Accounts
ADD Salt BINARY(128) NOT NULL DEFAULT 123;
20 changes: 0 additions & 20 deletions Models/Auth.cs

This file was deleted.

68 changes: 68 additions & 0 deletions Models/AuthModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.ComponentModel.DataAnnotations;

namespace SecureDesignProject.Models;

public record LoginDetails
{
[Required]
[MaxLength(255)]
public string Email { get; init; } = "";

[Required]
[MaxLength(255)]
public string Password { get; init; } = "";
};

public record RegisterDetails
{
[Required]
[MaxLength(255)]
public string Email { get; init; } = "";

[Required]
[MaxLength(255)]
[MinLength(8)]
public string Password { get; init; } = "";

[Required]
[MaxLength(255)]
public string FirstName { get; init; } = "";

[Required]
[MaxLength(255)]
public string LastName { get; init; } = "";

[Required]
public int InviteCode { get; init; }
};

public record Account
{
public Guid AccountId { get; init; } = Guid.NewGuid();
public string Email { get; init; }
public byte[] HashedPassword { get; init; }

public byte[] Salt { get; init; }
}

public record Patient
{
public Guid PatientId { get; init; } = Guid.NewGuid();
public Guid AccountId { get; init; }
public string FirstName { get; init; } = "";
public string LastName { get; init; } = "";
public string? Address { get; init; }
}

public record Session
{
public Guid SessionId { get; init; }
public Guid AccountId { get; init; }

public bool IsValid { get; init; }
public byte[] SessionKey { get; init; }

public DateTime Created { get; init; }
public DateTime LastSeen { get; init; }
public DateTime Expires { get; init; }
}
7 changes: 6 additions & 1 deletion Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
using SecureDesignProject.Middleware;
using SecureDesignProject.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<DatabaseService>();
builder.Services.AddSingleton<AuthService>();

var app = builder.Build();

Expand All @@ -18,7 +23,7 @@

app.UseRouting();

app.UseAuthorization();
app.UseAuthMiddleware();

app.MapControllerRoute(
name: "default",
Expand Down
2 changes: 1 addition & 1 deletion Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7049;http://localhost:5133",
"applicationUrl": "https://localhost:7049",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
5 changes: 4 additions & 1 deletion SecureDesignProject.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
</PropertyGroup>

<ItemGroup>
<Folder Include="Views\Auth\" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
</ItemGroup>

</Project>
102 changes: 102 additions & 0 deletions Services/AuthService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System.Security.Cryptography;
using System.Text;
using SecureDesignProject.Models;
using Konscious.Security.Cryptography;

namespace SecureDesignProject.Services;

public class AuthService(DatabaseService dbService)
{
public (bool success, byte[] sessionKey) AttemptLogin(LoginDetails loginDetails)
{
var account = dbService.GetAccountByEmail(loginDetails.Email);

if (account == null || VerifyHash(loginDetails.Password, account.HashedPassword, account.Salt))
{
return (false, []);
}

var newSession = dbService.InsertSession(new Session
{
SessionId = Guid.NewGuid(),
AccountId = account.AccountId,

SessionKey = RandomNumberGenerator.GetBytes(255),
IsValid = true,

Created = DateTime.Now,
LastSeen = DateTime.Now,
Expires = DateTime.Now.AddMinutes(10),
});

return (true, newSession.SessionKey);
}

public bool IsValidSession(byte[] key)
{
var session = dbService.GetSessionByKey(key);

return session != null;
}

public (bool success, byte[] SessionKey) AttemptCreateAccount(RegisterDetails registerDetails)
{
var existingAccount = dbService.GetAccountByEmail(registerDetails.Email);

if (existingAccount != null)
{
return (false, []);
}

var salt = GenerateSalt();
var account = dbService.InsertAccount(new Account
{
Email = registerDetails.Email,
HashedPassword = HashPassword(registerDetails.Password, salt),
});



var patient = dbService.InsertPatient(new Patient
{
FirstName = registerDetails.FirstName,
LastName = registerDetails.LastName,
AccountId = account.AccountId,
Address = null
});


//login new user
var (success, sessionKey) = AttemptLogin(new LoginDetails
{
Email = registerDetails.Email,
Password = registerDetails.Password
});

return (success, sessionKey);
}

private static byte[] HashPassword(string password, byte[] salt)
{
var argon2Id = new Argon2id(Encoding.UTF8.GetBytes(password));
argon2Id.Salt = salt;
argon2Id.Iterations = 4;
argon2Id.MemorySize = 64000;
argon2Id.DegreeOfParallelism = 1; // num of threads

var hash = argon2Id.GetBytes(64); // 64 byte hash

return hash;
}

private static byte[] GenerateSalt()
{
return RandomNumberGenerator.GetBytes(128);
}

private static bool VerifyHash(string password, byte[] hash, byte[] salt)
{
var newHash = HashPassword(password, salt);
return hash.SequenceEqual(newHash);
}
}
Loading

0 comments on commit 3f5566a

Please sign in to comment.