refactor(security): migrate from jws to jwe
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
+6
-6
@@ -12,12 +12,12 @@
|
|||||||
MONGODB_URI=mongodb://localhost:27017/kotlin-api
|
MONGODB_URI=mongodb://localhost:27017/kotlin-api
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════════════════════════
|
||||||
# JWT Security Configuration
|
# JWT Security Configuration (JWE - JSON Web Encryption)
|
||||||
# ════════════════════════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════════════════════════
|
||||||
# REQUIRED: Generate a secure base64-encoded secret for JWT token signing
|
# REQUIRED: Generate a secure base64-encoded secret for JWT token encryption
|
||||||
# MUST be base64-encoded (your code uses Decoders.BASE64.decode)
|
# MUST be exactly 32 bytes when base64-decoded (for AES-256-GCM encryption)
|
||||||
#
|
#
|
||||||
# Generate a secure base64-encoded secret (single line, no newlines):
|
# Generate a secure 32-byte base64-encoded encryption secret (single line, no newlines):
|
||||||
# openssl rand -base64 64 | tr -d '\n'
|
# openssl rand -base64 32 | tr -d '\n'
|
||||||
#
|
#
|
||||||
JWT_SECRET=your-base64-encoded-secret-here
|
JWT_ENCRYPTION_SECRET=your-base64-encoded-32-byte-secret-here
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.springframework.stereotype.Component
|
|||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties("jwt")
|
@ConfigurationProperties("jwt")
|
||||||
data class JwtProperties(
|
data class JwtProperties(
|
||||||
var secret: String = "",
|
var encryptionSecret: String = "",
|
||||||
var accessTokenExpiration: Long = 3600000, // 1 hour
|
var accessTokenExpiration: Long = 3600000, // 1 hour
|
||||||
var refreshTokenExpiration: Long = 2592000000, // 30 days
|
var refreshTokenExpiration: Long = 2592000000, // 30 days
|
||||||
var issuer: String = "kotlin-api",
|
var issuer: String = "kotlin-api",
|
||||||
|
|||||||
+10
-13
@@ -7,16 +7,15 @@ import io.jsonwebtoken.Jwts
|
|||||||
import io.jsonwebtoken.MalformedJwtException
|
import io.jsonwebtoken.MalformedJwtException
|
||||||
import io.jsonwebtoken.UnsupportedJwtException
|
import io.jsonwebtoken.UnsupportedJwtException
|
||||||
import io.jsonwebtoken.io.Decoders
|
import io.jsonwebtoken.io.Decoders
|
||||||
import io.jsonwebtoken.security.Keys
|
|
||||||
import io.visus.demos.kotlinapi.config.JwtProperties
|
import io.visus.demos.kotlinapi.config.JwtProperties
|
||||||
import io.visus.demos.kotlinapi.domain.model.JwtTokenType
|
import io.visus.demos.kotlinapi.domain.model.JwtTokenType
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.security.SignatureException
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.crypto.SecretKey
|
import javax.crypto.SecretKey
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class JwtTokenProvider(
|
class JwtTokenProvider(
|
||||||
@@ -24,9 +23,9 @@ class JwtTokenProvider(
|
|||||||
) {
|
) {
|
||||||
private val logger = LoggerFactory.getLogger(JwtTokenProvider::class.java)
|
private val logger = LoggerFactory.getLogger(JwtTokenProvider::class.java)
|
||||||
|
|
||||||
private val secretKey: SecretKey by lazy {
|
private val encryptionKey: SecretKey by lazy {
|
||||||
val bytes = Decoders.BASE64.decode(jwtProperties.secret)
|
val bytes = Decoders.BASE64.decode(jwtProperties.encryptionSecret)
|
||||||
Keys.hmacShaKeyFor(bytes)
|
SecretKeySpec(bytes, "AES")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> extractClaim(
|
fun <T> extractClaim(
|
||||||
@@ -36,9 +35,9 @@ class JwtTokenProvider(
|
|||||||
val claims =
|
val claims =
|
||||||
Jwts
|
Jwts
|
||||||
.parser()
|
.parser()
|
||||||
.verifyWith(secretKey)
|
.decryptWith(encryptionKey)
|
||||||
.build()
|
.build()
|
||||||
.parseSignedClaims(token)
|
.parseEncryptedClaims(token)
|
||||||
.payload
|
.payload
|
||||||
|
|
||||||
return claimsResolver(claims)
|
return claimsResolver(claims)
|
||||||
@@ -76,7 +75,7 @@ class JwtTokenProvider(
|
|||||||
).issuedAt(now)
|
).issuedAt(now)
|
||||||
.expiration(expiry)
|
.expiration(expiry)
|
||||||
.issuer(jwtProperties.issuer)
|
.issuer(jwtProperties.issuer)
|
||||||
.signWith(secretKey, Jwts.SIG.HS512)
|
.encryptWith(encryptionKey, Jwts.KEY.DIRECT, Jwts.ENC.A256GCM)
|
||||||
.compact()
|
.compact()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +94,7 @@ class JwtTokenProvider(
|
|||||||
.toString(), // UUIDv7
|
.toString(), // UUIDv7
|
||||||
).issuedAt(now)
|
).issuedAt(now)
|
||||||
.expiration(expiry)
|
.expiration(expiry)
|
||||||
.signWith(secretKey, Jwts.SIG.HS512)
|
.encryptWith(encryptionKey, Jwts.KEY.DIRECT, Jwts.ENC.A256GCM)
|
||||||
.compact()
|
.compact()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +121,11 @@ class JwtTokenProvider(
|
|||||||
try {
|
try {
|
||||||
Jwts
|
Jwts
|
||||||
.parser()
|
.parser()
|
||||||
.verifyWith(secretKey)
|
.decryptWith(encryptionKey)
|
||||||
.build()
|
.build()
|
||||||
.parseSignedClaims(token)
|
.parseEncryptedClaims(token)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (ex: SignatureException) {
|
|
||||||
logger.error("Invalid JWT signature: {}", ex.message)
|
|
||||||
} catch (ex: MalformedJwtException) {
|
} catch (ex: MalformedJwtException) {
|
||||||
logger.error("Invalid JWT token: {}", ex.message)
|
logger.error("Invalid JWT token: {}", ex.message)
|
||||||
} catch (ex: ExpiredJwtException) {
|
} catch (ex: ExpiredJwtException) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ spring:
|
|||||||
uri: ${MONGODB_URI:mongodb://localhost:27017/kotlin-api}
|
uri: ${MONGODB_URI:mongodb://localhost:27017/kotlin-api}
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: ${JWT_SECRET}
|
encryption-secret: ${JWT_ENCRYPTION_SECRET}
|
||||||
access-token-expiration: 3600000 # 1 hour in milliseconds
|
access-token-expiration: 3600000 # 1 hour in milliseconds
|
||||||
refresh-token-expiration: 2592000000 # 30 days in milliseconds
|
refresh-token-expiration: 2592000000 # 30 days in milliseconds
|
||||||
issuer: kotlin-api
|
issuer: kotlin-api
|
||||||
|
|||||||
Reference in New Issue
Block a user