我正在研究Web API,我正在寻找注册后生成访问令牌的最佳方法。
方案::用户成功创建用户正在执行的操作后,便从移动应用程序(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);
}