Skip to content

Commit

Permalink
Merge pull request #45 from walt-id/feat-siopv2
Browse files Browse the repository at this point in the history
Feat siopv2
  • Loading branch information
severinstampler authored Oct 28, 2021
2 parents 95f248d + 0a3f479 commit a3c26d1
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 8 deletions.
88 changes: 88 additions & 0 deletions src/main/kotlin/id/walt/model/siopv2/SIOPv2Request.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package id.walt.model.siopv2

import com.beust.klaxon.Json
import com.beust.klaxon.Klaxon
import id.walt.model.Claim
import io.javalin.http.Context
import java.net.URLEncoder
import java.nio.charset.StandardCharsets

data class SIOPv2Request(
val response_type: String = "id_token",
val client_id: String,
val redirect_uri: String,
val scope: String = "openid",
val nonce: String,
val registration: Registration = Registration(),
@Json("exp") val expiration: Long,
@Json("iat") val issuedAt: Long,
val claims: Claims

) {
private fun enc(str: String): String = URLEncoder.encode(str, StandardCharsets.UTF_8)
fun toUriQueryString(): String {
return "response_type=${enc(response_type)}&client_id=${enc(client_id)}&redirect_uri=${enc(redirect_uri)}" +
"&scope=${enc(scope)}&nonce=${enc(nonce)}&registration=${enc(Klaxon().toJsonString(registration))}" +
"&exp=$expiration&iat=$issuedAt&claims=${enc(Klaxon().toJsonString(claims))}"
}

companion object {
fun fromHttpContext(ctx: Context): SIOPv2Request {
val requiredParams = setOf("client_id", "redirect_uri", "nonce", "registration", "exp", "iat", "claims")
if (requiredParams.any { ctx.queryParam(it).isNullOrEmpty() })
throw IllegalArgumentException("HTTP context missing mandatory query parameters")
return SIOPv2Request(
ctx.queryParam("response_type") ?: "id_token",
ctx.queryParam("client_id")!!,
ctx.queryParam("redirect_uri")!!,
ctx.queryParam("scope") ?: "openid",
ctx.queryParam("nonce")!!,
Klaxon().parse<Registration>(ctx.queryParam("registration")!!)!!,
ctx.queryParam("exp")!!.toLong(),
ctx.queryParam("iat")!!.toLong(),
Klaxon().parse<Claims>(ctx.queryParam("claims")!!)!!
)
}
}
}

data class Registration(
val subject_identifier_types_supported: List<String> = listOf("did"),
val did_methods_supported: List<String> = listOf("did:ebsi:"),
val vp_formats: VPFormats = VPFormats(),
val client_name: String? = null,
val client_purpose: String? = null,
val tos_uri: String? = null,
val logo_uri: String? = null
)

data class VPFormats(
val jwt_vp: JwtVPFormat? = JwtVPFormat(),
val ldp_vp: LdpVpFormat? = LdpVpFormat()
)

data class JwtVPFormat (
val alg: Set<String> = setOf("EdDSA", "ES256K")
)

data class LdpVpFormat(
val proof_type: Set<String> = setOf("Ed25519Signature2018")
)

data class InputDescriptor (
val id: String,
val schema: String
)

data class PresentationDefinition (
val id: String,
val input_descriptors: List<InputDescriptor>
)

data class VpTokenClaim (
val presentation_definition: PresentationDefinition
)

data class Claims (
val vp_token: VpTokenClaim? = null
)
56 changes: 56 additions & 0 deletions src/main/kotlin/id/walt/model/siopv2/SIOPv2Response.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package id.walt.model.siopv2

import com.beust.klaxon.Json
import com.beust.klaxon.Klaxon
import id.walt.services.did.DidService
import id.walt.services.jwt.JwtService
import id.walt.services.vc.VcUtils
import id.walt.vclib.Helpers.encode
import id.walt.vclib.VcLibManager
import id.walt.vclib.vclist.VerifiablePresentation
import java.time.Instant
import java.time.temporal.Temporal
import java.util.*

data class SIOPv2Response (
val did: String,
val id_token: SIOPv2IDToken,
val vp_token: SIOPv2VPToken
) {
fun getIdToken(): String {
return JwtService.getService().sign(did, Klaxon().toJsonString(id_token))
}

fun getVpToken(): String {
return JwtService.getService().sign(did, Klaxon().toJsonString(vp_token))
}
}

data class SIOPv2IDToken(
@Json("iss") val issuer: String = "https://self-issued.me/v2",
@Json("sub") val subject: String,
@Json("aud") val client_id: String,
@Json("exp") val expiration: Long = Instant.now().plusSeconds(60*60).epochSecond,
@Json("iat") val issueDate: Long = Instant.now().epochSecond,
val nonce: String)

data class SIOPv2VPToken(
val vp_token: List<SIOPv2Presentation>
)

