Skip to content

Commit

Permalink
Merge pull request #30 from european-epc-competence-center/openid4vp
Browse files Browse the repository at this point in the history
Openid4vp
  • Loading branch information
F-Node-Karlsruhe authored Mar 15, 2023
2 parents d03a89d + eb86dbc commit fdc8c0a
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 4 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ VC Verifier Changelog
WIP
---

1.4.0 (2023-03-15)
---

- add OpenId4VP functionality


1.3.0 (2023-02-28)
---
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@ const routes = [
path: '/',
name: 'Entry',
component: () =>
import( '@/views/Entry.vue'),
import('@/views/Entry.vue'),
},
{
path: '/verify',
name: 'Verify',
component: () => import('@/views/Verify.vue'),
},
{
path: '/request',
name: 'Presentation Request',
component: () => import('@/views/PresentationRequest.vue'),
},
]
}
}
]

const router = createRouter({
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { createStore } from 'vuex'

export default createStore({
state: {
version: '1.3.0',
version: '1.4.0',
verifiables: [],
VC_REGISTRY: process.env.VC_REGISTRY || 'https://ssi.eecc.de/api/registry/vcs/',
OPENID_ENDPOINT: process.env.OPENID_ENDPOINT || 'https://ssi.eecc.de/api/openid/',
showQRModal: false
},
mutations: {
Expand Down
19 changes: 18 additions & 1 deletion frontend/src/views/Entry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@
<a href="https://id.eecc.de"><img id="logo" src="@/assets/img/logo.png" /></a>
</div>
<div class="card-body" style="overflow-y: scroll;">
<p class="m-3">Select the credential/s using one of the options below.</p>
<div class="row m-3">
<div class="col-9">
<p class="mb-0">Select the credential/s using one of the options below or send a presentation request.
</p>
</div>
<div class="col-3 text-end">
<router-link :to="{ path: 'request' }" class="btn btn-outline-primary" type="button"
data-bs-container="body" data-bs-trigger="hover" data-bs-toggle="tooltip"
data-bs-title="Generates an OpenId4VP presentation request"><i class="bi-send-check-fill" role="img"
aria-label="Presentation request"></i>
</router-link>
</div>
</div>
<!--By json file-->
<div class="card m-3 p-3 shadow">
<h5>Credential/Presentation Files</h5>
Expand Down Expand Up @@ -61,6 +73,7 @@
</div>
</template>
<script>
import { Tooltip } from 'bootstrap';
import { useToast } from "vue-toastification";
import ScanModal from "@/components/ScanModal.vue"
Expand All @@ -80,6 +93,10 @@ export default {
}
},
mounted() {
new Tooltip(document.body, {
selector: "[data-bs-toggle='tooltip']",
trigger: 'hover'
})
document.getElementById('scan-modal').addEventListener('hidden.bs.modal', () => { this.scan = '' });
},
methods: {
Expand Down
168 changes: 168 additions & 0 deletions frontend/src/views/PresentationRequest.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<template>
<div id="maincard" class="card m-md-auto shadow">
<div class="card-header text-center p-3 pb-1">
<router-link to="/"><i id="backbutton" style="font-size: 2rem;" class="bi-arrow-left-square-fill mx-3 my-1"
role="img" aria-label="API Docs"></i></router-link>
<h3 class="mb-0 mx-5">Presentation Request</h3>
<a href="https://github.com/european-epc-competence-center/vc-verifier"><i class="bi-github mx-3" role="img"
aria-label="GitHub"></i></a>
<a href="https://ssi.eecc.de/api/openid/swagger/"><i class="bi-filetype-doc mx-3" role="img"
aria-label="API Docs"></i></a>
<a href="https://id.eecc.de"><img id="logo" src="@/assets/img/logo.png" /></a>
</div>
<div class="card-body" style="overflow-y: scroll;">
<div class="row mx-md-3">
<div class="col-12">
<p>Define which credentials you want to have presented</p>
</div>
</div>
<div class="row mx-md-3">
<div class="col-md-4">
<select v-model="credentialType" @change="registerPresentationRequest" id="credentialType"
class="form-select" aria-label="Credential Type">
<option selected :value="undefined">All</option>
<option value="ProductPassportCredential">Product Passport Credential</option>
<option value="GS1GLNCredential">GS1 GLN Credential</option>
</select>
<label for="credentialType" class="form-label text-muted ms-1"><small>Credential type</small></label>
</div>
</div>
<div class="row">
<div class="col-12 px-0 mt-3 text-center">
<div v-if="generating" style="min-height: 300px;">
<p class="m-5 text-muted">Registering presentation request</p>
<div class="spinner-border text-primary m-5" role="status"
style="width: 5rem; height: 5rem; top: 150px;">
<span class="visually-hidden">Generating...</span>
</div>
</div>
<qrcode-vue v-else-if="presentationRequestId" :value="presentationRequestURI" :margin="1" :size="300"
level="M" class="my-3" id="presentation-request-canvas" />
<div v-else style="min-height: 300px;">
<p class="p-5 text-muted">Please configure your presentation request</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { useToast } from 'vue-toastification';
import QrcodeVue from 'qrcode.vue'
export default {
name: 'PresentationRequest',
data() {
return {
toast: useToast(),
qrSize: 300,
generating: false,
presentationRequestId: undefined,
nonce: Math.random().toString(16).substring(2, 24),
credentialType: undefined,
intervalId: undefined
}
},
components: {
QrcodeVue
},
mounted() {
if (this.intervalId) clearInterval(this.intervalId)
this.intervalId = setInterval(this.getPresentation, 3000);
},
beforeUnmount() {
if (this.intervalId) clearInterval(this.intervalId)
},
computed: {
presentationDefinition() {
return {
"id": "eecc_verifier_request",
"input_descriptors": [
{
"id": "eecc_verifier_request_" + this.credentialType || "",
"format": {
"ldp_vc": {
"proof_type": [
"Ed25519Signature2020"
]
}
},
"constraints": {
"fields": [
{
"path": [
"$.type"
],
"filter": {
"type": "array",
"contains": {
"type": "string",
"const": this.credentialType
}
}
}
]
}
}
]
}
},
presentationRequest() {
return {
"nonce": this.nonce,
"response_mode": "direct_post",
"response_type": "vp_token",
"client_id": this.$store.state.OPENID_ENDPOINT + "presentation" + this.getRequestPath(),
"response_uri": this.$store.state.OPENID_ENDPOINT + "presentation" + this.getRequestPath(),
"presentation_definition": this.presentationDefinition
};
},
presentationRequestURI() {
return 'openid-presentation-request://?client_id='
+ encodeURI(this.$store.state.OPENID_ENDPOINT + 'presentation/' + this.presentationRequestId)
+ '&request_uri='
+ encodeURI(this.$store.state.OPENID_ENDPOINT + 'request/' + this.presentationRequestId)
}
},
methods: {
getPresentation() {
if (this.presentationRequestId) {
this.$api.get(this.$store.state.OPENID_ENDPOINT + 'presentation' + this.getRequestPath())
.then((res) => {
clearInterval(this.intervalId)
this.intervalId = undefined
const presentationResponse = res.data
const presentation = typeof presentationResponse.vp_token == 'string' ? JSON.parse(presentationResponse.vp_token) : presentationResponse.vp_token;
this.$store.dispatch("addVerifiables", [presentation])
.then(() => this.$router.push({ path: '/verify' }));
})
.catch((error) => {
if (error.response.status != 404) {
this.toast.error(`Something went wrong fetching the presentation request!\n${error}`);
console.error(error)
clearInterval(this.intervalId)
this.intervalId = undefined
}
}).finally(() => {
});
}
},
getRequestPath() {
return this.presentationRequestId ? '/' + this.presentationRequestId : '';
},
registerPresentationRequest() {
this.generating = true
this.$api.post(this.$store.state.OPENID_ENDPOINT + 'request' + this.getRequestPath(), this.presentationRequest)
.then((res) => {
this.presentationRequestId = res.data;
})
.catch((error) => {
this.toast.error(`Something went wrong generating the presentation request!\n${error}`);
}).finally(() => {
this.generating = false
});
}
}
}
</script>

0 comments on commit fdc8c0a

Please sign in to comment.