Example #1
0
        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);
        }
Example #2
0
        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));
        }
Example #4
0
        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());
        }