public void NewUserToken_InvalidUsername_ThrowsSystemException() { var sut = new GenerateJwtToken(_key, _getUserAuth.Object); var testUser = new User(); Assert.That(() => sut.NewUserToken(testUser), Throws.Exception); }
public void Authenticate_NonExistingUser_ThrowsNonExistingUserException() { var randomUsername = TestData.CreateRandomString(); var randomPassword = TestData.CreateRandomString(); _getUserAuth.Setup(x => x.Get(randomUsername)).Returns((User)null); var sut = new GenerateJwtToken(_key, _getUserAuth.Object); Assert.That(() => sut.Authenticate(randomUsername, randomPassword), Throws.Exception.TypeOf <NonExistingUserException>()); }
public void NewUserToken_WhenCalled_ReturnsJwtToken() { var sut = new GenerateJwtToken(_key, _getUserAuth.Object); var testUser = new User { Username = TestData.CreateRandomString() }; var jwtToken = sut.NewUserToken(testUser); Assert.That(jwtToken, Is.TypeOf <string>()); }
public void Authenticate_InvalidPassword_ThrowsUserValidationException() { var randomUsername = TestData.CreateRandomString(); var randomPassword = TestData.CreateRandomString(); var testUser = new User { Password = BCrypt.Net.BCrypt.HashPassword(TestData.CreateRandomString()), Username = randomUsername }; _getUserAuth.Setup(x => x.Get(randomUsername)).Returns(testUser); var sut = new GenerateJwtToken(_key, _getUserAuth.Object); Assert.That(() => sut.Authenticate(randomUsername, randomPassword), Throws.Exception.TypeOf <UserValidationException>()); }
public void Authenticate_ValidInput_ReturnsJwtToken() { var randomUsername = TestData.CreateRandomString(); var randomPassword = TestData.CreateRandomString(); var testUser = new User { Password = BCrypt.Net.BCrypt.HashPassword(randomPassword), Username = randomUsername }; _getUserAuth.Setup(x => x.Get(randomUsername)).Returns(testUser); var sut = new GenerateJwtToken(_key, _getUserAuth.Object); var jwtToken = sut.Authenticate(randomUsername, randomPassword); Assert.That(jwtToken, Is.TypeOf <string>()); }
public async Task <AuthenticationResponse> Handle(AuthenticateUserCommand request, CancellationToken cancellationToken) { // Validate our input AuthenticateUserValidator validator = new AuthenticateUserValidator(); ValidationResult validationResult = validator.Validate(request); if (!validationResult.IsValid) { return(new AuthenticationResponse(validationResult.Errors) { Message = "Please include both a username/email and a password" }); } #region Setup our caching client and key IDatabase cache = _redisContext.ConnectionMultiplexer.GetDatabase(); var attemptsKey = Common.Constants.CachingKeys.LoginAttempts(request.UserNameOrEmail); var attemptsCount = 0; #endregion #region Check lockouts and login attempts (Redis Cache) // Check attempts count on this username/email var attemptsCacheValue = cache.StringGet(attemptsKey); if (attemptsCacheValue.HasValue) { attemptsCount = Convert.ToInt32(attemptsCacheValue); if (attemptsCount >= _coreConfiguration.Logins.MaxAttemptsBeforeLockout) { return(new AuthenticationResponse { Message = "Too many attempts!" }); } } #endregion //========================================================================= // DETERMINE AUTHENTICATION TYPE AND QUERY THE DOCUMENT STORE //========================================================================= #region get user from document store var authenticationType = "NameKey"; var authenticationQuery = Common.Transformations.NameKey.Transform(request.UserNameOrEmail); if (request.UserNameOrEmail.Contains("@") && request.UserNameOrEmail.Contains(".")) { authenticationType = "Email"; authenticationQuery = request.UserNameOrEmail.ToLower().Trim(); } // Create the query string sqlQuery = $"SELECT * FROM Documents d WHERE d.{ authenticationType } ='{ authenticationQuery }'"; var sqlSpec = new SqlQuerySpec { QueryText = sqlQuery }; // Generate collection uri Uri collectionUri = UriFactory.CreateDocumentCollectionUri(_documentContext.Settings.Database, _documentContext.Settings.Collection); // Generate FeedOptions/ParitionKey var feedOptions = new FeedOptions { PartitionKey = new PartitionKey(Common.Constants.DocumentType.User()) }; // Run query against the document store var getResult = _documentContext.Client.CreateDocumentQuery <UserDocumentModel>( collectionUri, sqlSpec, feedOptions ); var userDocumentModel = getResult.AsEnumerable().FirstOrDefault(); if (userDocumentModel == null) { #region Update login attempts (Redis Cache) cache.StringSet(attemptsKey, (attemptsCount + 1), TimeSpan.FromHours(_coreConfiguration.Logins.LockoutTimespanHours), When.Always, CommandFlags.FireAndForget); #endregion return(new AuthenticationResponse { Message = "Incorrect credentials" }); } #endregion //========================================================================= // DETERMINE IF THE PASSWORD IS CORRECT //========================================================================= var authenticationGranted = Common.Encryption.PasswordHashing.ValidatePassword(request.Password, userDocumentModel.PasswordHash, userDocumentModel.PasswordSalt); if (!authenticationGranted) { #region Update login attempts (Redis Cache) cache.StringSet(attemptsKey, (attemptsCount + 1), TimeSpan.FromHours(_coreConfiguration.Logins.LockoutTimespanHours), When.Always, CommandFlags.FireAndForget); #endregion return(new AuthenticationResponse { Message = "Incorrect credentials" }); } else { #region Clear login attempts (Redis Cache) if (attemptsCount > 0) { cache.KeyDelete(attemptsKey, CommandFlags.FireAndForget); } #endregion //========================================================================= // // BUILD THE JWT TOKEN, REFRESH TOKEN AND RETURN RESULTS // //========================================================================= var jwtTokenString = GenerateJwtToken.GenerateJwtTokenString( _coreConfiguration, userDocumentModel.Id, userDocumentModel.UserName, userDocumentModel.NameKey, userDocumentModel.Email, userDocumentModel.FirstName, userDocumentModel.LastName, userDocumentModel.Roles ); // Generate refresh token var refreshToken = string.Empty; try { refreshToken = await _mediator.Send(new GenerateRefreshTokenCommand { UserId = userDocumentModel.Id }); } catch (Exception ex) { Log.Error("There was an error generating a refresh token for {loginString} during authentication {@ex}", request.UserNameOrEmail, ex); } //========================================================================= // UPDATE LAST LOGIN DATETIME ON USER //========================================================================= #region Update Last Login DateTime // Update field: userDocumentModel.LastLoginDate = DateTime.UtcNow; var documentUri = UriFactory.CreateDocumentUri( _documentContext.Settings.Database, _documentContext.Settings.Collection, userDocumentModel.Id); ResourceResponse <Document> result; try { // Save the document to document store using the IDocumentContext dependency result = await _documentContext.Client.ReplaceDocumentAsync( documentUri, userDocumentModel, new RequestOptions { PartitionKey = new PartitionKey(Common.Constants.DocumentType.User().ToString()) } ); } catch (Exception ex) { // throw DocumentStoreException (if a custom exception type is desired) // ... Will be caught, logged and handled by the ExceptionHandlerMiddleware // Pass exception up the chain: throw ex; // ...Or pass along as inner exception: //throw new Exception("An error occured trying to use the document store", ex); } finally { // Close any open connections, etc... } #endregion //========================================================================= // LOG THE ACTIVITY //========================================================================= var user = AutoMapper.Mapper.Map <Core.Domain.Entities.User>(userDocumentModel); Log.Information("User authenticated {@user}", user); return(new AuthenticationResponse { isSuccess = true, JwtToken = jwtTokenString, RefreshToken = refreshToken, User = user, Message = "Authentication succeeded" }); } }
public async Task <AuthenticationResponse> Handle(AuthenticateRefreshTokenCommand request, CancellationToken cancellationToken) { // Validate our input AuthenticateRefreshTokenValidator validator = new AuthenticateRefreshTokenValidator(); ValidationResult validationResult = validator.Validate(request); if (!validationResult.IsValid) { return(new AuthenticationResponse(validationResult.Errors) { Message = "Validation issues" }); } //====================================================== // QUERY THE DOCUMENT STORE FOR BOTH THE REFRESH TOKEN //====================================================== #region get request token from document store // Create the query string sqlQuery = $"SELECT * FROM Documents d WHERE d.id ='{ request.RefreshToken }'"; var sqlSpec = new SqlQuerySpec { QueryText = sqlQuery }; // Generate collection uri Uri collectionUri = UriFactory.CreateDocumentCollectionUri(_documentContext.Settings.Database, _documentContext.Settings.Collection); // Generate FeedOptions/ParitionKey var feedOptions = new FeedOptions { PartitionKey = new PartitionKey(Common.Constants.DocumentType.RefreshToken()) }; // Run query against the document store var getResult = _documentContext.Client.CreateDocumentQuery <RefreshTokenDocumentModel>( collectionUri, sqlSpec, feedOptions ); var refreshDocumentModel = getResult.AsEnumerable().FirstOrDefault(); #endregion //========================================================================= // DETERMINE IF THE TOKEN EXISTS AND IS NOT EXPIRED //========================================================================= if (refreshDocumentModel == null) { return(new AuthenticationResponse { Message = "Invalid Token" }); } else if (refreshDocumentModel.CreatedDate.AddHours(_coreConfiguration.JSONWebTokens.RefreshTokenExpirationHours) <= DateTime.UtcNow) { // Delete the expired refresh token and return as expired await _mediator.Send(new DeleteRefreshTokenCommand { Id = request.RefreshToken }); return(new AuthenticationResponse { Message = "Expired Token" }); } else { //========================================================================= // TOKEN IS VALID, BUILD OUR AUTHENTICATION RESPONSE //========================================================================= // Get the user var user = await _mediator.Send(new GetUserByIdQuery { Id = refreshDocumentModel.UserId }); //========================================================================= // // BUILD THE JWT TOKEN, REFRESH TOKEN AND RETURN RESULTS // //========================================================================= if (user == null) { return(new AuthenticationResponse { Message = "Invalid Token" }); } var jwtTokenString = GenerateJwtToken.GenerateJwtTokenString( _coreConfiguration, user.Id.ToString(), user.UserName, user.NameKey, user.Email, user.FirstName, user.LastName, user.Roles ); var refreshToken = string.Empty; try { // Generate new refresh token refreshToken = await _mediator.Send(new GenerateRefreshTokenCommand { UserId = user.Id.ToString() }); // Delete this refresh token await _mediator.Send(new DeleteRefreshTokenCommand { Id = request.RefreshToken }); } catch (Exception ex) { Log.Error("There was an error generating a followup refresh token for user:{userId} during authentication {@ex}", user.Id.ToString(), ex); } return(new AuthenticationResponse { isSuccess = true, JwtToken = jwtTokenString, RefreshToken = refreshToken, User = user, Message = "Authentication succeeded" }); } }