package space.luminic.finance.api import org.apache.commons.codec.digest.DigestUtils.sha256 import org.apache.commons.codec.digest.HmacUtils.hmacSha256 import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.security.core.context.SecurityContextHolder import org.springframework.web.bind.annotation.* import space.luminic.finance.dtos.UserDTO import space.luminic.finance.dtos.UserDTO.AuthUserDTO import space.luminic.finance.dtos.UserDTO.RegisterUserDTO import space.luminic.finance.mappers.UserMapper.toDto import space.luminic.finance.mappers.UserMapper.toTelegramMap import space.luminic.finance.services.AuthService import java.security.MessageDigest import java.time.Instant import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @RestController @RequestMapping("/auth") class AuthController( private val authService: AuthService, @Value("\${telegram.bot.token}") private val botToken: String ) { private val logger = LoggerFactory.getLogger(javaClass) fun verifyTelegramAuth(data: Map, botToken: String): Boolean { val hash = data["hash"] ?: return false val dataCheckString = data .filterKeys { it != "hash" } .toSortedMap() .map { "${it.key}=${it.value}" } .joinToString("\n") val secretKey = sha256(botToken) val hmacHex = hmacSha256(secretKey, dataCheckString) if (hmacHex != hash) return false val authDate = data["auth_date"]?.toLongOrNull() ?: return false val now = Instant.now().epochSecond // Опционально — запрет старых ответов (например, старше 1 часа) val maxAgeSeconds = 3600 if (now - authDate > maxAgeSeconds) return false return true } private fun sha256(input: String): ByteArray = MessageDigest.getInstance("SHA-256").digest(input.toByteArray()) private fun hmacSha256(secret: ByteArray, message: String): String { val key = SecretKeySpec(secret, "HmacSHA256") val mac = Mac.getInstance("HmacSHA256") mac.init(key) val hashBytes = mac.doFinal(message.toByteArray()) return hashBytes.joinToString("") { "%02x".format(it) } } @GetMapping("/test") fun test(): String { val authentication = SecurityContextHolder.getContext().authentication logger.info("SecurityContext in controller: $authentication") return "Hello, ${authentication.name}" } @PostMapping("/login") fun login(@RequestBody request: AuthUserDTO): Map { val token = authService.login(request.username.lowercase(), request.password) return mapOf("token" to token) } @PostMapping("/register") fun register(@RequestBody request: RegisterUserDTO): UserDTO { return authService.register(request.username, request.password, request.firstName).toDto() } @PostMapping("/tg-login") fun tgLogin(@RequestBody tgUser: UserDTO.TelegramAuthDTO): String { val ok = verifyTelegramAuth(tgUser.toTelegramMap(), botToken) if (!ok) throw IllegalArgumentException("Invalid Telegram login") return authService.tgAuth(tgUser) } @GetMapping("/me") fun getMe(): UserDTO { logger.info("Get Me") authService.getSecurityUser() return authService.getSecurityUser().toDto() } }