Skip to content

Commit

Permalink
Add sdjwt and mdoc claims to redirect page
Browse files Browse the repository at this point in the history
  • Loading branch information
Jdu278 committed Aug 21, 2024
1 parent 507c75d commit f059156
Show file tree
Hide file tree
Showing 7 changed files with 2,070 additions and 2,294 deletions.
2 changes: 1 addition & 1 deletion layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="lg:pt-10 bg-primary">
<HeaderComponent/>
</div>
<div class="flex-grow max-w-xl lg:max-w-7xl mx-auto">
<div class="flex-grow max-w-sm lg:max-w-7xl mx-auto">
<div class="mx-6 my-4">
<slot/>
</div>
Expand Down
4 changes: 4 additions & 0 deletions models/TagValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type TagValue = {
tag: number,
value: Uint8Array,
}
4,212 changes: 1,949 additions & 2,263 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@
"@headlessui/vue": "^1.7.22",
"@heroicons/vue": "^2.1.5",
"@pinia/nuxt": "^0.5.1",
"@sd-jwt/crypto-browser": "^0.7.2",
"@sd-jwt/crypto-nodejs": "^0.7.2",
"@sd-jwt/decode": "^0.7.2",
"@sd-jwt/types": "^0.7.2",
"axios": "^1.7.2",
"buffer": "^6.0.3",
"cbor-web": "^9.0.2",
"nuxt": "^3.12.2",
"pinia": "^2.1.7",
"qrcode.vue": "^3.4.1"
Expand Down
84 changes: 55 additions & 29 deletions pages/wallet-redirect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,91 @@
<div class="text-xl lg:text-3xl font-bold text-accent m-6 text-center">
<h1>Ihre Daten</h1>
</div>
<div v-if="errorMessage" class="mx-6 p-4 border-red-400 border rounded text-red-500">
<p>{{ errorMessage }}</p>
</div>
<div v-else class="mx-6 py-4 border-neutral-400 border rounded">
<div v-if="dataList" class="mx-6 border-neutral-400 border rounded">
<div class="overflow-x-auto">
<table class="table w-full">
<table class="table-auto w-full">
<tbody>
<tr v-for="item in dataList" :key="item.kind">
<td>{{ item.kind }}</td>
<tr v-for="item in dataList" :key="item.key">
<td class="px-4 py-2 border">{{ item.key }}</td>
<td class="px-4 py-2 border">{{ item.value }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="errorMessage" class="mx-6 p-4 border-red-400 border rounded text-red-500">
<p>{{ errorMessage }}</p>
</div>
</div>
</template>

<script setup lang="ts">
import axios from "axios";
import {ref, onMounted} from 'vue';
import type {TransactionRequest} from "~/models/TransactionRequest";
import {onMounted, ref} from 'vue';
import {getMdocClaims, getSdJwtClaims} from "~/utils/utils";
const runtimeConfig = useRuntimeConfig()
const baseUrl = runtimeConfig.public.apiUrl
const dataList = ref<{ kind: string }[]>([]);
const dataList = ref<{ key: string; value: string }[]>([]);
const errorMessage = ref<string | null>(null);
let vpToken: string;
onMounted(async () => {
const sessionStore = useSessionStore();
const presentationId = sessionStore.presentationId
const route = useRoute()
const responseCode = route.query.response_code
const nonce = route.query.nonce
const responseCode = ref()
const hash = route.hash
if (responseCode && presentationId) {
try {
const response = await axios.get<TransactionRequest>(
`${baseUrl}/ui/presentations`
);
const presentationResponse = response.data;
if (nonce !== presentationResponse.nonce) {
errorMessage.value = 'Nonce ist falsch';
return;
}
// TODO: add check for nonce
// const nonce = route.query.nonce
// if (nonce !== vpToken.nonce) {
// errorMessage.value = 'Nonce ist falsch';
// return;
// }
const regex = /^\$\['[^']+'\]/;
if (hash && presentationId) {
try {
const params = new URLSearchParams(hash.slice(1))
responseCode.value = params.get('response_code')
dataList.value = presentationResponse.presentation_definition.input_descriptors[0].constraints.fields.map(field => ({
kind: field.path[0].replace(regex, '')
}));
const response = await axios.get(
`${baseUrl}/ui/presentations/${presentationId}?response_code=${responseCode.value}`
);
vpToken = response.data.vp_token;
sessionStore.$reset();
} catch (error) {
if (vpToken.includes('ey')) {
const sdJwtClaims = await getSdJwtClaims(vpToken)
if (sdJwtClaims) {
dataList.value = Object.entries(sdJwtClaims)
.filter(([key]) => !['cnf', 'exp', 'iat', 'iss', 'vct'].includes(key))
.map(([key, value]) => ({
key: key.replaceAll('_', ' '),
value: typeof value === 'object' ? JSON.stringify(value) : String(value)
}));
sessionStore.$reset();
} else {
errorMessage.value = 'Fehler beim Abrufen der Daten';
}
} else {
const mdocClaims = await getMdocClaims(vpToken)
if (mdocClaims) {
dataList.value = Object.entries(mdocClaims).map(([, valueObj]) => ({
key: valueObj.key,
value: valueObj.value
}));
sessionStore.$reset();
} else {
errorMessage.value = 'Fehler beim Abrufen der Daten';
}
}
} catch {
errorMessage.value = 'Fehler beim Abrufen der Daten';
}
} else {
errorMessage.value = 'Fehler beim Abrufen der Daten';
}
});
})
</script>
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
"extends": "./.nuxt/tsconfig.json",
}
54 changes: 54 additions & 0 deletions utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {type DecodedSDJwt, decodeSdJwt, getClaims} from "@sd-jwt/decode";
import {digest} from "@sd-jwt/crypto-browser";
import type {TagValue} from "~/models/TagValue";
import * as cbor from 'cbor-web';
import {Buffer} from 'buffer';

export async function getSdJwtClaims(vpToken: string): Promise<{ key: string; value: string }[] | undefined> {
try {
const decodedSdJwt: DecodedSDJwt = await decodeSdJwt(vpToken, digest);
return await getClaims(
decodedSdJwt.jwt.payload,
decodedSdJwt.disclosures,
digest,
)
} catch {
throw Error()
}
}

export async function getMdocClaims(vpToken: string): Promise<{
[p: string]: { key: string; value: string }
} | undefined> {
try {

const buf: Buffer = Buffer.from(vpToken, 'base64');
const valueOut: any = await cbor.decodeFirst(buf, {
preferWeb: true

});
// TODO: edit for multiple Presentations
let namespaces = valueOut.nameSpaces;
let firstNamespace = Object.entries(namespaces)[0][0];
const dataArray: TagValue[] = namespaces[firstNamespace];

const requestDataPromises = dataArray.map((item: TagValue) => cborDecode(item.value));
const requestData = await Promise.all(requestDataPromises);

const claims: { key: string; value: string }[] = requestData.map((item: { elementIdentifier: string, elementValue: string }) => {
return {key: item.elementIdentifier.replaceAll('_', ' '), value: item.elementValue};
});

return Object.fromEntries(
Object.entries(claims)) as { [key: string]: { key: string; value: string } };

} catch {
throw Error()
}
}

async function cborDecode(buf: Uint8Array): Promise<any> {
return cbor.decodeFirst(buf, {
preferWeb: true
});
}

0 comments on commit f059156

Please sign in to comment.