-
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.
Merge pull request #36 from devdanielsun/developer
Developer
- Loading branch information
Showing
69 changed files
with
1,524 additions
and
157 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
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 |
---|---|---|
@@ -1,8 +1,8 @@ | ||
USE [pollor_db] | ||
GO | ||
|
||
INSERT INTO users (emailaddress, first_name, last_name, profile_username, created_at) | ||
VALUES ('[email protected]', 'Tester', 'Test', 'Testing', '1970-01-01T00:00:01'); | ||
INSERT INTO users (emailaddress, first_name, last_name, username, password, created_at) | ||
VALUES ('[email protected]', 'Tester', 'Test', 'Testing', '', '1970-01-01T00:00:01'); | ||
GO | ||
|
||
INSERT INTO polls (user_id, question, ending_date, created_at) | ||
|
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,196 @@ | ||
using System.IdentityModel.Tokens.Jwt; | ||
using System.Security.Claims; | ||
using System.Text; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.IdentityModel.Tokens; | ||
using Microsoft.AspNetCore.Identity; | ||
using pollor.Server.Models; | ||
using pollor.Server.Services; | ||
using Microsoft.EntityFrameworkCore.ChangeTracking; | ||
using System.Security.Principal; | ||
using Microsoft.EntityFrameworkCore.Metadata.Internal; | ||
using Microsoft.AspNetCore.Authorization; | ||
using pollor.Server.Controllers; | ||
using System.Data; | ||
|
||
[Route("api/auth")] | ||
[ApiController] | ||
public class AuthController : ControllerBase | ||
{ | ||
private readonly ILogger<AuthController> _logger; | ||
|
||
public AuthController(ILogger<AuthController> logger) | ||
{ | ||
_logger = logger; | ||
} | ||
|
||
|
||
[HttpPost("register")] | ||
public IActionResult Register([FromBody] RegisterModel registerUser) | ||
{ | ||
if (registerUser is null) | ||
{ | ||
return BadRequest(new { message = "Invalid client request" }); | ||
} | ||
|
||
if (registerUser.password!.Length < 8) { | ||
return BadRequest(new { message = "Password must be longer than 8 characters." }); | ||
} | ||
|
||
bool isUsernameAvailable = new PollorDbContext().UserAuthModel.Where(u => u.username!.ToLower().Equals(registerUser.username!.ToLower())).IsNullOrEmpty(); | ||
if (isUsernameAvailable == false) { | ||
return BadRequest(new { message = "Username is already taken, please login or use another username." }); | ||
} | ||
|
||
bool isEmailAvailable = new PollorDbContext().UserAuthModel.Where(u => u.emailaddress!.ToLower().Equals(registerUser.emailaddress!.ToLower())).IsNullOrEmpty(); | ||
if (isEmailAvailable == false) { | ||
return BadRequest(new { message = "Emailaddress is already taken, please login or use another emailaddress." }); | ||
} | ||
|
||
var hasher = new PasswordHasher<RegisterModel>(); | ||
var hashedPass = hasher.HashPassword(registerUser, registerUser.password!); | ||
UserAuthModel tempUser = new UserAuthModel() { | ||
username = registerUser.username, | ||
password = hashedPass, | ||
emailaddress = registerUser.emailaddress, | ||
created_at = DateTime.Now, | ||
}; | ||
|
||
try | ||
{ | ||
using (var context = new PollorDbContext()) | ||
{ | ||
// Create new user | ||
EntityEntry<UserAuthModel> createdUser = context.UserAuthModel.Add(tempUser); | ||
context.SaveChanges(); | ||
|
||
// Get full user data | ||
UserModel? newUser = context.Users | ||
.Where(u => u.id.Equals(createdUser.Entity.id) && | ||
u.username!.Equals(createdUser.Entity.username) && | ||
u.emailaddress!.Equals(createdUser.Entity.emailaddress)) | ||
.FirstOrDefault(); | ||
if (newUser == null) | ||
{ | ||
return NotFound(new { message = "User not found..." }); | ||
} | ||
var tokeOptions = GetJwtTokenOptions(1, newUser); | ||
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions); | ||
return Created("user/" + newUser.id, new AuthenticatedResponse { token = tokenString, user = newUser }); | ||
|
||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
_logger.LogError(ex, ex.Message); | ||
return StatusCode(500, new { message = ex.Message }); | ||
} | ||
} | ||
|
||
|
||
[HttpPost("login")] | ||
public IActionResult Login([FromBody] LoginModel loginUser) | ||
{ | ||
if (loginUser is null) | ||
{ | ||
return BadRequest(new { message = "Invalid client request" }); | ||
} | ||
|
||
var authUser = new PollorDbContext().UserAuthModel.Where(u => u.username!.ToLower().Equals(loginUser.username!.ToLower())).FirstOrDefault(); | ||
if (authUser == null) { | ||
return Unauthorized(new { message = "Username or password is wrong!" }); | ||
} | ||
|
||
var hasher = new PasswordHasher<LoginModel>(); | ||
PasswordVerificationResult passwordIsOk = hasher.VerifyHashedPassword(loginUser, authUser.password!, loginUser.password!); | ||
|
||
if (passwordIsOk == PasswordVerificationResult.Failed) { | ||
return Unauthorized(new { message = "Username or password is wrong!" }); | ||
} | ||
|
||
if (authUser.username == loginUser.username && (passwordIsOk == PasswordVerificationResult.Success || passwordIsOk == PasswordVerificationResult.SuccessRehashNeeded)) | ||
{ | ||
if (passwordIsOk == PasswordVerificationResult.SuccessRehashNeeded) { | ||
// rehash password and save to DB | ||
_logger.LogError("Rehash password and save to DB"); | ||
} | ||
|
||
int tokenLongerValid = (bool)loginUser.tokenLongerValid ? 31 : 1;// true = 31, false = 1 | ||
var currentUser = new PollorDbContext().Users.Where(u => u.username!.ToLower().Equals(authUser.username!.ToLower())).FirstOrDefault(); | ||
var tokenOptions = GetJwtTokenOptions(tokenLongerValid, currentUser!); | ||
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions); | ||
|
||
return Ok(new AuthenticatedResponse { token = tokenString, user = currentUser}); | ||
} | ||
|
||
return Unauthorized(new { message = "something went wrong" } ); | ||
} | ||
|
||
[HttpPost("validate")] | ||
[Authorize] | ||
public IActionResult Validate([FromBody] ValidateTokenModel validateTokenModel) | ||
{ | ||
// Retrieve the JWT token from the Authorization header | ||
var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); | ||
|
||
if (string.IsNullOrEmpty(token)) | ||
{ | ||
return BadRequest(new { message = "Token is missing" }); | ||
} | ||
|
||
SecurityToken validatedToken; | ||
IPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(token, AuthService.GetValidationParameters(), out validatedToken); | ||
|
||
// The user is authenticated, and you can access user information | ||
var userClaims = HttpContext.User; | ||
// Perform additional validation or return success response | ||
|
||
var username = userClaims.Claims.Where(e => e.Type.EndsWith("identity/claims/name")).Select(e => e.Value).SingleOrDefault(); | ||
var userId = userClaims.Claims.Where(e => e.Type.EndsWith("identity/claims/nameidentifier")).Select(e => e.Value).SingleOrDefault(); | ||
var userRole = userClaims.Claims.Where(e => e.Type.EndsWith("identity/claims/role")).Select(e => e.Value).SingleOrDefault(); | ||
|
||
try | ||
{ | ||
using (var context = new PollorDbContext()) | ||
{ | ||
UserModel? user = context.Users | ||
.Where(u => u.id.ToString().Equals(userId) && | ||
u.username!.Equals(username) && | ||
u.role!.Equals(userRole)) | ||
.FirstOrDefault(); | ||
if (user == null) | ||
{ | ||
return NotFound(new { message = "User not found..." }); | ||
} | ||
return Ok(new AuthenticatedResponse { token = validateTokenModel.token, user = user }); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
_logger.LogError(ex, ex.Message); | ||
return StatusCode(500, new { message = ex.Message }); | ||
} | ||
} | ||
|
||
private JwtSecurityToken GetJwtTokenOptions (int tokenValidForXDays, UserModel user) { | ||
var jwtClaims = new List<Claim> | ||
{ | ||
new Claim(ClaimTypes.Name, user.username!), | ||
new Claim(ClaimTypes.NameIdentifier, user.id.ToString()), | ||
new Claim(ClaimTypes.Role, user.role!) | ||
}; | ||
|
||
String jwtTokenDomain = Environment.GetEnvironmentVariable("JWT_TOKEN_DOMAIN")!.Split(',').FirstOrDefault()!; | ||
|
||
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("SECRET_JWT_KEY")!)); | ||
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); | ||
var tokeOptions = new JwtSecurityToken( | ||
issuer: jwtTokenDomain, | ||
audience: jwtTokenDomain, | ||
claims: jwtClaims, | ||
expires: DateTime.Now.AddDays(tokenValidForXDays), | ||
signingCredentials: signinCredentials | ||
); | ||
return tokeOptions; | ||
} | ||
} |
Oops, something went wrong.