Какой лучший способ вернуть токен доступа из запроса регистрации OpenIddict C # dotnet core 2.2 - PullRequest
0 голосов
/ 21 марта 2019

Я работаю над Web API и ищу лучший способ создания токена доступа после регистрации.

Сценарий: Пользователь отправляет запросы от Mobile App (Ios, Android) на создание учетной записи и передает параметры имя, фамилию, пароль и адрес электронной почты или телефон после успешного создания пользователя, что я делаю, это создание запроса на вход и возврат ответа.

Вопрос: Есть ли лучший способ сделать это? используя этот подход, я не могу проверить его с помощью XUnit, потому что я могу создать пользователя, но когда пытаюсь войти в систему, получая ошибки

    [HttpPost, Route("signup")]
    [ProducesErrorResponseType(typeof(Error))]
    [ProducesResponseType(typeof(AccessTokenResponse), (int)HttpStatusCode.OK)]
    [AllowAnonymous]
    public async Task<IActionResult> Register([FromBody]RegistrationDto model)
    {
        ValidatePassword(model.Password, nameof(model.Password));
        if (!ModelState.IsValid)
        {
            return InoperableResult(ModelState);
        }

        var appUsr = _mapper.Map<AppUser>(model);

        if (Utilities.IsValidEmail(model.Identifier))
        {
            var isEmailAlreadyRegistered = await _customUserManager.IsEmailRegistered(model.Identifier);
            if (isEmailAlreadyRegistered)
            {
                var er = new Error(ErrorCodes.RegistrationFailure, "Failed to register user.");
                er.AddDetailError("DuplicateEmailAddress", "Email address is already registered.");
                return InoperableResult(er);
            }
            appUsr.Email = model.Identifier;
            appUsr.UserName = model.Identifier;
        }
        else
        {
            var isPhoneAlreadyRegistered = await _customUserManager.IsPhoneRegistered(model.Identifier);
            if (isPhoneAlreadyRegistered)
            {
                var er = new Error(ErrorCodes.RegistrationFailure, "Failed to register user.");
                er.AddDetailError("DuplicatePhoneNumber", "Phone number is already registered.");
                return InoperableResult(er);
            }
            appUsr.PhoneNumber = model.Identifier;
            appUsr.UserName = model.Identifier;
        }
        appUsr.IsActive = true;
        IdentityResult result = await _userManager.CreateAsync(appUsr, model.Password);

        if (!result.Succeeded)
            return InoperableResult(ErrorCodes.RegistrationFailure, "Failed to register user.", result);

        if (appUsr.Email.HasValue())
        {
            await _notifier.SendWelcomeEmail(appUsr);
            await _notifier.SendVerificationCode(appUsr, CustomUserManager.ConfirmEmailTokenPurpose, NotificationRoute.Email);
        }

        if (appUsr.PhoneNumber.HasValue())
            await _notifier.SendVerificationCode(appUsr, CustomUserManager.ConfirmPhoneTokenPurpose, NotificationRoute.SMS);

        try
        {
            var json = await LoginAsync(appUsr.UserName, model.Password);
            if (!string.IsNullOrEmpty(json))
            {
                return new OkObjectResult(json);
            }


            // Getting BaseUrl and will use for invoking sign in action to get the tokens
            string BaseUrl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
            var uri = String.Format("{0}/api/connect/token", BaseUrl);
            // prepare form url encoded object for sign in request
            var formData = new FormUrlEncodedContent(new[] {
              new KeyValuePair<string, string>("username", appUsr.UserName),
              new KeyValuePair<string, string>("password", model.Password),
              new KeyValuePair<string, string>("grant_type", "password"),
              new KeyValuePair<string, string>("scope", "openid profile offline_access"),
            //  //new KeyValuePair<string, string>("resource", BaseUrl)
            //});

            var client = new HttpClient();

            var req = new HttpRequestMessage(HttpMethod.Post, uri) { Content = formData };
            var res = await client.SendAsync(req); // execute sign in requset after sucessfully account creation
            var responseString = await res.Content.ReadAsStringAsync();
            if (res.StatusCode == HttpStatusCode.OK)
            {
                return new OkObjectResult(responseString);
            }

        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message, ex);
        }
        return InoperableResult(ErrorCodes.LoginFailure, "User is created but failed to Sign in");
    }

Войти

[HttpPost("~/api/connect/token")]
    [Consumes("application/x-www-form-urlencoded")]
    [ProducesResponseType(typeof(AccessTokenResponse), (int)HttpStatusCode.OK)]
    public async Task<IActionResult> Exchange([FromBody]OpenIdConnectRequest request)
    {
        if (request.IsPasswordGrantType())
        {
            request.Username = request.Username.Replace(" ", "+");
            var user = await _customUserManager.FindByEmailOrPhone(request.Username);
            if (user == null)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "User not found.", HttpStatusCode.Unauthorized.ToInt());
            }

            // Ensure the user is active.
            if (!user.IsActive)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "The specified user account is disabled.",
                    HttpStatusCode.Unauthorized.ToInt());
            }

            // Validate the username/password parameters and ensure the account is not locked out.
            var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, true);

            // Ensure the user is not already locked out.
            if (result.IsLockedOut)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "The specified user account has been suspended.",
                    HttpStatusCode.Unauthorized.ToInt());
            }

            // Reject the token request if two-factor authentication has been enabled by the user.
            if (result.RequiresTwoFactor)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "Invalid login procedure.",
                        HttpStatusCode.Unauthorized.ToInt());
            }

            // Ensure the user is allowed to sign in.
            if (result.IsNotAllowed)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "The specified user is not allowed to sign in.",
                      HttpStatusCode.Unauthorized.ToInt());
            }

            if (!result.Succeeded)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "Please check that your email and password is correct.",
                     HttpStatusCode.Unauthorized.ToInt());
            }

            // Create a new authentication ticket.
            var ticket = await _customUserManager.CreateTicketAsync(request, user);
            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
        }
        else if (request.IsRefreshTokenGrantType())
        {
            // Retrieve the claims principal stored in the refresh token.
            var info = await HttpContext.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme);

            // Retrieve the user profile corresponding to the refresh token.
            // Note: if you want to automatically invalidate the refresh token
            // when the user password/roles change, use the following line instead:
            // var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
            var user = await _userManager.GetUserAsync(info.Principal);
            if (user == null)
            {
                return InoperableResult(ErrorCodes.LoginFailure, "The refresh token is no longer valid.",
                    HttpStatusCode.Unauthorized.ToInt());
            }

            // Ensure the user is still allowed to sign in.
            if (!await _signInManager.CanSignInAsync(user))
            {
                return InoperableResult(ErrorCodes.LoginFailure, "The user is no longer allowed to sign in.",
                    HttpStatusCode.Unauthorized.ToInt());
            }

            // Create a new authentication ticket, but reuse the properties stored
            // in the refresh token, including the scopes originally granted.

            var ticket = await _customUserManager.CreateTicketAsync(request, user);
            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
        }

        var er = new Error(ErrorCodes.LoginFailure, ResponseMessages.InvalidDataMsg);
        er.AddDetailError(OpenIdConnectConstants.Errors.InvalidGrant, "The specified grant type is not supported.");
        return InoperableResult(er);
    }
...