diff --git a/web/src/admin/IdentityDetails.vue b/web/src/admin/IdentityDetails.vue index 4e5e87d..315ad96 100644 --- a/web/src/admin/IdentityDetails.vue +++ b/web/src/admin/IdentityDetails.vue @@ -1,6 +1,10 @@ @@ -43,6 +51,9 @@ export default { return { selectedCredentialType: '', selectedWalletDID: '', + authorizationDetails: '', + authorizationDetailsError: undefined, + authorizationDetailsPlaceholder: '[{"type": "openid_credential", ...}]', issueError: undefined, credentialProfiles: [], walletDIDs: [], @@ -53,6 +64,11 @@ export default { return this.$route.params.subjectID } }, + watch: { + authorizationDetails() { + this.authorizationDetailsError = undefined + } + }, created() { // Fetch config for credential profiles this.$api.get('api/config') @@ -93,6 +109,7 @@ export default { }, issueCredential() { this.issueError = undefined + this.authorizationDetailsError = undefined const issuerDID = this.getIssuerForType(this.selectedCredentialType) if (!issuerDID) { this.issueError = 'No issuer found for selected credential type' @@ -104,6 +121,28 @@ export default { return } + let parsedAuthorizationDetails + const trimmedAuthorizationDetails = this.authorizationDetails.trim() + if (trimmedAuthorizationDetails) { + try { + parsedAuthorizationDetails = JSON.parse(trimmedAuthorizationDetails) + } catch (e) { + this.authorizationDetailsError = 'Invalid JSON: ' + e.message + return + } + if (!Array.isArray(parsedAuthorizationDetails)) { + this.authorizationDetailsError = 'Authorization Details must be a JSON array' + return + } + const nonObject = parsedAuthorizationDetails.find( + entry => entry === null || typeof entry !== 'object' || Array.isArray(entry) + ) + if (nonObject !== undefined) { + this.authorizationDetailsError = 'Each Authorization Details entry must be a JSON object' + return + } + } + const redirectUri = `${window.location.origin}${window.location.pathname}${this.$router.resolve({name: 'admin.identityDetails', params: {subjectID: this.subjectID}}).href}` const requestBody = { @@ -112,6 +151,9 @@ export default { wallet_did: this.selectedWalletDID, redirect_uri: redirectUri, } + if (parsedAuthorizationDetails !== undefined) { + requestBody.authorization_details = parsedAuthorizationDetails + } this.$api.post(`api/proxy/internal/auth/v2/${encodeURIPath(this.subjectID)}/request-credential`, requestBody) .then(data => { @@ -147,5 +189,22 @@ export default { :deep(select) { width: 100%; } + +details { + margin-top: 0.5rem; +} + +details > summary { + cursor: pointer; + user-select: none; + font-weight: 500; +} + +details > textarea { + width: 100%; + margin-top: 0.5rem; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.875rem; +} diff --git a/web/src/components/ErrorMessage.vue b/web/src/components/ErrorMessage.vue index 8c1bee9..efb4ee0 100644 --- a/web/src/components/ErrorMessage.vue +++ b/web/src/components/ErrorMessage.vue @@ -3,7 +3,7 @@ class="appearance-none mb-2 mt-2 px-6 py-3.5 w-full text-sm text-red-500 bg-red-100 placeholder-red-500 outline-hidden border border-red-500 focus:ring-4 focus:ring-blue-200 rounded-md" role="alert">
{{ title }}
-
{{ message }}
+
{{ message }}