Skip to content

Commit

Permalink
refactor: refactor jwt module
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoe Maas committed Oct 9, 2024
1 parent 6bfd979 commit bb5f34d
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.sphereon.oid.fed.common.jwt

/**
* The main object used by code to be calling into the platform specific callbacks for signature creation/verification
*
* Non Kotlin code could still extend their implementations. This object is available directly as well except for JS, which has to use its actual implementations this service depends on
*/

object JwtService {
val JWT = jwtService()
}

/**
* The main entry point for platform validation, delegating to a platform specific callback implemented by external developers
*/

interface ICallbackService<PlatformCallbackType> {
/**
* Disable callback verification (be careful!)
*/
fun disable(): PlatformCallbackType

/**
* Enable the callback verification (default)
*/
fun enable(): PlatformCallbackType


/**
* Is the service enabled or not
*/
fun isEnabled(): Boolean

/**
* Register the platform specific callback that implements the verification
*
* External developers use this as an entry point for their platform code
*/
fun register(platformCallback: PlatformCallbackType): ICallbackService<PlatformCallbackType>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package com.sphereon.oid.fed.common.jwt

import com.sphereon.oid.fed.openapi.models.JWTHeader
import com.sphereon.oid.fed.openapi.models.Jwk
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonElement
import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport
import kotlin.jvm.JvmStatic

@ExperimentalJsExport
@JsExport
data class JwtSignInput (
val payload: JsonObject,
val payload: Map<String, JsonElement>,
val header: JWTHeader,
val key: Jwk
)
Expand All @@ -21,9 +22,62 @@ data class JwtVerifyInput (
val key: Jwk
)

@ExperimentalJsExport
@JsExport
interface JwtService {
fun sign(input: JwtSignInput): String
fun verify(input: JwtVerifyInput): Boolean
interface IJwtService {
suspend fun sign(input: JwtSignInput): String
suspend fun verify(input: JwtVerifyInput): Boolean
}

interface JwtCallbackService: ICallbackService<IJwtService>, IJwtService

expect fun jwtService(): JwtCallbackService

object JwtServiceObject: JwtCallbackService {

@JvmStatic
private lateinit var platformCallback: IJwtService

private var disabled = false;

@OptIn(ExperimentalJsExport::class)
override suspend fun sign(input: JwtSignInput): String {
if (!isEnabled()) {
JwtConst.LOG.info("JWT sign has been disabled")
throw IllegalStateException("JWT sign is disabled cannot sign")
} else if (!this::platformCallback.isInitialized) {
JwtConst.LOG.info("JWT callback (JS) is not registered")
throw IllegalStateException("JWT have not been initialized. Please register your JWTCallbackJS, or a default implementation")
}
return platformCallback.sign(input)
}

@OptIn(ExperimentalJsExport::class)
override suspend fun verify(input: JwtVerifyInput): Boolean {
if (!isEnabled()) {
JwtConst.LOG.info("JWT verify has been disabled")
throw IllegalStateException("JWT verify is disabled cannot sign")
} else if (!this::platformCallback.isInitialized) {
JwtConst.LOG.info("JWT callback (JS) is not registered")
throw IllegalStateException("JWT have not been initialized. Please register your JWTCallbackJS, or a default implementation")
}
return platformCallback.verify(input)
}

override fun disable(): IJwtService {
this.disabled = true
return this
}

override fun enable(): IJwtService {
this.disabled = false
return this
}

override fun isEnabled(): Boolean {
return !this.disabled
}

override fun register(platformCallback: IJwtService): JwtCallbackService {
this.platformCallback = platformCallback
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sphereon.oid.fed.common.jwt

import com.sphereon.oid.fed.common.logging.Logger

object JwtConst {
val LOG_NAMESPACE = "sphereon:kmp:openid-federation-common"
val LOG = Logger.Static.tag(LOG_NAMESPACE)
val JWT_LITERAL = "JWT"
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
package com.sphereon.oid.fed.common.logging

import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity

object Logger {

fun verbose(tag: String, message: String) {
class Logger(val tag: String = "") {
fun verbose(message: String, tag: String = this.tag) {
Logger.v(tag = tag, messageString = message)
}

fun debug(tag: String, message: String) {
fun debug(message: String, tag: String = this.tag) {
Logger.d(tag = tag, messageString = message)
}

fun info(tag: String, message: String) {
fun info(message: String, tag: String = this.tag) {
Logger.i(tag = tag, messageString = message)
}

fun warn(tag: String, message: String) {
fun warn(message: String, tag: String = this.tag) {
Logger.w(tag = tag, messageString = message)
}

fun error(tag: String, message: String, throwable: Throwable? = null) {
fun error(message: String, throwable: Throwable? = null, tag: String = this.tag) {
Logger.e(tag = tag, messageString = message, throwable = throwable)
}
}

fun setMinSeverity(severity: Severity) = Logger.setMinSeverity(severity)

object Static {
fun tag(tag: String = "", severity: Severity = Severity.Info) = Logger(tag).also { it.setMinSeverity(severity) }
}

}

val DefaultLogger = Logger("")
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.sphereon.oid.fed.common.jwt

/**
* The main object used by code to be calling into the platform specific callbacks for signature creation/verification
*
* Non Kotlin code could still extend their implementations. This object is available directly as well except for JS, which has to use its actual implementations this service depends on
*/

@ExperimentalJsExport
@JsExport
object JwtServiceJS {
val JWT = jwtService()
}

/**
* The main entry point for platform validation, delegating to a platform specific callback implemented by external developers
*/

@ExperimentalJsExport
@JsExport
interface ICallbackServiceJS<PlatformCallbackType> {
/**
* Disable callback verification (be careful!)
*/
fun disable(): PlatformCallbackType

/**
* Enable the callback verification (default)
*/
fun enable(): PlatformCallbackType


/**
* Is the service enabled or not
*/
fun isEnabled(): Boolean

/**
* Register the platform specific callback that implements the verification
*
* External developers use this as an entry point for their platform code
*/
@JsName("register")
fun register(platformCallback: PlatformCallbackType): ICallbackServiceJS<PlatformCallbackType>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.sphereon.oid.fed.common.jwt

import kotlinx.coroutines.await
import kotlin.js.Promise

@ExperimentalJsExport
@JsExport
interface IJwtServiceJS {
fun sign(input: JwtSignInput): Promise<String>
fun verify(input: JwtVerifyInput): Promise<Boolean>
}

@ExperimentalJsExport
@JsExport
interface JwtCallbackServiceJS: ICallbackService<IJwtServiceJS>, IJwtServiceJS

@ExperimentalJsExport
@JsExport
object JwtServiceObjectJS: JwtCallbackServiceJS {

private lateinit var platformCallback: IJwtServiceJS

private var disabled = false;

override fun disable(): IJwtServiceJS {
this.disabled = true
return this
}

override fun enable(): IJwtServiceJS {
this.disabled = false
return this
}

override fun isEnabled(): Boolean {
return !this.disabled
}

override fun register(platformCallback: IJwtServiceJS): JwtCallbackServiceJS {
this.platformCallback = platformCallback
return this
}


override fun sign(input: JwtSignInput): Promise<String> {
if (!isEnabled()) {
JwtConst.LOG.info("JWT sign has been disabled")
throw IllegalStateException("JWT sign is disabled cannot sign")
} else if (!this::platformCallback.isInitialized) {
JwtConst.LOG.info("JWT callback (JS) is not registered")
throw IllegalStateException("JWT have not been initialized. Please register your JWTCallbackJS, or a default implementation")
}
return platformCallback.sign(input)
}

override fun verify(input: JwtVerifyInput): Promise<Boolean> {
if (!isEnabled()) {
JwtConst.LOG.info("JWT verify has been disabled")
throw IllegalStateException("JWT verify is disabled cannot sign")
} else if (!this::platformCallback.isInitialized) {
JwtConst.LOG.info("JWT callback (JS) is not registered")
throw IllegalStateException("JWT have not been initialized. Please register your JWTCallbackJS, or a default implementation")
}
return platformCallback.verify(input)
}
}

open class JwtServiceJSAdapter @OptIn(ExperimentalJsExport::class) constructor(val jwtcallbackJS: JwtServiceObjectJS = JwtServiceObjectJS):
JwtCallbackService {
@OptIn(ExperimentalJsExport::class)
override fun disable(): IJwtService {
this.jwtcallbackJS.disable()
return this
}

@OptIn(ExperimentalJsExport::class)
override fun enable(): IJwtService {
this.jwtcallbackJS.enable()
return this
}

@OptIn(ExperimentalJsExport::class)
override fun isEnabled(): Boolean {
return this.jwtcallbackJS.isEnabled()
}

override fun register(platformCallback: IJwtService): ICallbackService<IJwtService> {
throw Error("Register function should not be used on the adapter. It depends on the Javascript jwtService object")
}

@OptIn(ExperimentalJsExport::class)
override suspend fun sign(input: JwtSignInput): String {
JwtConst.LOG.error("Creating JWT sign signature...")
return try {
this.jwtcallbackJS.sign(input).await()
} catch (e: Exception) {
throw e
}.also {
JwtConst.LOG.error("Created JWT sign signature: $it")
}
}

@OptIn(ExperimentalJsExport::class)
override suspend fun verify(input: JwtVerifyInput): Boolean {
JwtConst.LOG.error("Verifying JWT signature...")
return try {
this.jwtcallbackJS.verify(input).await()
} catch (e: Exception) {
throw e
}.also {
JwtConst.LOG.error("Verified JWT signature: $it")
}
}

}

@OptIn(ExperimentalJsExport::class)
object JWTServiceJSAdapterObject: JwtServiceJSAdapter(JwtServiceObjectJS)

actual fun jwtService(): JwtCallbackService = JWTServiceJSAdapterObject
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sphereon.oid.fed.common.jwt

actual fun jwtService(): JwtCallbackService = JwtServiceObject

0 comments on commit bb5f34d

Please sign in to comment.