public async Task ValidateAsync_Should_Set_Success_GrantValidationResult() { var account = Account.Builder() .SetId(Guid.NewGuid()) .SetEmail("*****@*****.**") .SetConfirmed(true) .SetPasswordHash("PasswordHash") .SetSecurityStamp(Guid.NewGuid()) .SetCreated(DateTimeOffset.UtcNow) .SetRoles(new List <Guid> { Guid.NewGuid() }) .Build(); var getAccountResult = GetResult <Account> .Ok(account); var accountCanBeAuthenticatedResult = VerificationResult.Ok(); var accountClaims = new List <Claim> { new Claim(JwtClaimTypes.Subject, account.Id.ToString()), new Claim(JwtClaimTypes.Email, account.Email), new Claim(JwtClaimTypes.EmailVerified, account.Confirmed.ToString(), ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Scope, "Scope"), new Claim(ClaimTypes.Name, account.Email), new Claim(ClaimTypes.NameIdentifier, account.Id.ToString()) }; var context = new ResourceOwnerPasswordValidationContext { Request = new ValidatedTokenRequest { GrantType = GrantType.ResourceOwnerPassword }, Password = "******", UserName = account.Email }; _accountGetterServiceMock.Setup(x => x.GetByEmailAsync(It.IsAny <string>())) .ReturnsAsync(getAccountResult); _accountVerificationServiceMock .Setup(x => x.VerifyAccountCanBeAuthenticated(It.IsAny <Account>(), It.IsAny <string>())) .Returns(accountCanBeAuthenticatedResult); _accountClaimsCreatorServiceMock.Setup(x => x.CreateAccountClaimsAsync(It.IsAny <Account>())).ReturnsAsync(accountClaims); Func <Task> result = async() => await _resourceOwnerPasswordValidator.ValidateAsync(context); await result.Should().NotThrowAsync <Exception>(); _accountGetterServiceMock.Verify(x => x.GetByEmailAsync(It.Is <string>(e => e == context.UserName)), Times.Once); _accountVerificationServiceMock.Verify(x => x.VerifyAccountCanBeAuthenticated(It.Is <Account>(a => a == account), It.Is <string>(p => p == context.Password)), Times.Once); _accountClaimsCreatorServiceMock.Verify(x => x.CreateAccountClaimsAsync(It.Is <Account>(a => a == account)), Times.Once); }
public async Task <IdentityLoginOutput> Login([FromBody] IdentityLoginInput model) { var userContext = new ResourceOwnerPasswordValidationContext { UserName = model.Login, Password = model.Password }; await _usersValidator.ValidateAsync(userContext); if (!userContext.Result.IsError) { await _events.RaiseAsync(new UserLoginSuccessEvent(model.Login, userContext.Result.Subject.ToString(), model.Login)); // issue authentication cookie with subject ID and username var userId = userContext.Result.Subject.Claims.FirstOrDefault(x => x.Type == "sub"); var user = UserRepository.FirstOrDefault(us => !us.IsDeleted && us.Id == Guid.Parse(userId.Value)); if (user != null) { var claims = new[] { new Claim(IdentityClaims.Email, user.Email), new Claim(IdentityClaims.FirstName, user.FirstName), new Claim(IdentityClaims.LastName, user.SecondName) }; await HttpContext.SignInAsync(userId != null?userId.Value : "", user.UserName, (AuthenticationProperties)null, claims); } return(await Task.FromResult(new IdentityLoginOutput { LoginResult = LogInStatus.Validated })); } await _events.RaiseAsync(new UserLoginFailureEvent(model.Login, "invalid credentials")); switch (Enum.Parse <LogInStatus>(userContext.Result.Error)) { case LogInStatus.IncorrectUserOrPassword: case LogInStatus.UserInactive: case LogInStatus.UnknownError: case LogInStatus.UserBlocked: return(await Task.FromResult(new IdentityLoginOutput { LoginResult = Enum.Parse <LogInStatus>(userContext.Result.Error) })); default: { return(await Task.FromResult(new IdentityLoginOutput { LoginResult = LogInStatus.UnknownError, Message = userContext.Result.Error })); } } }
public async Task <IActionResult> Login(LoginInputModel model) { if (ModelState.IsValid) { // validate username/password against in-memory store var validationContext = new ResourceOwnerPasswordValidationContext { UserName = model.Username, Password = model.Password }; await _passwordValidator.ValidateAsync(validationContext); if (!validationContext.Result.IsError) { AuthenticationProperties props = null; // only set explicit expiration here if persistent. // otherwise we reply upon expiration configured in cookie middleware. if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; } ; // issue authentication cookie with subject ID and username var user = await _userStore.GetUserByLoginAsync(LoginProvider.UserNamePassword, model.Username); var userId = user.UserId; await HttpContext.Authentication.SignInAsync(userId, model.Username, props); _appMonitor.LogEvent("LoginSucceeded", properties: new Dictionary <string, string> { { "LoginType", "interactive" }, { "LoginSubType", "local" } }); // make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint if (_interaction.IsValidReturnUrl(model.ReturnUrl)) { return(Redirect(model.ReturnUrl)); } return(Redirect("~/")); } ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await _account.BuildLoginViewModelAsync(model); return(View(vm)); }
public async Task <IActionResult> Login(LoginInputModel model) { if (ModelState.IsValid) { var vc = new ResourceOwnerPasswordValidationContext { UserName = model.Username, Password = model.Password }; // validate username/password against in-memory store await _resOwnerValidator.ValidateAsync(vc); if (vc.Result != null && !vc.Result.IsError) { AuthenticationProperties props = null; // only set explicit expiration here if persistent. // otherwise we reply upon expiration configured in cookie middleware. if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; } ; // issue authentication cookie with subject ID and username var user = await _userRepo.FindAsync(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Email, user.SubjectId, user.FullName)); await HttpContext.Authentication.SignInAsync(user.SubjectId, user.Email, props); // make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint or a local page if (_interaction.IsValidReturnUrl(model.ReturnUrl) || Url.IsLocalUrl(model.ReturnUrl)) { return(Redirect(model.ReturnUrl)); } return(Redirect("~/")); } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials")); ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await _account.BuildLoginViewModelAsync(model); return(View(vm)); }
public async Task <IActionResult> Authenticate( [FromBody] SigninModel signinModel, [FromServices] IResourceOwnerPasswordValidator resourceOwnerPasswordValidator, [FromServices] ISystemClock systemClock ) { var contx = new ResourceOwnerPasswordValidationContext { UserName = signinModel.Username, Password = signinModel.Password }; await resourceOwnerPasswordValidator.ValidateAsync(contx); if (!contx.Result.IsError) { await HttpContext.SignInAsync(contx.Result.Subject.GetSubjectId(), contx.Result.Subject.Claims.ToArray()); return(NoContent()); } return(this.Unauthorized()); }
private async Task <TokenRequestValidationResult> ValidateResourceOwnerCredentialRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start resource owner password token request validation"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.Contains(GrantType.ResourceOwnerPassword)) { LogError("{clientId} not authorized for resource owner flow", _validatedRequest.Client.ClientId); return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient)); } ///////////////////////////////////////////// // check if client is allowed to request scopes ///////////////////////////////////////////// if (!(await ValidateRequestedScopesAsync(parameters))) { return(Invalid(OidcConstants.TokenErrors.InvalidScope)); } ///////////////////////////////////////////// // check resource owner credentials ///////////////////////////////////////////// var userName = parameters.Get(OidcConstants.TokenRequest.UserName); var password = parameters.Get(OidcConstants.TokenRequest.Password); if (userName.IsMissing() || password.IsMissing()) { LogError("Username or password missing"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (userName.Length > _options.InputLengthRestrictions.UserName || password.Length > _options.InputLengthRestrictions.Password) { LogError("Username or password too long"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.UserName = userName; ///////////////////////////////////////////// // authenticate user ///////////////////////////////////////////// var resourceOwnerContext = new ResourceOwnerPasswordValidationContext { UserName = userName, Password = password, Request = _validatedRequest }; await _resourceOwnerValidator.ValidateAsync(resourceOwnerContext); if (resourceOwnerContext.Result.IsError) { if (resourceOwnerContext.Result.Error == OidcConstants.TokenErrors.UnsupportedGrantType) { LogError("Resource owner password credential grant type not supported"); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "password grant type not supported"); return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType, customResponse: resourceOwnerContext.Result.CustomResponse)); } var errorDescription = "invalid_username_or_password"; if (resourceOwnerContext.Result.ErrorDescription.IsPresent()) { errorDescription = resourceOwnerContext.Result.ErrorDescription; } LogError("User authentication failed: {error}", errorDescription ?? resourceOwnerContext.Result.Error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, errorDescription); return(Invalid(OidcConstants.TokenErrors.InvalidGrant, errorDescription, resourceOwnerContext.Result.CustomResponse)); } if (resourceOwnerContext.Result.Subject == null) { var error = "User authentication failed: no principal returned"; LogError(error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext(resourceOwnerContext.Result.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.ResourceOwnerValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { LogError("User has been disabled: {subjectId}", resourceOwnerContext.Result.Subject.GetSubjectId()); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "user is inactive"); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.UserName = userName; _validatedRequest.Subject = resourceOwnerContext.Result.Subject; await RaiseSuccessfulResourceOwnerAuthenticationEventAsync(userName, resourceOwnerContext.Result.Subject.GetSubjectId()); _logger.LogDebug("Resource owner password token request validation success."); return(Valid(resourceOwnerContext.Result.CustomResponse)); }
private async Task <TokenRequestValidationResult> ValidateResourceOwnerCredentialRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start resource owner password token request validation"); // if we've disabled local authentication, then fail if (_options.AuthenticationOptions.EnableLocalLogin == false || _validatedRequest.Client.EnableLocalLogin == false) { LogError("EnableLocalLogin is disabled, failing with UnsupportedGrantType"); return(Invalid(OidcConstants.TokenErrors.UnsupportedGrantType)); } ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.ResourceOwnerPassword)) { LogError("Client not authorized for resource owner flow"); return(Invalid(OidcConstants.TokenErrors.UnauthorizedClient)); } ///////////////////////////////////////////// // check if client is allowed to request scopes ///////////////////////////////////////////// if (!(await ValidateRequestedScopesAsync(parameters))) { LogError("Invalid scopes."); return(Invalid(OidcConstants.TokenErrors.InvalidScope)); } ///////////////////////////////////////////// // check resource owner credentials ///////////////////////////////////////////// var userName = parameters.Get(OidcConstants.TokenRequest.UserName); var password = parameters.Get(OidcConstants.TokenRequest.Password); if (userName.IsMissing() || password.IsMissing()) { LogError("Username or password missing."); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } if (userName.Length > _options.InputLengthRestrictions.UserName || password.Length > _options.InputLengthRestrictions.Password) { LogError("Username or password too long."); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.UserName = userName; ///////////////////////////////////////////// // authenticate user ///////////////////////////////////////////// var resourceOwnerResult = await _resourceOwnerValidator.ValidateAsync(userName, password, _validatedRequest); if (resourceOwnerResult.IsError) { var error = "invalid_username_or_password"; if (resourceOwnerResult.Error.IsPresent()) { error = resourceOwnerResult.Error; } LogError("User authentication failed: " + error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant, error)); } if (resourceOwnerResult.Principal == null) { var error = "User authentication failed: no principal returned"; LogError(error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, error); return(Invalid(OidcConstants.TokenErrors.InvalidGrant)); } _validatedRequest.UserName = userName; _validatedRequest.Subject = resourceOwnerResult.Principal; await RaiseSuccessfulResourceOwnerAuthenticationEventAsync(userName, resourceOwnerResult.Principal.GetSubjectId()); _logger.LogInformation("Resource owner password token request validation success."); return(Valid()); }