0.0 2FA Implementación y configuración - Siuss/gamerly-backend GitHub Wiki
Para implementar la autenticación en dos pasos (2FA) en un proyecto Kotlin con Spring Security, sigue estos pasos sugeridos:
Incluye las dependencias necesarias en tu build.gradle.kts
o pom.xml
:
// Spring Security
implementation("org.springframework.boot:spring-boot-starter-security")
// Biblioteca para TOTP (Time-based One-Time Password)
implementation("com.google.zxing:javase:3.4.1") // Para generar QR codes
implementation("commons-codec:commons-codec:1.15") // Para codificar secretos
Añade campos para almacenar el secreto 2FA y el estado de habilitación:
@Entity
class User(
// ... otros campos (email, password, etc.)
var secret2FA: String? = null, // Secreto para generar códigos TOTP
var is2FAEnabled: Boolean = false // Estado del 2FA
)
Define un filtro personalizado para manejar la autenticación en dos pasos.
Ejemplo de configuración en SecurityConfig.kt
:
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val userDetailsService: UserDetailsService,
private val twoFactorAuthenticationFilter: TwoFactorAuthenticationFilter
) : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers("/login", "/register", "/2fa-setup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(customAuthenticationSuccessHandler())
.and()
.addFilterBefore(twoFactorAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
}
@Bean
fun customAuthenticationSuccessHandler(): AuthenticationSuccessHandler {
return CustomAuthenticationSuccessHandler()
}
}
Crea un servicio para generar el secreto TOTP y el QR Code:
@Service
class TwoFactorAuthService {
fun generateSecret(): String {
val secureRandom = SecureRandom()
val keyBytes = ByteArray(20)
secureRandom.nextBytes(keyBytes)
return Base32().encodeToString(keyBytes)
}
fun generateQRCodeUrl(user: User): String {
return "otpauth://totp/${user.email}?secret=${user.secret2FA}&issuer=MyApp"
}
}
Crea endpoints para manejar la habilitación/deshabilitación del 2FA:
Controlador:
@RestController
@RequestMapping("/2fa")
class TwoFactorAuthController(
private val twoFactorAuthService: TwoFactorAuthService,
private val userRepository: UserRepository
) {
@PostMapping("/enable")
fun enable2FA(@AuthenticationPrincipal user: User): ResponseEntity<String> {
val secret = twoFactorAuthService.generateSecret()
user.secret2FA = secret
user.is2FAEnabled = true
userRepository.save(user)
val qrCodeUrl = twoFactorAuthService.generateQRCodeUrl(user)
return ResponseEntity.ok(qrCodeUrl)
}
@PostMapping("/verify")
fun verifyCode(@RequestParam code: String, @AuthenticationPrincipal user: User): ResponseEntity<Boolean> {
val isValid = twoFactorAuthService.validateCode(user.secret2FA!!, code)
return ResponseEntity.ok(isValid)
}
}
Implementa un filtro para verificar el código después de la autenticación inicial:
Filtro Personalizado (TwoFactorAuthenticationFilter.kt
):
class TwoFactorAuthenticationFilter(
private val userDetailsService: UserDetailsService
) : OncePerRequestFilter() {
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
if (request.requestURI == "/login" && request.method == "POST") {
val username = request.getParameter("username")
val user = userDetailsService.loadUserByUsername(username) as User
if (user.is2FAEnabled) {
val code = request.getParameter("code")
if (!validateCode(user.secret2FA!!, code)) {
throw BadCredentialsException("Código 2FA inválido")
}
}
}
chain.doFilter(request, response)
}
private fun validateCode(secret: String, code: String): Boolean {
val totp = Totp(secret)
return totp.verify(code)
}
}
- Paso 1: El usuario accede a su perfil y habilita 2FA.
-
Paso 2: Muestra un QR Code generado con
generateQRCodeUrl()
para que el usuario lo escanee con Google Authenticator o Authy. - Paso 3: Solicita al usuario que ingrese un código de verificación para confirmar.
- Pruebas: Usa apps como Google Authenticator para validar el flujo.
- Backup Codes: Genera códigos de respaldo en caso de pérdida del dispositivo.
- Encriptación: Almacena el secreto 2FA encriptado en la base de datos.
-
Biblioteca TOTP: Usa
com.warrenstrange:googleauth
para simplificar la validación. - Documentación: Spring Security Multi-Factor Authentication.
Con estos pasos, nuestra aplicación Gamerly tendrá autenticación en dos pasos robusta y personalizable.