data class SIOPv2Presentation(
val format: String,
val presentation: String
) {
companion object {
fun createFromVPString(vpStr: String): SIOPv2Presentation {
return SIOPv2Presentation(
format = when(VcLibManager.isJWT(vpStr)) {
true -> "jwt_vp"
else -> "ldp_vp"
},
presentation = vpStr
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,15 @@ object CustodianController {
// )
fun listCredentialsDocs() = document()
.operation { it.summary("Lists all credentials the custodian knows of").operationId("listCredentials").addTagsItem("Credentials") }
.queryParam<String>("id", isRepeatable = true)
.json<String>("200") { it.description("Credentials list") }

fun listCredentials(ctx: Context) {
ctx.json(ListCredentialsResponse(custodian.listCredentials()))
val ids = ctx.queryParams("id").toSet()
if(ids.isEmpty())
ctx.json(ListCredentialsResponse(custodian.listCredentials()))
else
ctx.json(ListCredentialsResponse(custodian.listCredentials().filter { it.id != null && ids.contains(it.id!!)}))
}

// @OpenApi(
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/id/walt/services/did/DidService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ object DidService {
KeyService.getService().delete(id!!)
pubKeyJwk!!.kid = id
WaltIdServices.log.debug { "Importing key: ${pubKeyJwk.kid}" }
KeyService.getService().import(Klaxon().toJsonString(pubKeyJwk))
val keyId = KeyService.getService().import(Klaxon().toJsonString(pubKeyJwk))
WaltContext.keyStore.addAlias(keyId, didStr)
return true
}
else -> {
Expand Down
38 changes: 32 additions & 6 deletions src/main/kotlin/id/walt/signatory/CLIDataProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package id.walt.signatory

import id.walt.vclib.model.VerifiableCredential
import id.walt.vclib.vclist.VerifiableDiploma
import id.walt.vclib.vclist.VerifiableId
import java.util.*

object CLIDataProviders {
fun getCLIDataProviderFor(templateId: String): SignatoryDataProvider? {
return when(templateId) {
"VerifiableDiploma" -> VerifiableDiplomaCLIDataProvider()
"VerifiableId" -> VerifiableIDCLIDataProvider()
else -> null
}
}
Expand All @@ -32,14 +34,14 @@ abstract class CLIDataProvider : SignatoryDataProvider {
class VerifiableDiplomaCLIDataProvider : CLIDataProvider() {
override fun populate(template: VerifiableCredential, proofConfig: ProofConfig): VerifiableCredential {
template as VerifiableDiploma

template.apply {
id = proofConfig.credentialId ?: "education#higherEducation#${UUID.randomUUID()}"
issuer = proofConfig.issuerDid
if (proofConfig.issueDate != null) issuanceDate = dateFormat.format(proofConfig.issueDate)
if (proofConfig.expirationDate != null) expirationDate = dateFormat.format(proofConfig.expirationDate)
validFrom = issuanceDate
validFrom = issuanceDate

credentialSubject!!.apply {
id = proofConfig.subjectDid

Expand Down Expand Up @@ -67,10 +69,10 @@ class VerifiableDiplomaCLIDataProvider : CLIDataProvider() {
id = proofConfig.issuerDid
preferredName = prompt("Preferred name", preferredName) ?: ""
homepage = prompt("Homepage", homepage) ?: ""
registration = prompt("Registration", registration) ?: ""
registration = prompt("Registration", registration) ?: ""
}
}

println()
println("Grading scheme")
println("----------------------")
Expand Down Expand Up @@ -101,7 +103,31 @@ class VerifiableDiplomaCLIDataProvider : CLIDataProvider() {
}
}
}

return template
}
}

class VerifiableIDCLIDataProvider : CLIDataProvider() {
override fun populate(vc: VerifiableCredential, proofConfig: ProofConfig): VerifiableCredential {
vc as VerifiableId
vc.id = proofConfig.credentialId ?: "education#higherEducation#${UUID.randomUUID()}"
vc.issuer = proofConfig.issuerDid
if (proofConfig.issueDate != null) vc.issuanceDate = dateFormat.format(proofConfig.issueDate)
if (proofConfig.expirationDate != null) vc.expirationDate = dateFormat.format(proofConfig.expirationDate)
vc.validFrom = vc.issuanceDate
vc.credentialSubject!!.id = proofConfig.subjectDid

println()
println("Subject personal data, ID: ${proofConfig.subjectDid}")
println("----------------------")
vc.credentialSubject!!.firstName = prompt("First name", vc.credentialSubject!!.firstName)
vc.credentialSubject!!.familyName = prompt("Family name", vc.credentialSubject!!.familyName)
vc.credentialSubject!!.dateOfBirth = prompt("Date of birth", vc.credentialSubject!!.dateOfBirth)
vc.credentialSubject!!.gender = prompt("Gender", vc.credentialSubject!!.gender)
vc.credentialSubject!!.placeOfBirth = prompt("Place of birth", vc.credentialSubject!!.placeOfBirth)
vc.credentialSubject!!.currentAddress = prompt("Current address", vc.credentialSubject!!.currentAddress)

return vc
}
}

0 comments on commit a3c26d1

Please sign in to comment.