Реализация двухфакторной с ASOS
Возможно, я все об этом ошибаюсь, но я следовал примеру установки в этой статье: Создание собственного сервера OpenID Connect с ASOS, однако он не объясняет, как реализовать два фактора, поскольку это не является основной целью этого. статья.
Единственное упоминание о двух фракциях в этой статье - в конечной точке токена, где выполняется проверка двухфакторной аутентификации, где он отклоняет запрос токена, если включен двухфакторный. Поэтому я добавил проверку в своем клиентском приложении, которая проверяет это конкретное условие отклонения, а затем перенаправляет на страницу, которая запрашивает двухфакторный код. Эта страница сначала вызывает конечную точку авторизации, которая отправляет код на телефон пользователя, который находится в файле. Затем, как только пользователь ввел код, он снова вызывает конечную точку авторизации с этим кодом, и метод, который обрабатывает конечную точку авторизации, проверяет код и возвращает SignInResult в случае успеха. Тем не менее я получаю ошибку 500 в методе SignIn(принцип, свойства, схема), который возвращает SignInResult.
Прежде всего, я хотел бы знать, правильно ли я это делаю, и, во-вторых, если я делаю это правильно, что я делаю неправильно? Пожалуйста, найдите мои соответствующие биты кода ниже:
AuthorizationProvider:
public override async Task HandleTokenRequest(HandleTokenRequestContext context) {
var _dbContext = context.HttpContext.RequestServices.GetRequiredService<DbContext>();
if(context.Request.IsPasswordGrantType()) {
var u = await _dbContext.Users.Include(m => m.Organization).SingleOrDefaultAsync(x => x.Email.ToLowerInvariant() == context.Request.Username.ToLowerInvariant());
if(u == null || !PasswordHelper.VerifyPassword(context.Request.Password, u.Password)) {
context.Reject(
error: OpenIdConnectConstants.Errors.AccessDenied,
description: "The email or password is incorrect");
return;
} else if(u.Organization == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Your user account is not associated with an organization.");
return;
}
// // Reject the token request if two-factor authentication has been enabled by the user.
if(u.TwoFactorEnabled) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Two-factor authentication is required for this account.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, u.Id.ToString()));
claims.Add(new Claim(ClaimTypes.Name, context.Request.Username));
claims.Add(new Claim(ClaimTypes.Role, u.RoleString));
claims.Add(new Claim("user_id", u.Id.ToString()));
claims.Add(new Claim("org_id", u.Organization.Id.ToString()));
foreach(var claim in claims) {
claim.SetDestinations(new List<string> { OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken });
}
identity.AddClaims(claims);
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess);
context.Validate(ticket);
}
}
Авторизовать метод:
[HttpPost]
[Route("authorize")]
public async Task<IActionResult> Authorize() {
var request = HttpContext.GetOpenIdConnectRequest();
if(request.Code == null) {
var u = await _dbContext.Users.SingleOrDefaultAsync(x => x.Email.ToLowerInvariant() == request.Username.ToLowerInvariant());
if(u == null || !PasswordHelper.VerifyPassword(request.Password, u.Password)) {
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
var response = await _twoFactorProvider.SendCode(CodeMethods.sms, u.AuthyId);
return Ok(new { Id = u.AuthyId, Response = response });
} else {
var response = await _twoFactorProvider.VerifyCode(request.Code, int.Parse(request.Username));
if(response.Token != "is valid") {
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
var u = await _dbContext.Users.Include(m => m.Organization).SingleOrDefaultAsync(x => x.AuthyId == int.Parse(request.Username));
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, u.Id.ToString()));
claims.Add(new Claim(ClaimTypes.Name, u.Email));
claims.Add(new Claim(ClaimTypes.Role, u.RoleString));
claims.Add(new Claim("user_id", u.Id.ToString()));
claims.Add(new Claim("org_id", u.Organization.Id.ToString()));
foreach(var claim in claims) {
claim.SetDestinations(new List<string> { OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken });
}
identity.AddClaims(claims);
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
}
Методы клиента:
login(email, password) {
var deferred = q.defer();
var payload = {
'grant_type': 'password',
'username': email,
'password': password,
'scope': 'openid offline_access'
};
var url = this.authContextConfiguration.baseUrl + 'connect/token';
$.ajax({
url: url,
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: payload,
success: function (data) {
// store the access token / everything else //
deferred.resolve();
},
error: function (req) {
var error = {
message: req.responseJSON.error_description || 'There was an error when signing in.'
};
deferred.reject(error);
}
});
return deferred.promise;
}
twoFactorLogin(email, password) {
var deferred = q.defer();
var payload = {
'username': email,
'password': password
}
var url = this.authContextConfiguration.baseUrl + 'connect/authorize';
$.ajax({
url: url,
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: payload,
success: function (data) {
deferred.resolve(data);
},
error: function (req) {
var error = {
message: req.responseJSON.error_description || 'There was an error generating your two factor code.'
};
deferred.reject(error);
}
});
return deferred.promise;
}
verifyTwoFactorCode(id, code, remember) {
var deferred = q.defer();
var payload = {
'username': id,
'code': code,
'rememberMe': remember
}
var url = this.authContextConfiguration.baseUrl + 'connect/authorize';
$.ajax({
url: url,
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: payload,
success: function (data) {
deferred.resolve(data);
},
error: function (req) {
var error = {
message: req.responseJSON.error_description || 'There was an error verifying your two factor code.'
};
deferred.reject(error);
}
});
return deferred.promise;
}