Skip to content

Commit

Permalink
Merge branch 'main' into feat/did-web-extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
cpatsonakis committed Sep 19, 2024
2 parents ea0eb7c + fb6aa1b commit 4c1624e
Show file tree
Hide file tree
Showing 22 changed files with 396 additions and 315 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
10 changes: 6 additions & 4 deletions docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ services:
- caddy
environment:
NUXT_PUBLIC_ISSUER_CALLBACK_URL: "http://localhost:$DEMO_WALLET_FRONTEND_PORT"
NUXT_PUBLIC_DEV_WALLET_URL: "http://localhost:$DEV_WALLET_FRONTEND_PORT"
PORT: $DEMO_WALLET_FRONTEND_PORT

waltid-dev-wallet:
Expand All @@ -71,6 +72,7 @@ services:
- caddy
environment:
NUXT_PUBLIC_ISSUER_CALLBACK_URL: "http://localhost:$DEV_WALLET_FRONTEND_PORT"
NUXT_PUBLIC_DEMO_WALLET_URL: "http://localhost:$DEMO_WALLET_FRONTEND_PORT"
PORT: $DEV_WALLET_FRONTEND_PORT

web-portal:
Expand All @@ -83,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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import RowCredential from '@/components/walt/credential/RowCredential';
import Dropdown from '@/components/walt/forms/Dropdown';
import {AuthenticationMethods, VpProfiles} from '@/types/credentials'
import { AuthenticationMethods, VpProfiles } from '@/types/credentials'
import Checkbox from '@/components/walt/forms/Checkbox';
import InputField from '@/components/walt/forms/Input';
import Button from '@/components/walt/button/Button';
Expand Down Expand Up @@ -115,11 +115,11 @@ export default function IssueSection() {
<span> Authentication Method</span>
</div>
</div>
<Dropdown
values={AuthenticationMethods}
selected={selectedAuthenticationMethod}
setSelected={setSelectedAuthenticationMethod}
/>
<Dropdown
values={AuthenticationMethods}
selected={selectedAuthenticationMethod}
setSelected={setSelectedAuthenticationMethod}
/>
</div>
<div className="mt-3 flex flex-col sm:flex-row justify-between">
<div className="">
Expand Down Expand Up @@ -155,16 +155,16 @@ export default function IssueSection() {
</div>

<div className="mt-1 flex flex-col sm:flex-row justify-between">
<div className="">
<div className="">
<Checkbox value={requireVpProfile} onChange={setRequireVpProfile}>
VP Token Requested Profile
</Checkbox>
</div>
<Dropdown
values={VpProfiles}
selected={selectedVpProfile}
setSelected={setSelectedVpProfile}
/>
<Dropdown
values={VpProfiles}
selected={selectedVpProfile}
setSelected={setSelectedVpProfile}
/>
</div>

<hr className="my-5" />
Expand All @@ -173,7 +173,11 @@ export default function IssueSection() {
Cancel
</Button>
<Button
disabled={!(credentialsToIssue.length > 0 && (credentialsToIssue.length < 2 || credentialsToIssue.filter((cred) => cred.selectedFormat === "SD-JWT + VCDM").length === 0))}
disabled={!(credentialsToIssue.length > 0 && (
credentialsToIssue.length < 2 ||
credentialsToIssue.filter((cred) => cred.selectedFormat === "SD-JWT + W3C VC" || cred.selectedFormat === "SD-JWT + IETF SD-JWT VC").length === credentialsToIssue.length ||
credentialsToIssue.filter((cred) => !cred.selectedFormat || cred.selectedFormat === "JWT + W3C VC").length === credentialsToIssue.length
))}
onClick={handleIssue}>Issue</Button>
</div>
<div className="flex flex-col items-center mt-12">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function Checkbox({
name="comments"
type="checkbox"
checked={value}
onClick={() => {
onChange={() => {
onChange(!value);
}}
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
Expand Down
36 changes: 19 additions & 17 deletions waltid-applications/waltid-web-portal/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,27 @@ export default function App({ Component, pageProps }: AppProps) {
const [env, setEnv] = React.useState({} as { [key: string]: string });

React.useEffect(() => {
if (env.hasOwnProperty('NEXT_PUBLIC_VC_REPO')) {
axios.get(`${env.NEXT_PUBLIC_VC_REPO}/api/list`).then((response) => {
response.data.forEach((credential: string) => {
axios.get(`${env.NEXT_PUBLIC_VC_REPO}/api/vc/${credential}`).then((data) => {
setAvailableCredentials((prev) => [...prev, {
id: credential,
title: credential,
offer: data.data,
}]);
axios.get('/api/env').then((response) => {
if (response.data.hasOwnProperty('NEXT_PUBLIC_VC_REPO')) {
setEnv(response.data);

axios.get(`${response.data.NEXT_PUBLIC_VC_REPO}/api/list`).then((credentials) => {
credentials.data.forEach((credential: string) => {
axios.get(`${response.data.NEXT_PUBLIC_VC_REPO}/api/vc/${credential}`).then((data) => {
setAvailableCredentials((prev) => [...prev, {
id: credential,
title: credential,
offer: data.data,
}]);
});
});
});
});
}
else {
axios.get('/api/env').then((response) => {
setEnv(response.data);
});
}
}, [env]);
}
else {
throw new Error("Env variables not found");
}
});
}, []);

return (
<EnvContext.Provider value={env}>
Expand Down
124 changes: 47 additions & 77 deletions waltid-applications/waltid-web-portal/utils/getOfferUrl.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
import {AvailableCredential, CredentialFormats, DIDMethods, DIDMethodsConfig} from '@/types/credentials';
import { AvailableCredential, CredentialFormats, DIDMethods, DIDMethodsConfig } from '@/types/credentials';

const getOfferUrl = async (credentials: Array<AvailableCredential>, NEXT_PUBLIC_VC_REPO: string, NEXT_PUBLIC_ISSUER: string, authenticationMethod?: string, vpRequestValue?: string, vpProfile?: string) => {
const getOfferUrl = async (credentials: Array<AvailableCredential>, NEXT_PUBLIC_VC_REPO: string, NEXT_PUBLIC_ISSUER: string, authenticationMethod?: string, vpRequestValue?: string, vpProfile?: string) => {
const data = await fetch(`${NEXT_PUBLIC_ISSUER}/.well-known/openid-credential-issuer`).then(data => {
return data.json();
});
const credential_configurations_supported = data.credential_configurations_supported;

const payload = await Promise.all(credentials.map(async (c) => {
c = {...c, selectedFormat: c.selectedFormat ?? CredentialFormats[0], selectedDID: c.selectedDID ?? DIDMethods[0]};
c = { ...c, selectedFormat: c.selectedFormat ?? CredentialFormats[0], selectedDID: c.selectedDID ?? DIDMethods[0] };

const offer = { ...c.offer, id: uuidv4() };
const mapping = await (await fetch(`${NEXT_PUBLIC_VC_REPO}/api/mapping/${c.id}`).then(data => {
return data.json();
}).catch(err => {
return null;
}));

let payload: {
'issuerDid': string,
'issuerKey': { "type": string, "jwk": object },
Expand All @@ -35,27 +29,13 @@ const getOfferUrl = async (credentials: Array<AvailableCredential>, NEXT_PUBLIC_
credentialData: offer
}

if (c.selectedFormat === "SD-JWT + W3C VC") {
payload.selectiveDisclosure = {
"fields": {
"credentialSubject": {
sd: false,
children: {
fields: {}
}
}
}
}
for (const key in offer.credentialSubject) {
if (typeof offer.credentialSubject[key] === 'string') {
payload.selectiveDisclosure.fields.credentialSubject.children.fields[key] = {
sd: true
}
}
}
}

if (c.selectedFormat === "SD-JWT + IETF SD-JWT VC"){
if (c.selectedFormat === "SD-JWT + IETF SD-JWT VC") {
payload.mapping = {
id: "<uuid>",
iat: "<timestamp-seconds>",
nbf: "<timestamp-seconds>",
exp: "<timestamp-in-seconds:365d>"
};

// Hack - remove the following fields as they used for w3c only
delete payload.credentialData["@context"];
Expand All @@ -66,80 +46,70 @@ const getOfferUrl = async (credentials: Array<AvailableCredential>, NEXT_PUBLIC_
delete payload.credentialData["issued"];
delete payload.credentialData["issuer"];

// Hack - replace the "mapping" object with the new one
// delete payload.mapping;

payload.mapping = {
id: "<uuid>",
iat: "<timestamp-seconds>",
nbf: "<timestamp-seconds>",
exp: "<timestamp-in-seconds:365d>"
};

payload.credentialConfigurationId = Object.keys(credential_configurations_supported).find(key => key === c.id + "_vc+sd-jwt") as string;

interface SelectiveDisclosureField {
sd: boolean;
children: {
fields: Record<string, any>;
};
}

const selectiveDisclosure: {
fields: Record<string, SelectiveDisclosureField>;
} = {
fields: {}
};

payload.selectiveDisclosure = { "fields": {} }
for (const key in offer.credentialSubject) {
if (typeof offer.credentialSubject[key] === 'string') {
// Add a field entry for each property in credentialSubject
selectiveDisclosure.fields[key] = {
sd: true, // Default selective disclosure state
children: {
fields: {} // Placeholder for potential future nested fields
payload.selectiveDisclosure.fields[key] = {
sd: true
}
}
}
}
else {
payload.mapping = await (await fetch(`${NEXT_PUBLIC_VC_REPO}/api/mapping/${c.id}`).then(data => {
return data.json();
}).catch(err => {
return null;
}));

if (c.selectedFormat === "SD-JWT + W3C VC") {
payload.selectiveDisclosure = {
"fields": {
"credentialSubject": {
sd: false,
children: {
fields: {}
}
}
}
}
for (const key in offer.credentialSubject) {
if (typeof offer.credentialSubject[key] === 'string') {
payload.selectiveDisclosure.fields.credentialSubject.children.fields[key] = {
sd: true
}
};
}
}
}

payload.selectiveDisclosure = selectiveDisclosure

}

if (authenticationMethod) {
payload.authenticationMethod = authenticationMethod;
}

if (vpRequestValue) {
payload.vpRequestValue = vpRequestValue;
}

if (vpProfile) {
payload.vpProfile = vpProfile;
}

// If true, return the payload as is
if (credentials[0]?.selectedFormat === "SD-JWT + IETF SD-JWT VC") {

if (c.selectedFormat === "SD-JWT + IETF SD-JWT VC") {
const { credentialSubject, ...restOfCredentialData } = payload.credentialData; // Destructure credentialSubject and the rest

const updatedPayload = {
return {
...payload, // Keep the rest of the payload unchanged
credentialData: {
...restOfCredentialData, // Spread other fields from credentialData (e.g., id, issuer)
...credentialSubject // Spread fields from credentialSubject to the top level of credentialData
}
};

return updatedPayload
}

// Otherwise, return the payload with mapping if mapping exists, or just payload
return mapping ? { ...payload, mapping } : payload;
else {
return payload;
}
}));


const issueUrl = NEXT_PUBLIC_ISSUER + `/openid4vc/${credentials.length === 1 && (credentials[0].selectedFormat === "SD-JWT + W3C VC" || credentials[0].selectedFormat === "SD-JWT + IETF SD-JWT VC") ? "sdjwt" : "jwt"}/${(payload.length > 1 ? 'issueBatch' : 'issue')}`;
const issueUrl = NEXT_PUBLIC_ISSUER + `/openid4vc/${credentials[0].selectedFormat === "SD-JWT + W3C VC" || credentials[0].selectedFormat === "SD-JWT + IETF SD-JWT VC" ? "sdjwt" : "jwt"}/${(payload.length > 1 ? 'issueBatch' : 'issue')}`;
return axios.post(issueUrl, payload.length > 1 ? payload : payload[0]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
d="M15 19l-7-7 7-7" />
</svg>
</button>
<VerifiableCredentialCard :credential="{
<VerifiableCredentialCard :key="index" :credential="{
parsedDocument: {
type: [credentialTypes[index]],
issuer: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
<VerifiableCredentialCard :credential="{
<VerifiableCredentialCard :key="index" :credential="{
document: matchedCredentials[index].document
}" class="sm:w-[400px]" />
<button v-if="matchedCredentials.length > 1" @click="index++"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export default defineNuxtConfig({
projectId: process.env.ProjectId,
issuerCallbackUrl: "http://localhost:7100",
credentialsRepositoryUrl: "http://localhost:3000",
devWalletUrl: "https://wallet-dev.walt.id",
demoWalletUrl: "https://wallet-dev.walt.id",
}
},

Expand Down
Loading

0 comments on commit 4c1624e

Please sign in to comment.