All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.provenance.p8e.shared.util.TokenManager.kt Maven / Gradle / Ivy

package io.provenance.p8e.shared.util

import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.interfaces.DecodedJWT
import io.p8e.proto.Authentication.Jwt
import io.p8e.util.*
import io.p8e.util.toUuidProv
import io.provenance.p8e.shared.config.JwtProperties
import org.springframework.stereotype.Component
import java.security.PublicKey
import java.time.Instant
import java.util.Date

abstract class JwtClaims(vararg val claims: JwtClaim<*>) {
    companion object {
        fun cloneFromJwt(jwt: DecodedJWT): JwtClaims {
            throw NotImplementedError("cloneFromJwt not implemented")
        }
    }
}

class KeyClaims(val publicKey: PublicKey) : JwtClaims(
    PublicKeyClaim(publicKey)
) {
    companion object {
        val cloneFromJwt: (DecodedJWT) -> KeyClaims = { jwt ->
            KeyClaims(
                PublicKeyClaim.validate(jwt.claims[PublicKeyClaim.KEY]?.asString())
            )
        }
    }
}

class IdentityClaims(val provenanceJwt: String) : JwtClaims(
    ProvenanceJwtClaim(provenanceJwt),
    ProvenanceIdentityClaim(provenanceJwt)
) {
    companion object {
        val cloneFromJwt: (DecodedJWT) -> IdentityClaims = { jwt ->
            IdentityClaims(
                ProvenanceJwtClaim.validate(jwt.claims[ProvenanceJwtClaim.KEY]?.asString())
            )
        }
    }
}

abstract class JwtClaim(public val key: String, protected val value: T) {
    abstract fun getValue(): String
}

class PublicKeyClaim(private val publicKey: PublicKey) : JwtClaim(
    KEY, publicKey) {
    companion object {
        const val KEY = "publicKey"
        fun validate(value: String?) = value?.toJavaPublicKey()
            .orThrow { IllegalArgumentException("Provided JWT is missing Public Key claim.") }
    }

    override fun getValue(): String = publicKey.toHex()
}

class ProvenanceJwtClaim(private val jwt: String) : JwtClaim(
    KEY, jwt) {
    companion object {
        const val KEY = "provenanceJwt"
        fun validate(value: String?): String = value?.orThrow { IllegalArgumentException("Provided JWT is missing Provenance JWT claim.") }!!
    }

    override fun getValue(): String = value
}

class ProvenanceIdentityClaim(private val jwt: String) : JwtClaim(KEY, extractUuid(jwt)) {
    companion object {
        const val KEY = "provenanceIdentityUuid"
        fun validate(value: String?): String = value?.toUuidProv().toString()
            .orThrow { IllegalArgumentException("Provided JWT is missing Provenance Identity UUID claim.") }

        fun extractUuid(jwt: String) = JWT.decode(jwt).claims["uuid"]?.asString().let {
            validate(it)
        }
    }

    override fun getValue(): String = value
}

class TokenManager(
    jwtProperties: JwtProperties,
    private val claimGenerator: (jwt: DecodedJWT) -> T
) {
    private val expireSeconds = jwtProperties.expireSeconds.toLong()

    private val consumer = jwtProperties.consumer
    private val issuer = jwtProperties.issuer
    private val algorithm = Algorithm.HMAC256(jwtProperties.secret)
    private val verifier = JWT.require(algorithm).withIssuer(issuer).build()

    fun create(
        claims: T
    ): Jwt {
        val jwt = JWT.create()
            .also {
                claims.claims.forEach { claim -> it.withClaim(claim.key, claim.getValue()) }
            }
            .withClaim(CONSUMER_CLAIM, consumer)
            .withIssuer(issuer)
            .withExpiresAt(Date.from(Instant.now().plusSeconds(expireSeconds)))
            .withIssuedAt(Date.from(Instant.now().minusSeconds(1)))
            .sign(algorithm)

        return Jwt.newBuilder()
            .setToken(jwt)
            .build()
    }

    fun verify(
        jwt: String
    ): DecodedJWT {
        return verifier.verify(jwt)
    }

    fun rotate(
        jwt: String
    ): Jwt {
        val decoded = verify(jwt)

        return create(
            claimGenerator(decoded)
        )
    }

    companion object {
        const val CONSUMER_CLAIM = "con"
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy