Я хочу добавить многофакторную аутентификацию с помощью программных токенов TOTP в приложение Angular & Spring, сохраняя при этом все максимально приближенное к значениям по умолчанию Spring Boot Security Starter .
Проверка токена происходит локально (с библиотекой aerogear-otp- java), стороннего поставщика API нет.
Настройка токенов для пользователя работает, но проверка их с помощью диспетчера / провайдеров аутентификации Spring Security - нет.
TL; DR
- Каков официальный способ интеграции дополнительного поставщика AuthenticationProvider в Spring Boot Security Starter настроенную систему?
- Что рекомендуемые способы предотвращения атак воспроизведения?
Длинная версия
API имеет конечную точку /auth/token
, из которой веб-интерфейс может получить токен JWT, указав имя пользователя и пароль. Ответ также включает статус аутентификации, который может быть либо AUTHENTICATED , либо PRE_AUTHENTICATED_MFA_REQUIRED .
Если пользователю требуется MFA, токен выдается с одним предоставленным полномочием. PRE_AUTHENTICATED_MFA_REQUIRED
и срок действия 5 минут. Это позволяет пользователю получить доступ к конечной точке /auth/mfa-token
, где он может предоставить код TOTP из своего приложения Authenticator и получить полностью аутентифицированный токен для доступа к сайту.
Провайдер и токен
Я создал свой пользовательский MfaAuthenticationProvider
, который реализует AuthenticationProvider
:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// validate the OTP code
}
@Override
public boolean supports(Class<?> authentication) {
return OneTimePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
И OneTimePasswordAuthenticationToken
, который расширяет AbstractAuthenticationToken
для хранения имя пользователя (взято из подписанного JWT) и код OTP.
Конфиг
У меня есть мой пользовательский WebSecurityConfigurerAdapter
, куда я добавляю свой пользовательский AuthenticationProvider
через http.authenticationProvider()
. В соответствии с JavaDo c, это, кажется, правильное место:
Позволяет добавить дополнительный поставщик AuthenticationProvider для использования
Соответствующие части моего SecurityConfig
выглядит так
@Configuration
@EnableWebSecurity
@EnableJpaAuditing(auditorAwareRef = "appSecurityAuditorAware")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationProvider(new MfaAuthenticationProvider());
http.authorizeRequests()
// Public endpoints, HTML, Assets, Error Pages and Login
.antMatchers("/", "favicon.ico", "/asset/**", "/pages/**", "/api/auth/token").permitAll()
// MFA auth endpoint
.antMatchers("/api/auth/mfa-token").hasAuthority(ROLE_PRE_AUTH_MFA_REQUIRED)
// much more config
Контроллер
В AuthController
введен AuthenticationManagerBuilder
и он стягивает все это вместе.
@RestController
@RequestMapping(AUTH)
public class AuthController {
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@PostMapping("/mfa-token")
public ResponseEntity<Token> mfaToken(@Valid @RequestBody OneTimePassword oneTimePassword) {
var username = SecurityUtils.getCurrentUserLogin().orElse("");
var authenticationToken = new OneTimePasswordAuthenticationToken(username, oneTimePassword.getCode());
var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
// rest of class
Однако публикация по /auth/mfa-token
приводит к этой ошибке:
"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken
Почему Spring Security не получает мой провайдер аутентификации? Отладка контроллера показывает, что DaoAuthenticationProvider
- единственный поставщик аутентификации в AuthenticationProviderManager
.
Если я выставляю свой MfaAuthenticationProvider
как боб, то регистрируется только только провайдер, поэтому я получаю обратное:
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.
Итак, как Я получаю оба?
Мой вопрос
Каков рекомендуемый способ интеграции дополнительного AuthenticationProvider
в Spring Boot Security Starter настроенную систему, чтобы я мог получить оба DaoAuthenticationProvider
и мой собственный кастом MfaAuthenticationProvider
? Я хочу сохранить значения по умолчанию Spring Boot Scurity Starter и дополнительно иметь своего собственного провайдера.
Предотвращение атаки воспроизведения
Я знаю, что алгоритм OTP сам по себе не защищает от атак воспроизведения в течение интервала времени, в течение которого код является действительным; RF C 6238 разъясняет это
Верификатор НЕ ДОЛЖЕН принимать вторую попытку OTP после успешной проверки для первого OTP, что гарантирует одноразовое использование OTP .
Мне было интересно, есть ли рекомендуемый способ реализации защиты. Так как токены OTP основаны на времени, я думаю о сохранении последнего успешного входа в модель пользователя и о том, что на интервал времени 30 секунд должен быть только один успешный вход. Это, конечно, означает синхронизацию в пользовательской модели. Любые лучшие подходы?
Спасибо.
-
PS: так как это вопрос безопасности, я ищу ответ из достоверных и / или официальных источников. Спасибо.