Rework token invalidation (#307)

This commit is contained in:
KirillPamPam
2023-09-25 16:02:36 +04:00
committed by GitHub
parent 109fb97e0a
commit 8e26d4a8cf
8 changed files with 51 additions and 104 deletions

View File

@@ -1,20 +1,22 @@
package io.emeraldpay.dshackle.auth
import com.github.benmanes.caffeine.cache.Caffeine
import org.springframework.stereotype.Component
import java.time.Duration
import java.time.Instant
import java.util.concurrent.ConcurrentHashMap
@Component
class AuthContext {
private val sessions = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofDays(1))
.build<String, Boolean>()
companion object {
val sessions = ConcurrentHashMap<String, TokenWrapper>()
fun putSessionInContext(tokenWrapper: TokenWrapper) {
sessions.put(tokenWrapper.sessionId, true)
}
fun putTokenInContext(tokenWrapper: TokenWrapper) {
sessions[tokenWrapper.sessionId] = tokenWrapper
}
fun removeToken(sessionId: String) {
sessions.remove(sessionId)
}
fun containsSession(sessionId: String): Boolean {
return sessions.asMap()[sessionId] != null
}
data class TokenWrapper(

View File

@@ -13,7 +13,9 @@ const val AUTH_METHOD_NAME = "emerald.Auth/Authenticate"
const val REFLECT_METHOD_NAME = "grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo"
@Component
class AuthInterceptor : ServerInterceptor {
class AuthInterceptor(
private val authContext: AuthContext
) : ServerInterceptor {
private val specialMethods = setOf(AUTH_METHOD_NAME, REFLECT_METHOD_NAME)
override fun <ReqT : Any, RespT : Any> interceptCall(
@@ -26,7 +28,7 @@ class AuthInterceptor : ServerInterceptor {
)
val isOrdinaryMethod = !specialMethods.contains(call.methodDescriptor.fullMethodName)
if (isOrdinaryMethod && (sessionId == null || !AuthContext.sessions.containsKey(sessionId))) {
if (isOrdinaryMethod && (sessionId == null || !authContext.containsSession(sessionId))) {
val cause = if (sessionId == null) "sessionId is not passed" else "Session $sessionId does not exist"
throw Status.UNAUTHENTICATED
.withDescription(cause)

View File

@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.auth.processor
import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.RegisteredClaims
import com.auth0.jwt.algorithms.Algorithm
import io.emeraldpay.dshackle.auth.AuthContext
import io.emeraldpay.dshackle.auth.service.KeyReader
@@ -13,6 +14,7 @@ import java.security.PublicKey
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.UUID
const val SESSION_ID = "sessionId"
@@ -37,6 +39,9 @@ abstract class AuthProcessor(
try {
val verifier: JWTVerifier = JWT.require(verifyingAlgorithm(keys.externalPublicKey))
.withIssuer(authorizationConfig.publicKeyOwner)
.withClaim(RegisteredClaims.ISSUED_AT) { claim, _ ->
claim.asInstant().plus(1, ChronoUnit.MINUTES).isAfter(Instant.now())
}
.build()
verifier.verify(token)
} catch (e: Exception) {

View File

@@ -1,26 +0,0 @@
package io.emeraldpay.dshackle.auth.processor
import io.emeraldpay.dshackle.auth.AuthContext
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.time.Instant
import java.time.temporal.ChronoUnit
@Component
open class TokenProcessor {
companion object {
private val log = LoggerFactory.getLogger(TokenProcessor::class.java)
}
@Scheduled(fixedRate = 30000)
fun invalidateTokens() {
AuthContext.sessions
.filter { Instant.now().isAfter(it.value.issuedAt.plus(1, ChronoUnit.HOURS)) }
.forEach {
log.info("Invalidate token with sessionId ${it.key}")
AuthContext.removeToken(it.key)
}
}
}

View File

@@ -11,7 +11,8 @@ import org.springframework.stereotype.Service
class AuthService(
private val authorizationConfig: AuthorizationConfig,
private val rsaKeyReader: KeyReader,
private val authProcessorResolver: AuthProcessorResolver
private val authProcessorResolver: AuthProcessorResolver,
private val authContext: AuthContext
) {
fun authenticate(token: String): String {
@@ -31,7 +32,7 @@ class AuthService(
.getAuthProcessor(decodedJwt)
.process(keys, token)
.run {
AuthContext.putTokenInContext(this)
authContext.putSessionInContext(this)
this.token
}
}