Commit 026ab955 authored by Hidde-Jan Jongsma's avatar Hidde-Jan Jongsma

Add scaffold of issue / verify components

parent 65158741
......@@ -8,6 +8,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@privacybydesign/irmajs": "^0.1.9",
"axios": "^0.19.2",
"bootstrap-vue": "^2.1.0",
"core-js": "^3.6.4",
......
<template>
<div class="credential-request">
<slot></slot>
<b-overlay :show="status === 'loading'">
<wallet-select
:enabled-wallets="availableWallets"
@choice="(wallet) => (selectedWallet = wallet)"
/>
</b-overlay>
<p v-if="status === 'error'" class="text-danger">Something went wrong :(</p>
</div>
</template>
<script>
import axios from "axios";
import WalletSelect from "./WalletSelect.vue";
import IrmaVerify from "./IrmaVerify.vue";
import JolocomVerify from "./JolocomVerify.vue";
import JolocomIssue from "./JolocomIssue.vue";
const WAIT_TIME = 1000;
const componentMap = {
jolocom: {
verify: JolocomVerify,
issue: JolocomIssue,
},
irma: {
verify: IrmaVerify,
},
};
export default {
name: "CredentialRequest",
components: {
WalletSelect,
},
props: {
token: {
type: String,
required: true,
},
type: {
type: String,
required: true,
validator: (value) => ["issue", "verify"].includes(value),
},
},
data() {
return {
status: "loading",
request: null,
availableWallets: [],
selectedWallet: null,
};
},
computed: {
requestComponent() {
if (this.request && this.selectedWallet) {
return componentMap[this.selectedWallet][this.type];
}
return null;
},
requestId() {
// TODO: get from request
if (!this.request) {
return null;
}
return `credential-${this.type}-request:${this.request.uuid}`;
},
},
sockets: {
// connect() {
// console.log("Connecting to backend");
// this.$socket.emit("started", this.requestId);
// },
// update(status) {
// console.log("Received new status from backend");
// this.status = status;
// },
redirect({ status, redirectUrl }) {
// TODO: Maybe we don't need to show received data?
console.log("Received redirect", status);
this.status = status;
setTimeout(() => (window.location = redirectUrl), WAIT_TIME);
},
},
created() {
setTimeout(this.performRequest.bind(this), WAIT_TIME);
},
methods: {
async performRequest() {
try {
const result = await axios.get(`/api/${this.type}`, {
params: {
token: this.token,
},
});
console.log(result.data);
this.request = result.data[`${this.type}Request`];
this.availableWallets = result.data.availableConnectors;
this.status = "ready";
this.$emit("request", this.request);
} catch (e) {
this.$emit("error", e);
this.status = "error";
console.error(e);
}
},
},
};
</script>
<script>
import irma from "@privacybydesign/irmajs";
export default {
name: "IrmaSession",
props: {
jwt: {
type: String,
required: true,
},
server: {
type: String,
required: true,
},
},
mounted() {
irma
.startSession(this.server, this.jwt, "publickey")
.then(({ sessionPtr, token }) =>
irma.handleSession(sessionPtr, {
token,
server: this.server,
resultJwt: true,
})
)
.then((resultJwt) => {
console.log("Got result from IRMA server", resultJwt);
this.$emit("result", resultJwt);
})
.catch((err) => {
console.log(err);
if (err === irma.SessionStatus.Cancelled) {
this.$emit("cancel");
} else {
this.$emit("error");
}
});
},
};
</script>
<template>
<div class="irma-verify">
<irma-session
v-once
:jwt="jwt"
:server="server"
@result="handleResult"
@cancel="$emit('cancel')"
@error="$emit('error')"
></irma-session>
</div>
</template>
<script>
import IrmaSession from "./IrmaSession.vue";
export default {
name: "IrmaVerify",
components: {
IrmaSession,
},
props: {
// status: {
// type: String,
// required: true,
// },
// requestId: {
// type: String,
// required: true,
// },
jwt: {
type: String,
required: true,
},
server: {
type: String,
required: true,
},
},
methods: {
handleResult(resultJwt) {
this.$emit("update", "handling");
console.log(resultJwt);
// axios
// .post(`/connectors/irma/verify/${this.requestId}`, resultJwt, {
// headers: {
// "Content-Type": "text/plain",
// },
// })
// .then((response) => {
// console.log("Backend handled jwt", response);
// })
// .catch(() => this.$emit("error"));
},
},
};
</script>
<template>
<div class="jolocom-issue">
<div v-if="status === 'started'" class="text-center">
<b-spinner variant="secondary" />
</div>
<b-modal
:visible="showModal"
title="Please scan the QR code with your Jolocom Wallet"
size="lg"
cancel-variant="danger"
no-close-on-esc
no-close-on-backdrop
hide-header-close
@cancel="$emit('cancel')"
@ok="$emit('done')"
>
<p class="text-center">
<img :src="qr" alt="Could not render QR code..." />
</p>
</b-modal>
</div>
</template>
<script>
const WAIT_TIME = 1000;
export default {
name: "JolocomIssue",
props: {
status: {
type: String,
required: true,
},
qr: {
type: String,
required: true,
},
},
data() {
return {
modal: false,
};
},
computed: {
showModal() {
return this.status === "started" && this.modal;
},
},
mounted() {
const that = this;
setTimeout(() => that.showQR(), WAIT_TIME);
},
methods: {
showQR() {
this.modal = true;
},
},
};
</script>
<template>
<div class="jolocom-verify">
<div v-if="status === 'started'" class="text-center">
<b-spinner variant="secondary" />
</div>
<b-modal
:visible="showModal"
title="Please scan the QR code with your Jolocom Wallet"
no-close-on-esc
no-close-on-backdrop
hide-header-close
@cancel="$emit('cancel')"
>
<p class="text-center">
<img :src="qr" alt="Could not render QR code..." />
</p>
<template #modal-footer="{ cancel }">
<b-button variant="danger" @click="cancel">
Cancel
</b-button>
</template>
</b-modal>
</div>
</template>
<script>
const WAIT_TIME = 1000;
export default {
name: "JolocomVerify",
props: {
status: {
type: String,
required: true,
},
qr: {
type: String,
required: true,
},
},
data() {
return {
modal: false,
};
},
computed: {
showModal() {
return this.status === "started" && this.modal;
},
},
mounted() {
const that = this;
setTimeout(() => that.showQR(), WAIT_TIME);
},
methods: {
showQR() {
this.modal = true;
},
},
};
</script>
<template>
<div class="status-header">
<p v-if="notification" class="lead notification" v-text="notification"></p>
</div>
</template>
<script>
const notificationMap = {
started: "Starting session...",
handling: "Handling data...",
error: "Something went wrong, you will be redirected shortly.",
cancelled: "You cancelled the request, you will be redirected shortly.",
done: "Success! We'll redirect you shortly.",
};
export default {
name: "StatusHeader",
props: {
status: {
type: String,
required: true,
},
},
computed: {
notification() {
return notificationMap[this.status];
},
},
};
</script>
......@@ -2,14 +2,18 @@
<b-card
:title="wallet.title"
class="wallet-card text-center"
:class="{ 'wallet-enabled': wallet.enabled }"
:class="{
'wallet-enabled': wallet.enabled,
'wallet-disabled': !wallet.enabled,
'text-muted': !wallet.enabled,
}"
body-class="d-flex flex-column"
@click="wallet.enabled && $emit('choice')"
>
<div class="flex-grow-1 d-flex align-items-center">
<div class="flex-grow-1">
<b-img class="wallet-image p-4" :src="wallet.imageUrl" fluid-grow />
<div v-if="!wallet.enabled" class="text-muted">
<div class="flex-grow-1 p-5">
<b-img class="wallet-image my-1" :src="wallet.imageUrl" fluid-grow />
<div class="enabled-notice text-muted" :aria-hidden="wallet.enabled">
Unavailable
</div>
</div>
......@@ -17,7 +21,7 @@
<template #footer>
<div class="d-inline-flex mx-1">
<a :href="wallet.appleDownloadUrl" target="_blank">
<a :href="wallet.appleDownloadUrl" target="_blank" @click.stop>
<b-img
class="store-badge"
src="../assets/app-store-badge.svg"
......@@ -26,7 +30,7 @@
</a>
</div>
<div class="d-inline-flex mx-1">
<a :href="wallet.googleDownloadUrl" target="_blank">
<a :href="wallet.googleDownloadUrl" target="_blank" @click.stop>
<b-img
class="store-badge"
alt="Get it on Google Play"
......@@ -56,9 +60,21 @@ export default {
height: 40px;
}
.wallet-card:hover {
.wallet-enabled:hover {
/* background: rgba(0, 0, 0, 0.05); */
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.125) !important;
cursor: pointer;
}
.wallet-enabled .enabled-notice {
opacity: 0;
}
.wallet-disabled {
background: rgba(0, 0, 0, 0.05);
}
.wallet-disabled img {
opacity: 0.6;
}
</style>
......@@ -34,7 +34,8 @@
class="data-input"
:value="JSON.stringify(data, null, 2)"
required
@input="(val) => (data = JSON.parse(val))"
rows="6"
@input="updateData"
></b-form-textarea>
</b-form-group>
......@@ -44,7 +45,11 @@
<b-card class="mt-3" header="Created Token">
<pre class="token-output">{{ token }}</pre>
<p v-if="token" class="m-0">
<router-link :to="tokenLink">{{ tokenLinkText }}</router-link>
<router-link
:to="tokenLink"
target="_blank"
v-text="tokenLinkText"
></router-link>
</p>
</b-card>
</div>
......@@ -132,7 +137,7 @@ export default {
this.data = data;
}
} catch {
console.log("Could not decode", newData);
return;
}
},
},
......
<template>
<div class="issue">
{{ token }}
<credential-request :token="token" type="issue">
<div class="py-5 text-center">
<h2>Select your Wallet app</h2>
<p id="loading" class="lead notification">
Please choose the wallet app you want to use to issue your
credentials.
</p>
</div>
</credential-request>
</div>
</template>
<script>
import CredentialRequest from "../components/CredentialRequest.vue";
export default {
name: "Issue",
components: {
CredentialRequest,
},
props: {
token: {
type: String,
required: true,
},
},
data() {
return {
request: null,
};
},
};
</script>
......@@ -7,8 +7,8 @@
</template>
<script>
import CreateTokenForm from "../components/CreateTokenForm.vue";
import CreateRequestForm from "../components/CreateRequestForm.vue";
import CreateTokenForm from "../components/utils/CreateTokenForm.vue";
import CreateRequestForm from "../components/utils/CreateRequestForm.vue";
export default {
name: "Utils",
......
<template>
<div class="verify">
{{ token }}
<credential-request :token="token" type="verify">
<div class="py-5 text-center">
<h2>Select your Wallet app</h2>
<p id="loading" class="lead notification">
Please choose the wallet app you want to use to verify your
credentials.
</p>
</div>
</credential-request>
</div>
</template>
<script>
import CredentialRequest from "../components/CredentialRequest.vue";
export default {
name: "Verify",
components: {
CredentialRequest,
},
props: {
token: {
type: String,
required: true,
},
},
data() {
return {
request: null,
};
},
};
</script>
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment