Skip to content
This repository has been archived by the owner on Jan 9, 2024. It is now read-only.

Commit

Permalink
Move VC Cards to single component, add Selective Disclosure marking, …
Browse files Browse the repository at this point in the history
…added listing of selectively disclosure-able properties
  • Loading branch information
waltkb committed Nov 22, 2023
1 parent b63bef1 commit be12cb2
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 72 deletions.
4 changes: 2 additions & 2 deletions src/main/kotlin/id/walt/service/SSIKit2WalletService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ class SSIKit2WalletService(accountId: UUID, walletId: UUID) : WalletService(acco

override suspend fun deleteCredential(id: String) = transaction { CredentialsService.delete(walletId, id) }

override suspend fun getCredential(credentialId: String): String =
CredentialsService.get(walletId, credentialId)?.document
override suspend fun getCredential(credentialId: String): WalletCredential =
CredentialsService.get(walletId, credentialId)
?: throw IllegalArgumentException("WalletCredential not found for credentialId: $credentialId")

private fun getQueryParams(url: String): Map<String, MutableList<String>> {
Expand Down
18 changes: 11 additions & 7 deletions src/main/kotlin/id/walt/service/WalletKitWalletService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package id.walt.service

import id.walt.config.ConfigManager
import id.walt.config.RemoteWalletConfig
import id.walt.db.models.Accounts
import id.walt.db.models.WalletDid
import id.walt.db.models.WalletOperationHistories
import id.walt.db.models.WalletOperationHistory
import id.walt.db.models.*
import id.walt.service.dids.DidsService
import id.walt.service.dto.LinkedWalletDataTransferObject
import id.walt.service.dto.WalletDataTransferObject
Expand All @@ -23,6 +20,7 @@ import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Instant
import kotlinx.datetime.toJavaInstant
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
Expand Down Expand Up @@ -138,9 +136,15 @@ class WalletKitWalletService(accountId: UUID, walletId: UUID) : WalletService(ac
override suspend fun deleteCredential(id: String) =
authenticatedJsonDelete("/api/wallet/credentials/delete/$id").status.isSuccess()

override suspend fun getCredential(credentialId: String): String =
/*prettyJson.encodeToString(*/
listCredentials().first { it.parsedCredential["id"]?.jsonPrimitive?.content == credentialId }.toString()//)
override suspend fun getCredential(credentialId: String) = WalletCredential(
wallet = walletId,
id = credentialId,
document = listCredentials().first { it.parsedCredential["id"]?.jsonPrimitive?.content == credentialId }.toString(),
disclosures = null,
addedOn = Instant.DISTANT_PAST
)
/*prettyJson.encodeToString(*/
//)
/* override suspend fun getCredential(credentialId: String): String =
authenticatedJsonGet("/api/wallet/credentials/$credentialId")
.bodyAsText()*/
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/id/walt/service/WalletService.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.service

import id.walt.db.models.WalletCredential
import id.walt.db.models.WalletDid
import id.walt.db.models.WalletOperationHistory
import id.walt.service.dto.LinkedWalletDataTransferObject
Expand All @@ -15,7 +16,7 @@ abstract class WalletService(val accountId: UUID, val walletId: UUID) {
abstract fun listCredentials(): List<Credential>
abstract suspend fun listRawCredentials(): List<String>
abstract suspend fun deleteCredential(id: String): Boolean
abstract suspend fun getCredential(credentialId: String): String
abstract suspend fun getCredential(credentialId: String): WalletCredential

// SIOP
abstract suspend fun usePresentationRequest(request: String, did: String): Result<String?>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package id.walt.web.controllers

import id.walt.db.models.WalletCredential
import id.walt.web.getWalletService
import io.github.smiley4.ktorswaggerui.dsl.delete
import io.github.smiley4.ktorswaggerui.dsl.get
Expand Down Expand Up @@ -45,7 +46,7 @@ fun Application.credentials() = walletRoute {
summary = "View a credential"
response {
HttpStatusCode.OK to {
body<String> {
body<WalletCredential> {
description =
"WalletCredential in JWT (String starting with 'ey' or JSON_LD (JSON with proof) format"
}
Expand Down
47 changes: 47 additions & 0 deletions web/src/components/credentials/VerifiableCredentialCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div class="bg-white p-6 rounded-2xl shadow-2xl h-full">
<div class="flex justify-end gap-1.5">
<div
:class="credential.expirationDate
? (new Date(credential.expirationDate).getTime() > new Date().getTime()
? 'bg-cyan-100' : 'bg-red-50') : 'bg-cyan-50'
"
class="rounded-lg px-3 mb-2"
>
<span
:class="
credential.expirationDate
? new Date(credential.expirationDate).getTime() > new Date().getTime()
? 'text-cyan-900'
: 'text-orange-900'
: 'text-cyan-900'
"
>{{ credential.expirationDate ?
(new Date(credential.expirationDate).getTime() > new Date().getTime() ? "Valid" : "Expired")
: "Valid" }}
</span>
</div>

<div class="rounded-lg px-3 mb-2 bg-cyan-100">
<span>SD</span>
</div>
</div>
<h2 class="text-2xl font-bold text-gray-900 bold mb-8">
{{ credential.type[credential.type.length - 1].replace(/([a-z0-9])([A-Z])/g, "$1 $2") }}
</h2>
<div v-if="credential.issuer" class="flex items-center">
<img :src="credential.issuer?.image?.id ? credential.issuer?.image?.id : credential.issuer?.image" class="w-12" />
<div class="text-natural-600 ml-2 w-32">
{{ credential.issuer?.name }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
const props = defineProps(["credential"]);
</script>
<style scoped>
</style>
102 changes: 69 additions & 33 deletions web/src/pages/wallet/[wallet]/credentials/[credentialId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,30 @@
<LoadingIndicator v-if="pending">Loading credential...</LoadingIndicator>
<div v-else>
<div class="flex justify-center items-center my-10">
<div class="bg-white p-6 rounded-2xl shadow-2xl h-full w-[350px]">
<div class="flex justify-end">
<div
:class="jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? 'bg-cyan-50' : 'bg-red-50') : 'bg-cyan-50'"
class="rounded-lg px-3 mb-2"
>
<div :class="jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? 'text-cyan-900' : 'text-orange-900') : 'text-cyan-900'">
{{ jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? "Valid" : "Expired") : "Valid" }}
</div>
</div>
</div>
<h2 class="text-2xl font-bold mb-2 text-gray-900 bold mb-8">
{{ jwtJson?.type[jwtJson?.type.length - 1].replace(/([a-z0-9])([A-Z])/g, "$1 $2") }}
</h2>
<div v-if="jwtJson?.issuer" class="flex items-center">
<img :src="jwtJson?.issuer?.image?.id ? jwtJson?.issuer?.image?.id : jwtJson?.issuer?.image" class="w-12" />
<div class="text-natural-600 ml-2 w-32">
{{ jwtJson?.issuer?.name }}
</div>
</div>
</div>
<!-- <div class="bg-white p-6 rounded-2xl shadow-2xl h-full w-[350px]">
<div class="flex justify-end">
<div
:class="jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? 'bg-cyan-50' : 'bg-red-50') : 'bg-cyan-50'"
class="rounded-lg px-3 mb-2"
>
<div
:class="jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? 'text-cyan-900' : 'text-orange-900') : 'text-cyan-900'"
>
{{ jwtJson?.expirationDate ? (new Date(jwtJson?.expirationDate).getTime() > new Date().getTime() ? "Valid" : "Expired") : "Valid" }}
</div>
</div>
</div>
<h2 class="text-2xl font-bold mb-2 text-gray-900 bold mb-8">
{{ jwtJson?.type[jwtJson?.type.length - 1].replace(/([a-z0-9])([A-Z])/g, "$1 $2") }}
</h2>
<div v-if="jwtJson?.issuer" class="flex items-center">
<img :src="jwtJson?.issuer?.image?.id ? jwtJson?.issuer?.image?.id : jwtJson?.issuer?.image" class="w-12" />
<div class="text-natural-600 ml-2 w-32">
{{ jwtJson?.issuer?.name }}
</div>
</div>
</div>-->
<VerifiableCredentialCard :credential="jwtJson" />
</div>
<div class="px-7 py-1">
<div class="text-gray-600 font-bold">
Expand Down Expand Up @@ -214,6 +217,19 @@
{{ jwtJson?.issuer?.id ?? jwtJson?.issuer }}
</div>
</div>

<div v-if="disclosures">
<hr class="my-5" />
<div class="text-gray-500 mb-4 font-bold">Selectively disclosure-able attributes</div>
<ul v-if="disclosures.length > 0">
<li class="md:flex text-gray-500 mb-3 md:mb-1" v-for="disclosure in disclosures">
<div class="min-w-[19vw]">Attribute "{{ disclosure[1] }}"</div>
<div class="font-bold">{{ disclosure[2] }}</div>
</li>
</ul>
<div v-else>No disclosure-able attributes!</div>
</div>

<hr class="mt-5 mb-3" />
<div class="text-gray-600 flex justify-between">
<div>
Expand Down Expand Up @@ -266,36 +282,37 @@
</div>-->
<div class="shadow p-3 mt-2 font-mono overflow-scroll">
<h3 class="font-semibold mb-2">JWT</h3>
<pre v-if="credential && credential.length">{{
/*JSON.stringify(JSON.parse(*/
credential /*), null, 2)*/ ?? ""
}}</pre>
<pre v-if="credential && credential?.document">{{
/*JSON.stringify(JSON.parse(*/
credential.document /*), null, 2)*/ ?? ""
}}</pre>
</div>
<div class="shadow p-3 mt-2 font-mono overflow-scroll">
<h3 class="font-semibold mb-2">JSON</h3>
<pre v-if="credential && credential.length">{{ jwtJson }} </pre>
<pre v-if="credential && credential?.document">{{ jwtJson }} </pre>
</div>
</div>
</div>
</CenterMain>
</template>

<script setup>
<script lang="ts" setup>
import LoadingIndicator from "~/components/loading/LoadingIndicator.vue";
import CenterMain from "~/components/CenterMain.vue";
import BackButton from "~/components/buttons/BackButton.vue";
import { ref } from "vue";
import { decodeBase64ToUtf8 } from "~/composables/base64";
import VerifiableCredentialCard from "~/components/credentials/VerifiableCredentialCard.vue";

const route = useRoute();
const credentialId = route.params.credentialId;
const currentWallet = useCurrentWallet()
const credentialId = route.params.credentialId as string;
const currentWallet = useCurrentWallet();

let showCredentialJson = ref(false);

const jwtJson = computed(() => {
if (credential.value) {
const vcData = credential.value.split(".")[1];
const vcData = credential.value.document.split(".")[1];
console.log("Credential is: ", vcData);

const vcBase64 = vcData.replaceAll("-", "+").replaceAll("_", "/");
Expand All @@ -308,17 +325,36 @@ const jwtJson = computed(() => {

if (parsed.vc) return parsed.vc;
else return parsed;
} else return "";
} else return null;
});

const disclosures = computed(() => {
if (credential.value && credential.value.disclosures) {
try {
return credential.value.disclosures.split("~").map((elem) => JSON.parse(decodeBase64ToUtf8(elem)));
} catch (e) {
console.error(e);
return [];
}
} else return null;
});

const { data: credential, pending, refresh, error } = await useLazyFetch(`/r/wallet/${currentWallet.value}/credentials/${encodeURIComponent(credentialId)}`);
type WalletCredential = {
wallet: string,
id: string,
document: string,
disclosures: string | null,
addedOn: string,
}

const { data: credential, pending, refresh, error } = await useLazyFetch<WalletCredential>(`/r/wallet/${currentWallet.value}/credentials/${encodeURIComponent(credentialId)}`);
refreshNuxtData();

useHead({ title: "View credential - walt.id" });

async function deleteCredential() {
await $fetch(`/r/wallet/${currentWallet.value}/credentials/${encodeURIComponent(credentialId)}`, {
method: "DELETE",
method: "DELETE"
});
await navigateTo({ path: "/" });
}
Expand Down
30 changes: 2 additions & 28 deletions web/src/pages/wallet/[wallet]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,7 @@
class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow w-[96%] transform hover:scale-105 cursor-pointer duration-200"
>
<NuxtLink :to="`/wallet/${walletId}/credentials/` + encodeURIComponent(credential.id)">
<div class="bg-white p-6 rounded-2xl shadow-2xl h-full">
<div class="flex justify-end">
<div
:class="credential.expirationDate ? (new Date(credential.expirationDate).getTime() > new Date().getTime() ? 'bg-cyan-50' : 'bg-red-50') : 'bg-cyan-50'"
class="rounded-lg px-3 mb-2"
>
<span
:class="
credential.expirationDate
? new Date(credential.expirationDate).getTime() > new Date().getTime()
? 'text-cyan-900'
: 'text-orange-900'
: 'text-cyan-900'
"
>{{ credential.expirationDate ? (new Date(credential.expirationDate).getTime() > new Date().getTime() ? "Valid" : "Expired") : "Valid" }}</span
>
</div>
</div>
<h2 class="text-2xl font-bold mb-2 text-gray-900 bold mb-8">
{{ credential.type[credential.type.length - 1].replace(/([a-z0-9])([A-Z])/g, "$1 $2") }}
</h2>
<div v-if="credential.issuer" class="flex items-center">
<img :src="credential.issuer?.image?.id ? credential.issuer?.image?.id : credential.issuer?.image" class="w-12" />
<div class="text-natural-600 ml-2 w-32">
{{ credential.issuer?.name }}
</div>
</div>
</div>
<VerifiableCredentialCard :credential="credential"/>
</NuxtLink>
</li>
</ul>
Expand All @@ -79,6 +52,7 @@ import { PlusIcon } from "@heroicons/vue/24/outline";
import WalletPageHeader from "~/components/WalletPageHeader.vue";
import LoadingIndicator from "~/components/loading/LoadingIndicator.vue";
import CenterMain from "~/components/CenterMain.vue";
import VerifiableCredentialCard from "~/components/credentials/VerifiableCredentialCard.vue";
const config = useRuntimeConfig();
Expand Down

0 comments on commit be12cb2

Please sign in to comment.