-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Matthew
committed
Nov 13, 2024
1 parent
9a7d92a
commit 3f5566a
Showing
18 changed files
with
503 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.