package com.yau.digitalrmb.security.application; import com.yau.digitalrmb.security.config.SecurityProperties; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.security.MessageDigest; import java.security.SecureRandom; import java.sql.Timestamp; import java.time.Instant; import java.util.Base64; @Service public class RefreshTokenService { private static final SecureRandom RANDOM = new SecureRandom(); private final JdbcTemplate jdbcTemplate; private final SecurityProperties properties; public RefreshTokenService(JdbcTemplate jdbcTemplate, SecurityProperties properties) { this.jdbcTemplate = jdbcTemplate; this.properties = properties; } public String issue(long platformUserId) { byte[] bytes = new byte[48]; RANDOM.nextBytes(bytes); String token = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); jdbcTemplate.update("INSERT INTO auth_refresh_token (token_hash, platform_user_id, expires_at, revoked_at) VALUES (?, ?, ?, NULL)", hash(token), platformUserId, Timestamp.from(Instant.now().plus(properties.getSession().getRefreshTokenTtl()))); return token; } public void revokeForUser(String token, long platformUserId) { jdbcTemplate.update("UPDATE auth_refresh_token SET revoked_at = CURRENT_TIMESTAMP " + "WHERE token_hash = ? AND platform_user_id = ? AND revoked_at IS NULL", hash(token), platformUserId); } private static String hash(String value) { try { byte[] digest = MessageDigest.getInstance("SHA-256") .digest(value.getBytes(java.nio.charset.StandardCharsets.UTF_8)); StringBuilder result = new StringBuilder(digest.length * 2); for (byte item : digest) { result.append(String.format("%02x", item)); } return result.toString(); } catch (java.security.NoSuchAlgorithmException exception) { throw new IllegalStateException("SHA-256 is unavailable", exception); } } }