Я хочу добавить многофакторную аутентификацию с помощью программных токенов TOTP в приложение Angular & Spring, сохранив все как можно ближе к значениям по умолчанию Spring Boot Security Starter .
Проверка токена происходит локально (с помощью библиотеки aerogear-otp-java), стороннего поставщика API нет.
Настройка токенов для пользователя работает, но проверка их с помощью диспетчера / провайдеров аутентификации Spring Security - нет.
TL; DR
- Каков официальный способ интеграции дополнительного AuthenticationProvider в систему, настроенную для Spring Boot Security Starter ?
- Каковы рекомендуемые способы предотвращения повторных атак?
Длинная версия
У API есть конечная точка, /auth/token
из которой веб-интерфейс может получить токен JWT, указав имя пользователя и пароль. Ответ также включает в себя аутентификацию-статус, который может быть либо аутентифицирован или 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()
. В соответствии с JavaDoc, это, кажется, правильное место:
Позволяет добавить дополнительный 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 сам по себе не защищает от атак воспроизведения в течение интервала времени, в течение которого код является действительным; RFC 6238 проясняет это
Верификатор НЕ ДОЛЖЕН принимать вторую попытку OTP после успешной проверки для первого OTP, что гарантирует одноразовое использование OTP.
Мне было интересно, если есть рекомендуемый способ реализации защиты. Поскольку токены OTP основаны на времени, я думаю о сохранении последнего успешного входа в модель пользователя и о том, что на интервал времени 30 секунд должен быть только один успешный вход. Это, конечно, означает синхронизацию на пользовательской модели. Любые лучшие подходы?
Спасибо.
-
PS: так как это вопрос безопасности, я ищу ответ из достоверных и / или официальных источников. Спасибо.