Skip to content

Commit f37cdb7

Browse files
authored
Merge pull request #133 from dwelman/upload-credential
Allows for the upload of a credential directly into a wallet
2 parents 78d598d + f10d1b6 commit f37cdb7

File tree

6 files changed

+118
-2
lines changed

6 files changed

+118
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ During front-end development, you probably want to use the real filesystem and w
3434
make dev
3535
```
3636

37+
You can access the website at `http://localhost:1305/` by default
38+
3739
The API and domain types are generated from the `api/api.yaml`.
3840
```shell
3941
make gen-api

web/src/admin/IdentityDetails.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@
9898
<p v-else>
9999
No credentials in wallet.
100100
</p>
101+
<br>
102+
<button
103+
id="upload-credential-button"
104+
@click="$router.push({name: 'admin.uploadCredential', params: {subjectID: this.$route.params.subjectID}})"
105+
class="btn btn-primary"
106+
>
107+
Upload
108+
</button>
101109
</section>
102110
</div>
103111
</div>
@@ -107,9 +115,10 @@
107115
108116
import DiscoveryServiceDefinition from "./DiscoveryServiceDefinition";
109117
import ErrorMessage from "../components/ErrorMessage.vue";
118+
import UploadCredential from "./credentials/UploadCredential.vue";
110119
111120
export default {
112-
components: {ErrorMessage},
121+
components: {ErrorMessage, UploadCredential},
113122
data() {
114123
return {
115124
fetchError: undefined,

web/src/admin/credentials/IssueCredential.vue

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
<section v-if="issuedCredential">
77
<header>Issued Credential</header>
8+
<button class="btn btn-primary" @click="copyToClipboard">Copy to Clipboard</button>
89
<pre>{{ JSON.stringify(issuedCredential, null, 2) }}</pre>
910
</section>
1011

@@ -86,7 +87,12 @@
8687
</div>
8788
</div>
8889
</template>
89-
90+
<style scoped>
91+
button.btn.btn-primary {
92+
display: block;
93+
margin-bottom: 1rem;
94+
}
95+
</style>
9096
<script>
9197
import templates from "./templates";
9298
import ErrorMessage from "../../components/ErrorMessage.vue";
@@ -187,6 +193,15 @@ export default {
187193
.catch(response => {
188194
this.fetchError = response
189195
})
196+
},
197+
copyToClipboard() {
198+
navigator.clipboard.writeText(JSON.stringify(this.issuedCredential, null, 2))
199+
.then(() => {
200+
this.$emit('statusUpdate', 'Credential copied to clipboard')
201+
})
202+
.catch(err => {
203+
this.fetchError = 'Failed to copy credential: ' + err
204+
})
190205
}
191206
}
192207
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<modal-window :cancelRoute="{name: 'admin.identityDetails', params: {subjectID: this.$route.params.subjectID}}" :confirmFn="confirm" confirmText="Upload Credential"
3+
title="Upload a Credential JSON" type="add">
4+
5+
<p class="mb-3 text-sm">
6+
Here you can add a credential issued by another party to be stored in your wallet. Paste the JSON object below.
7+
</p>
8+
9+
<ErrorMessage v-if="apiError" :message="apiError" title="Could not upload credential"/>
10+
11+
<div class="mt-4">
12+
<upload-credential-form mode="new" :value="credential" @input="(credentialPaste)=> {credential = credentialPaste}"/>
13+
</div>
14+
</modal-window>
15+
</template>
16+
17+
<script>
18+
import ModalWindow from '../../components/ModalWindow.vue'
19+
import UploadCredentialForm from "./UploadCredentialForm.vue";
20+
import ErrorMessage from "../../components/ErrorMessage.vue";
21+
22+
export default {
23+
components: {
24+
ErrorMessage,
25+
ModalWindow,
26+
UploadCredentialForm
27+
},
28+
created() {
29+
this.subjectID = this.$route.params.subjectID;
30+
},
31+
data () {
32+
return {
33+
apiError: '',
34+
subjectID: '',
35+
credential: {}
36+
}
37+
},
38+
methods: {
39+
confirm () {
40+
this.$api.post(`api/proxy/internal/vcr/v2/holder/${this.subjectID}/vc`, this.credential)
41+
.then(() => {
42+
this.$emit('uploadCredential', 'Verifiable Credential issued, and loaded into wallet')
43+
this.$router.push({name: 'admin.identityDetails', params: {subjectID: this.subjectID}})
44+
})
45+
.catch(reason => {
46+
this.apiError = reason
47+
})
48+
}
49+
}
50+
}
51+
</script>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<form class="space-y-3">
3+
<div>
4+
<div v-if="error" class="text-red-500">{{ error }}</div>
5+
<textarea v-model="localValue" rows="10" cols="30"></textarea>
6+
</div>
7+
</form>
8+
</template>
9+
<script>
10+
export default {
11+
props: {
12+
value: Object,
13+
mode: String
14+
},
15+
data () {
16+
return {
17+
localValue: JSON.stringify(this.value, null, 2)
18+
}
19+
},
20+
emits: ['input'],
21+
watch: {
22+
localValue (newValue) {
23+
try {
24+
this.error = '';
25+
let parsedInput = JSON.parse(newValue)
26+
this.$emit('input', parsedInput);
27+
} catch (e) {
28+
this.error = 'Invalid JSON format';
29+
}
30+
}
31+
}
32+
}
33+
</script>

web/src/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Api from './plugins/api'
1414
import IdentityDetails from "./admin/IdentityDetails.vue";
1515
import IssueCredential from "./admin/credentials/IssueCredential.vue";
1616
import ActivateDiscoveryService from "./admin/ActivateDiscoveryService.vue";
17+
import UploadCredential from "./admin/credentials/UploadCredential.vue";
1718

1819
const routes = [
1920
{
@@ -51,6 +52,11 @@ const routes = [
5152
name: 'admin.identityDetails',
5253
component: IdentityDetails,
5354
},
55+
{
56+
path: 'id/:subjectID/upload',
57+
name: 'admin.uploadCredential',
58+
component: UploadCredential,
59+
},
5460
{
5561
path: 'id/:subjectID/discovery/:discoveryServiceID/activate',
5662
name: 'admin.activateDiscoveryService',

0 commit comments

Comments
 (0)