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

com.lightningkite.lightningserver.auth.JwtSigner.kt Maven / Gradle / Ivy

The newest version!
@file:UseContextualSerialization(Duration::class)

package com.lightningkite.lightningserver.auth

import com.lightningkite.lightningserver.exceptions.UnauthorizedException
import com.lightningkite.lightningserver.serialization.Serialization
import com.lightningkite.lightningserver.settings.generalSettings
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.descriptors.getContextualDescriptor
import java.security.SecureRandom
import java.time.Duration
import java.util.*


private val availableCharacters =
    "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~!@#%^&*()_+`-=[]{};':,./<>?"

/**
 * AuthSettings holds the values required to setup JWT Authentication.
 * This will be used by nearly every function in the auth package.
 * @param expirationMilliseconds The default expiration for tokens. This can be overridden for a specific token.
 * @param secret THis should be a long and complicated String. The jwtSecret should never be shared since it is what's used to sign JWTs.
 */
@Serializable
data class JwtSigner(
    val expiration: Duration = Duration.ofDays(365),
    val emailExpiration: Duration = Duration.ofHours(1),
    val secret: String = buildString {
        val rand = SecureRandom.getInstanceStrong()
        repeat(64) {
            append(
                availableCharacters[rand.nextInt(availableCharacters.length)]
            )
        }
    },
    val issuer: String? = null,
    val audience: String? = null
) {

    @kotlinx.serialization.Transient
    val hasher = SecureHasher.HS256(secret.toByteArray())

    /**
     * @return A JWT with the [subject], expiring in [expireDuration].
     */
    fun token(subject: String, expireDuration: Duration = expiration): String =
        Serialization.json.encodeJwt(
            hasher,
            subject,
            expireDuration,
            issuer ?: generalSettings().publicUrl,
            audience ?: generalSettings().publicUrl
        )

    /**
     * Returns the subject if the token was valid.
     */
    fun verify(token: String): String {
        return try {
            Serialization.json.decodeJwt(hasher, token, audience ?: generalSettings().publicUrl)
        } catch (e: JwtExpiredException) {
            throw UnauthorizedException(
                message = "This authorization has expired.",
                cause = e
            )
        } catch (e: JwtException) {
            throw UnauthorizedException(
                message = "Invalid token",
                cause = e
            )
        } catch (e: Exception) {
            throw UnauthorizedException(
                message = "Invalid token",
                cause = e
            )
        }
    }


    @Deprecated(
        "Use the version with duration instead",
        ReplaceWith("token(subject, Duration.ofMillis(expireDuration))", "java.time.Duration")
    )
    inline fun  token(subject: T, expireDuration: Long): String =
        token(Serialization.module.serializer(), subject, Duration.ofMillis(expireDuration))

    @Deprecated(
        "Use the version with duration instead",
        ReplaceWith("token(serializer, subject, Duration.ofMillis(expireDuration))", "java.time.Duration")
    )
    fun  token(serializer: KSerializer, subject: T, expireDuration: Long): String =
        token(serializer, subject, Duration.ofMillis(expireDuration))

    inline fun  token(subject: T, expireDuration: Duration = expiration): String =
        token(Serialization.module.serializer(), subject, expireDuration)

    fun  token(serializer: KSerializer, subject: T, expireDuration: Duration = expiration): String =
        Serialization.json.encodeJwt(
            hasher,
            serializer,
            subject,
            expireDuration,
            issuer ?: generalSettings().publicUrl,
            audience ?: generalSettings().publicUrl
        )

    inline fun  verify(token: String): T = verify(Serialization.module.serializer(), token)
    fun  verify(serializer: KSerializer, token: String): T {
        return try {
            Serialization.json.decodeJwt(hasher, serializer, token, audience ?: generalSettings().publicUrl)
        } catch (e: JwtExpiredException) {
            throw UnauthorizedException(
                message = "This authorization has expired.",
                cause = e
            )
        } catch (e: JwtException) {
            throw UnauthorizedException(
                message = "Invalid token",
                cause = e
            )
        } catch (e: Exception) {
            throw UnauthorizedException(
                message = "Invalid token",
                cause = e
            )
        }
    }

    private fun KSerializer<*>.isPrimitive(): Boolean {
        var current = this.descriptor
        while (true) {
            when (current.kind) {
                is PrimitiveKind -> return true
                SerialKind.CONTEXTUAL -> current =
                    Serialization.json.serializersModule.getContextualDescriptor(current)!!

                else -> return false
            }
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy