Desejo adicionar autenticação multifatorial com tokens flexíveis TOTP a um aplicativo Angular & Spring, mantendo tudo o mais próximo possível dos padrões do Spring Boot Security Starter .
A validação de token ocorre localmente (com a biblioteca aerogear-otp-java), sem provedor de API de terceiros.
A configuração de tokens para um usuário funciona, mas a validação deles aproveitando o Spring Security Authentication Manager / Providers não.
TL; DR
- Qual é a maneira oficial de integrar um AuthenticationProvider adicional em um sistema configurado do Spring Boot Security Starter ?
- Quais são as formas recomendadas para evitar ataques de repetição?
Versão longa
A API possui um ponto /auth/token
de extremidade a partir do qual o front-end pode obter um token JWT, fornecendo nome de usuário e senha. A resposta também inclui um status de autenticação, que pode ser AUTHENTICATED ou PRE_AUTHENTICATED_MFA_REQUIRED .
Se o usuário exigir MFA, o token será emitido com uma única autoridade concedida PRE_AUTHENTICATED_MFA_REQUIRED
e um tempo de expiração de 5 minutos. Isso permite que o usuário acesse o terminal /auth/mfa-token
em que pode fornecer o código TOTP a partir do aplicativo Authenticator e obtenha o token totalmente autenticado para acessar o site.
Fornecedor e Token
Eu criei meu costume MfaAuthenticationProvider
que implementa AuthenticationProvider
:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// validate the OTP code
}
@Override
public boolean supports(Class<?> authentication) {
return OneTimePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
E um OneTimePasswordAuthenticationToken
que se estende AbstractAuthenticationToken
para manter o nome de usuário (retirado do JWT assinado) e o código OTP.
Config
Eu tenho meu costume WebSecurityConfigurerAdapter
, onde adiciono meu costume AuthenticationProvider
via http.authenticationProvider()
. De acordo com o JavaDoc, este parece ser o lugar certo:
Permite adicionar um AuthenticationProvider adicional a ser usado
As partes relevantes da minha SecurityConfig
aparência se parecem com isso.
@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
Controlador
A AuthController
tem a AuthenticationManagerBuilder
injectada e é puxando-o todos juntos.
@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
No entanto, a publicação contra /auth/mfa-token
leva a este erro:
"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken
Por que o Spring Security não seleciona meu provedor de autenticação? A depuração do controlador mostra que DaoAuthenticationProvider
é o único provedor de autenticação disponível AuthenticationProviderManager
.
Se eu expor meu MfaAuthenticationProvider
como bean, é o único provedor que está registrado, portanto, obtenho o oposto:
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.
Então, como faço para obter os dois?
Minha pergunta
Qual é a maneira recomendada de integrar um adicional AuthenticationProvider
a um sistema configurado do Spring Boot Security Starter , para que eu possa obter os dois, o DaoAuthenticationProvider
meu e o meu próprio costume MfaAuthenticationProvider
? Desejo manter os padrões do Spring Boot Scurity Starter e ter meu próprio provedor adicionalmente.
Prevenção de Replay Attack
Eu sei que o algoritmo OTP por si só não protege contra ataques de repetição no intervalo de tempo em que o código é válido; A RFC 6238 deixa isso claro
O verificador NÃO DEVE aceitar a segunda tentativa do OTP após a validação bem-sucedida ter sido emitida para o primeiro OTP, o que garante o uso único de um OTP apenas uma vez.
Fiquei me perguntando se existe uma maneira recomendada de implementar a proteção. Como os tokens OTP são baseados no tempo, estou pensando em armazenar o último logon bem-sucedido no modelo do usuário e garantir que haja apenas um logon bem-sucedido por intervalo de 30 segundos. Obviamente, isso significa sincronização no modelo do usuário. Alguma abordagem melhor?
Obrigado.
-
PS: como se trata de uma questão de segurança, estou procurando uma resposta de fontes confiáveis e / ou oficiais. Obrigado.