Skip to content

Commit

Permalink
Merge pull request #757 from walt-id/releases/0.7.0
Browse files Browse the repository at this point in the history
Releases/0.7.0
  • Loading branch information
philpotisk committed Sep 18, 2024
2 parents f5c62f1 + 19c02f7 commit fb6aa1b
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 191 deletions.
2 changes: 1 addition & 1 deletion docker-compose/.env
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ MSSQL_DB_PORT=1433

#sqlite | postgres | mssql
DATABASE_ENGINE=postgres
VERSION_TAG=0.6.0
VERSION_TAG=0.7.0
COMPOSE_PROFILES=$DATABASE_ENGINE
8 changes: 4 additions & 4 deletions docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ services:
depends_on:
- caddy
environment:
NEXT_PUBLIC_VC_REPO: "http://localhost:$VC_REPO_PORT"
NEXT_PUBLIC_ISSUER: "http://localhost:$ISSUER_API_PORT"
NEXT_PUBLIC_VERIFIER: "http://localhost:$VERIFIER_API_PORT"
NEXT_PUBLIC_WALLET: "http://localhost:$DEMO_WALLET_FRONTEND_PORT"
NEXT_PUBLIC_VC_REPO: "http://host.docker.internal:$VC_REPO_PORT"
NEXT_PUBLIC_ISSUER: "http://host.docker.internal:$ISSUER_API_PORT"
NEXT_PUBLIC_VERIFIER: "http://host.docker.internal:$VERIFIER_API_PORT"
NEXT_PUBLIC_WALLET: "http://host.docker.internal:$DEMO_WALLET_FRONTEND_PORT"
PORT: $WEB_PORTAL_PORT

vc-repo:
Expand Down
298 changes: 176 additions & 122 deletions waltid-applications/waltid-web-wallet/libs/composables/presentation.ts
Original file line number Diff line number Diff line change
@@ -1,137 +1,191 @@
import { encodeDisclosure } from "./disclosures.ts";
import { useCurrentWallet } from "./accountWallet.ts";
import { ref, type Ref, computed, watch } from "vue";
import { computed, type Ref, ref, watch } from "vue";
import { decodeRequest } from "./siop-requests.ts";
import { navigateTo } from "nuxt/app";

