
org.http4k.security.CredentialsProvider.kt Maven / Gradle / Ivy
package org.http4k.security
import java.time.Clock
import java.time.Duration
import java.time.Duration.ZERO
import java.time.Instant
import java.util.concurrent.atomic.AtomicReference
fun interface CredentialsProvider : () -> T? {
companion object
}
fun interface RefreshCredentials : (T?) -> ExpiringCredentials?
data class ExpiringCredentials(val credentials: T, val expiry: Instant)
fun CredentialsProvider.Companion.Refreshing(
gracePeriod: Duration = Duration.ofSeconds(10),
clock: Clock = Clock.systemUTC(),
refreshFn: RefreshCredentials
) = object : CredentialsProvider {
private val stored = AtomicReference>(null)
private val isRefreshing = AtomicReference(false)
override fun invoke() = (stored.get()
?.takeIf {
val expiresSoon = it.expiresWithin(gracePeriod)
val expiresReallySoon = it.expiresWithin(gracePeriod.dividedBy(2))
val isAlreadyRefreshing = isRefreshing.get()
(!expiresSoon || isAlreadyRefreshing) && !expiresReallySoon
} ?: refresh())?.credentials
private fun refresh() = synchronized(this) {
isRefreshing.set(true)
try {
val current = stored.get()
when {
current != null && !current.expiresWithin(gracePeriod) -> current
else -> try {
refreshFn(current?.credentials).also(stored::set)
} catch (e: Exception) {
if (current == null || current.expiresWithin(ZERO)) throw e
current
}
}
} finally {
isRefreshing.set(false)
}
}
private fun ExpiringCredentials.expiresWithin(duration: Duration): Boolean =
expiry
.minus(duration)
.isBefore(clock.instant())
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy