Skip to content

Commit

Permalink
Fixed token serialization (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimble authored Aug 22, 2022
1 parent 7ae34bf commit 20afc6a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import com.github.oslokommune.oslonokkelen.adapter.thing.ThingDescription
import com.github.oslokommune.oslonokkelen.adapter.thing.ThingId
import com.github.oslokommune.oslonokkelen.adapter.thing.ThingState
import com.github.oslokommune.oslonokkelen.adapter.thing.ThingStateSnapshot
import com.google.gson.JsonObject
import com.google.protobuf.util.JsonFormat
import com.nimbusds.jwt.JWTClaimsSet
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf
Expand All @@ -33,15 +31,61 @@ object ProtobufParser {


fun parseActionRequestFromClaims(verifiedClaims: JWTClaimsSet) : Adapter.ActionRequest {
val requestClaim = verifiedClaims.getClaim("request")
val request = requestClaim as? JsonObject ?: throw IllegalStateException("Missing request claim")
val jsonParser = JsonFormat.parser()
val requestClaim = verifiedClaims.getJSONObjectClaim("request")
val requestBuilder = Adapter.ActionRequest.newBuilder()
jsonParser.merge(request.toString(), requestBuilder)
.setRequestId(requireString(requestClaim, "requestId"))
.setThingId(requireString(requestClaim, "thingId"))
.setActionId(requireString(requestClaim, "actionId"))
.setTimeBudgetMillis(requireLong(requestClaim, "timeBudgetMillis").toInt())
.addAllAttachments(requireList(requestClaim, "attachments").mapNotNull { attachment ->
if (attachment is Map<*, *>) {
val key = attachment.keys.firstOrNull() as? String

when (key) {
"norwegianFodselsnummer" -> {
val value = attachment.values.firstOrNull() as Map<*, *>

Adapter.Attachment.newBuilder()
.setNorwegianFodselsnummer(Adapter.Attachment.NorwegianFodselsnummer.newBuilder()
.setNumber(requireString(value, "number"))
.build())
.build()
}
else -> {
null
}
}
} else {
null
}
})

return requestBuilder.build()
}

private fun requireString(requestClaim: Map<*, *>, key: String): String {
val value = requestClaim[key] as? String

return if (!value.isNullOrBlank()) {
value
} else {
throw missingKey(key, requestClaim)
}
}

private fun requireLong(requestClaim: Map<String, Any>, key: String): Long {
return requestClaim[key] as? Long ?: throw missingKey(key, requestClaim)
}

private fun requireList(requestClaim: Map<String, Any>, key: String): List<*> {
val value = requestClaim[key] as? List<*>
return value ?: throw missingKey(key, requestClaim)
}

private fun missingKey(key: String,requestClaim: Map<*, *>): IllegalStateException {
return IllegalStateException("Missing key $key (found: ${requestClaim.keys.joinToString(", ")})")
}

fun parse(serializedRequest: Adapter.ActionRequest): AdapterActionRequest {
return AdapterActionRequest(
requestId = serializedRequest.requestId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.github.oslokommune.oslonokkelen.adapter.tokens.generator

import com.github.oslokommune.oslonokkelen.adapter.action.AdapterActionRequest
import com.github.oslokommune.oslonokkelen.adapter.protobuf.ProtobufSerializer
import com.google.gson.JsonParser
import com.google.protobuf.util.JsonFormat
import com.github.oslokommune.oslonokkelen.adapter.action.AdapterAttachment
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.crypto.ECDSASigner
Expand Down Expand Up @@ -43,32 +41,40 @@ class BackendTokenGenerator(
}

fun createActionRequestToken(remoteUri: URI, request: AdapterActionRequest): SignedJWT {
val parsedRequest = serializeRequestAsJson(request)
val requestClaim = mapOf(
"thingId" to request.actionId.thingId.value,
"actionId" to request.actionId.value,
"timeBudgetMillis" to request.timeBudget.toMillis(),
"requestId" to request.requestId,
"attachments" to request.attachments.mapNotNull { attachment ->
when (attachment) {
is AdapterAttachment.NorwegianFodselsnummer -> {
mapOf(
"norwegianFodselsnummer" to mapOf(
"number" to attachment.number
)
)
}

// These are response attachments
is AdapterAttachment.Code,
is AdapterAttachment.DeniedReason,
is AdapterAttachment.EndUserMessage,
is AdapterAttachment.ErrorDescription,
is AdapterAttachment.PunchCard -> null
}
}
)

return buildToken {
audience("${remoteUri.scheme}://${remoteUri.host}")
issuer("${oslonokkelenBackendUri.scheme}://${oslonokkelenBackendUri.host}")
jwtID(jwtIdGenerator())
claim("scope", listOf("action:execute"))
claim("request", parsedRequest)
claim("request", requestClaim)
}
}

/**
* This is a bit of a hack..
*
* We use protobuf to describe messages and it is possible to serialize the
* Java classes generated from the .proto files to json, BUT the jwt library
* can't work with these classes so we have to serialize the request to json
* and then back to classes Nimbus JWT can work with in order to embed the
* request in the token.
*/
private fun serializeRequestAsJson(request: AdapterActionRequest): Any? {
val protobufRequest = ProtobufSerializer.serialize(request)
val jsonRequest = JsonFormat.printer().print(protobufRequest)
return JsonParser.parseString(jsonRequest)
}

fun buildToken(builderBlock: JWTClaimsSet.Builder.() -> Unit): SignedJWT {
val claims = createTokenClaims(builderBlock)
val signingKey = tokenSigningKeySupplier.signingKeyFor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class BackendTokenGeneratorTest {
println(token.serialize())
}


companion object {

private val rightNow = Instant.now()
Expand Down

0 comments on commit 20afc6a

Please sign in to comment.