export async function usePresentation(query: any) {
const index = ref(0);
const failed = ref(false);
const failMessage = ref("Unknown error occurred.");

const currentWallet = useCurrentWallet();
async function resolvePresentationRequest(request: string) {
try {
const response = await $fetch(`/wallet-api/wallet/${currentWallet.value}/exchange/resolvePresentationRequest`, {
method: "POST",
body: request
});
return response;
} catch (e) {
failed.value = true;
throw e;
}
const index = ref(0);
const failed = ref(false);
const failMessage = ref("Unknown error occurred.");

const currentWallet = useCurrentWallet();

async function resolvePresentationRequest(request: string) {
try {
const response = await $fetch(
`/wallet-api/wallet/${currentWallet.value}/exchange/resolvePresentationRequest`,
{
method: "POST",
body: request,
},
);
return response;
} catch (e) {
failed.value = true;
throw e;
}

const request = await resolvePresentationRequest(decodeRequest(query.request as string));
const presentationUrl = new URL(request as string);
const presentationParams = presentationUrl.searchParams;

const verifierHost = new URL(presentationParams.get("response_uri") ?? presentationParams.get("redirect_uri") ?? "").host;
const presentationDefinition = presentationParams.get("presentation_definition") as string;
const matchedCredentials = await $fetch<Array<{ id: string, document: string, parsedDocument?: string, disclosures?: string }>>(`/wallet-api/wallet/${currentWallet.value}/exchange/matchCredentialsForPresentationDefinition`, {
method: "POST",
body: presentationDefinition
});

const selection = ref<{ [key: string]: boolean; }>({});
const selectedCredentialIds = computed(() => Object.entries(selection.value).filter((it) => it[1]).map((it) => it[0]))
for (let credential of matchedCredentials) {
selection.value[credential.id] = true
}

const request = await resolvePresentationRequest(
decodeRequest(query.request as string),
);
const presentationUrl = new URL(request as string);
const presentationParams = presentationUrl.searchParams;

const verifierHost = new URL(
presentationParams.get("response_uri") ??
presentationParams.get("redirect_uri") ??
"",
).host;
const presentationDefinition = presentationParams.get(
"presentation_definition",
) as string;
const matchedCredentials = await $fetch<
Array<{
id: string;
document: string;
parsedDocument?: string;
disclosures?: string;
}>
>(
`/wallet-api/wallet/${currentWallet.value}/exchange/matchCredentialsForPresentationDefinition`,
{
method: "POST",
body: presentationDefinition,
},
);

const selection = ref<{ [key: string]: boolean }>({});
const selectedCredentialIds = computed(() =>
Object.entries(selection.value)
.filter((it) => it[1])
.map((it) => it[0]),
);
for (let credential of matchedCredentials) {
selection.value[credential.id] = true;
}

const disclosures: Ref<{ [key: string]: any[] }> = ref({});
const encodedDisclosures = computed(() => {
if (JSON.stringify(disclosures.value) === "{}") return null;

const m: { [key: string]: any[] } = {};
for (let credId in disclosures.value) {
if (m[credId] === undefined) {
m[credId] = [];
}

for (let disclosure of disclosures.value[credId]) {
m[credId].push(encodeDisclosure(disclosure));
}
}

const disclosures: Ref<{ [key: string]: any[] }> = ref({});
const encodedDisclosures = computed(() => {
if (JSON.stringify(disclosures.value) === "{}") return null

const m: { [key: string]: any[] } = {}
for (let credId in disclosures.value) {
if (m[credId] === undefined) {
m[credId] = []
}

for (let disclosure of disclosures.value[credId]) {
m[credId].push(encodeDisclosure(disclosure))
}
}

return m
})

function addDisclosure(credentialId: string, disclosure: string) {
if (disclosures.value[credentialId] === undefined) {
disclosures.value[credentialId] = []
}
disclosures.value[credentialId].push(disclosure)
}
return m;
});

function removeDisclosure(credentialId: string, disclosure: string) {
disclosures.value[credentialId] = disclosures.value[credentialId].filter((elem) => elem[0] != disclosure[0])
function addDisclosure(credentialId: string, disclosure: string) {
if (disclosures.value[credentialId] === undefined) {
disclosures.value[credentialId] = [];
}

const disclosureModalState: Ref<{ [key: string]: boolean }> = ref({});
disclosures.value[credentialId].push(disclosure);
}

function removeDisclosure(credentialId: string, disclosure: string) {
disclosures.value[credentialId] = disclosures.value[credentialId].filter(
(elem) => elem[0] != disclosure[0],
);
}

const disclosureModalState: Ref<{ [key: string]: boolean }> = ref({});

for (let credential of matchedCredentials) {
disclosureModalState.value[credential.id] = false;
}
if (matchedCredentials[index.value]) {
disclosureModalState.value[matchedCredentials[index.value].id] = true;
}

function toggleDisclosure(credentialId: string) {
disclosureModalState.value[credentialId] =
!disclosureModalState.value[credentialId];
}

// Disable all disclosure modals when switching between credentials and set the current one to active
watch(index, () => {
for (let credential of matchedCredentials) {
disclosureModalState.value[credential.id] = false
disclosureModalState.value[credential.id] = false;
}
disclosureModalState.value[matchedCredentials[index.value].id] = true

function toggleDisclosure(credentialId: string) {
disclosureModalState.value[credentialId] = !disclosureModalState.value[credentialId]
}
// Disable all disclosure modals when switching between credentials and set the current one to active
watch(index, () => {
for (let credential of matchedCredentials) {
disclosureModalState.value[credential.id] = false
}
disclosureModalState.value[matchedCredentials[index.value].id] = true
})

async function acceptPresentation() {
const req = {
//did: String, // todo: choose DID of shared credential // for now wallet-api chooses the default wallet did
presentationRequest: request,
selectedCredentials: selectedCredentialIds.value,
disclosures: encodedDisclosures.value
};

const response = await fetch(`/wallet-api/wallet/${currentWallet.value}/exchange/usePresentationRequest`, {
method: "POST",
body: JSON.stringify(req),
redirect: "manual",
headers: {
"Content-Type": "application/json"
}
disclosureModalState.value[matchedCredentials[index.value].id] = true;
});

async function acceptPresentation() {
const req = {
//did: String, // todo: choose DID of shared credential // for now wallet-api chooses the default wallet did
presentationRequest: request,
selectedCredentials: selectedCredentialIds.value,
disclosures: encodedDisclosures.value,
};

const response = await fetch(
`/wallet-api/wallet/${currentWallet.value}/exchange/usePresentationRequest`,
{
method: "POST",
body: JSON.stringify(req),
redirect: "manual",
headers: {
"Content-Type": "application/json",
},
},
);

if (response.ok) {
const parsedResponse: { redirectUri: string } = await response.json();
if (parsedResponse.redirectUri) {
navigateTo(parsedResponse.redirectUri, {
external: true,
});

if (response.ok) {
const parsedResponse: { redirectUri: string } = await response.json();
if (parsedResponse.redirectUri) {
navigateTo(parsedResponse.redirectUri, {
external: true
});
} else {
window.alert("Presentation successful, no redirect URL supplied.");
navigateTo(`/wallet/${currentWallet.value}`, {
external: true
});
}
} else {
failed.value = true;
const error: { message: string; redirectUri: string | null | undefined, errorMessage: string } = await response.json();
failMessage.value = error.message;

console.log("Error response: " + JSON.stringify(error));
window.alert(error.errorMessage)

if (error.redirectUri != null) {
navigateTo(error.redirectUri as string, {
external: true
});
}
}
}

return {
currentWallet, verifierHost, presentationDefinition, matchedCredentials, selectedCredentialIds, disclosures, selection, index, disclosureModalState, toggleDisclosure, addDisclosure, removeDisclosure, acceptPresentation, failed, failMessage
} else {
window.alert("Presentation successful, no redirect URL supplied.");
navigateTo(`/wallet/${currentWallet.value}`, {
external: true,
});
}
} else {
failed.value = true;
const error: {
message: string;
redirectUri: string | null | undefined;
errorMessage: string;
} = await response.json();
failMessage.value = error.message;

console.log("Error response: " + JSON.stringify(error));
window.alert(error.errorMessage);

if (error.redirectUri != null) {
navigateTo(error.redirectUri as string, {
external: true,
});
}
}
}
}

return {
currentWallet,
verifierHost,
presentationDefinition,
matchedCredentials,
selectedCredentialIds,
disclosures,
selection,
index,
disclosureModalState,
toggleDisclosure,
addDisclosure,
removeDisclosure,
acceptPresentation,
failed,
failMessage,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import id.walt.credentials.verification.models.PresentationResultEntry
import id.walt.credentials.verification.models.PresentationVerificationResponse
import id.walt.credentials.verification.policies.JwtSignaturePolicy
import id.walt.crypto.utils.JwsUtils.decodeJws
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
Expand All @@ -22,6 +23,8 @@ import kotlin.time.measureTime
@JsExport
object Verifier {

private val log = KotlinLogging.logger { }

private fun JsonObject.getW3CType() = (this["type"] ?: this["vc"]?.jsonObject?.get("type") ?: this["vp"]?.jsonObject?.get("type")
?: throw IllegalArgumentException("No `type` supplied: $this")).let {
when (it) {
Expand Down
Loading

0 comments on commit fb6aa1b

Please sign in to comment.