diff --git a/openid4vci-plugin.php b/openid4vci-plugin.php
index 73a2502..70baea2 100644
--- a/openid4vci-plugin.php
+++ b/openid4vci-plugin.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: Universal OID4VCI
* Description: Issue verifiable credentials using the universal OID4VCI interface with an organization wallet.
- * Version: 0.4.0
+ * Version: 0.4.9
* Requires at least: 6.6
* Requires PHP: 7.2
* Author: Credenco B.V.
@@ -44,6 +44,7 @@
function create_block_openid4vci_block_init() {
register_block_type( __DIR__ . '/build/credentialIssue' );
register_block_type( __DIR__ . '/build/credentialIssueOrgWallet' );
+ register_block_type( __DIR__ . '/build/credentialIssueRaw' );
if(!session_id()) {
session_start();
}
@@ -53,6 +54,57 @@ function create_block_openid4vci_block_init() {
// Add an action to call our script enqueuing function
//add_action( 'wp_enqueue_script', 'enqueue_my_scripts' );
+/**
+ * Probeert de status_uri van een issuance te pollen voor een voltooide credential.
+ *
+ * @param string $statusUri
+ * @param string $authenticationHeaderName
+ * @param string $authenticationToken
+ * @param int $maxAttempts
+ * @param int $delaySeconds
+ *
+ * @return object|null
+ */
+function openid4vci_fetch_status_result( $statusUri, $authenticationHeaderName, $authenticationToken, $maxAttempts = 3, $delaySeconds = 1 ) {
+ if ( empty( $statusUri ) ) {
+ return null;
+ }
+
+ for ( $attempt = 1; $attempt <= $maxAttempts; $attempt ++ ) {
+ $statusResponse = wp_remote_get( $statusUri, array(
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ $authenticationHeaderName => $authenticationToken,
+ ),
+ 'timeout' => 20,
+ 'redirection' => 5,
+ 'blocking' => true,
+ ) );
+
+ if ( is_wp_error( $statusResponse ) ) {
+ error_log( '[openid4vci] status poll fout poging ' . $attempt . ': ' . $statusResponse->get_error_message() );
+ } else {
+ $statusBody = wp_remote_retrieve_body( $statusResponse );
+ $statusResult = json_decode( $statusBody );
+
+ if ( json_last_error() === JSON_ERROR_NONE ) {
+ if ( isset( $statusResult->credential ) ) {
+ return $statusResult;
+ }
+ error_log( '[openid4vci] status poll poging ' . $attempt . ' zonder credential: ' . $statusBody );
+ } else {
+ error_log( '[openid4vci] status poll JSON fout poging ' . $attempt . ': ' . json_last_error_msg() );
+ }
+ }
+
+ if ( $attempt < $maxAttempts ) {
+ sleep( $delaySeconds );
+ }
+ }
+
+ return null;
+}
+
function sendVciRequest($claims, $attributes) {
$options = new OpenID4VCI_Admin_Options();
$openidEndpoint = $options->openidEndpoint;
@@ -119,7 +171,74 @@ function sendVciRequest($claims, $attributes) {
return ["success" => false, "error" => $block_content];
}
+ if ( ! isset( $result->credential ) && isset( $result->status_uri ) ) {
+ $statusResult = openid4vci_fetch_status_result(
+ $result->status_uri,
+ $authenticationHeaderName,
+ $authenticationToken
+ );
+
+ if ( $statusResult ) {
+ $result = $statusResult;
+ }
+ }
+
return ["success" => true, "result" => $result];
}
+function sendRawIssueRequest( $claims, $attributes ) {
+ $options = new OpenID4VCI_Admin_Options();
+ $openidEndpoint = $options->openidEndpoint;
+ $authenticationHeaderName = $options->authenticationHeaderName;
+ $authenticationToken = $options->authenticationToken;
+
+ if ( ! empty( $attributes['openidEndpoint'] ) ) {
+ $openidEndpoint = $attributes['openidEndpoint'];
+ $authenticationHeaderName = $attributes['authenticationHeaderName'];
+ $authenticationToken = $attributes['authenticationToken'];
+ }
+
+ $templateId = $attributes['credentialIssueTemplateKey'] ?? '';
+ if ( empty( $templateId ) ) {
+ $block_content = '
Template ID ontbreekt voor raw issue.
';
+ return [ 'success' => false, 'error' => $block_content ];
+ }
+
+ $payload = array(
+ 'claims' => $claims,
+ 'template_id' => $templateId,
+ );
+
+ $payload = wp_json_encode( $payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
+
+ $response = wp_remote_post( $openidEndpoint, array(
+ 'headers' => array(
+ 'Content-Type' => 'application/json',
+ $authenticationHeaderName => $authenticationToken,
+ ),
+ 'timeout' => 45,
+ 'redirection' => 5,
+ 'blocking' => true,
+ 'body' => $payload,
+ ) );
+
+ if ( is_wp_error( $response ) ) {
+ $block_content = 'Raw issue request mislukt: ' . esc_html( $response->get_error_message() ) . '
';
+ return [ 'success' => false, 'error' => $block_content ];
+ }
+ $body = wp_remote_retrieve_body( $response );
+ $result = json_decode( $body );
+
+ if ( json_last_error() !== JSON_ERROR_NONE ) {
+ $block_content = 'Raw issue JSON fout: ' . json_last_error_msg() . '
';
+ return [ 'success' => false, 'error' => $block_content ];
+ }
+
+ if ( isset( $result->status ) && isset( $result->detail ) ) {
+ $block_content = 'Raw issue API fout: ' . $result->detail . '
';
+ return [ 'success' => false, 'error' => $block_content ];
+ }
+
+ return [ 'success' => true, 'result' => $result ];
+}
diff --git a/readme.txt b/readme.txt
index 951437a..1658642 100644
--- a/readme.txt
+++ b/readme.txt
@@ -2,7 +2,7 @@
Contributors: The WordPress Contributors
Tags: block
Tested up to: 6.6
-Stable tag: 0.2.0
+Stable tag: 0.4.9
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
diff --git a/src/credentialIssueRaw/block.json b/src/credentialIssueRaw/block.json
new file mode 100644
index 0000000..377f5a2
--- /dev/null
+++ b/src/credentialIssueRaw/block.json
@@ -0,0 +1,56 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "openid4vci-plugin/openid4vc-issue-raw",
+ "version": "0.1.0",
+ "title": "OID4VCI – Raw credential issue",
+ "category": "widgets",
+ "description": "Generate a raw SD-JWT credential directly",
+ "example": {},
+ "attributes": {
+ "openidEndpoint": {
+ "type": "string"
+ },
+ "authenticationHeaderName": {
+ "type": "string"
+ },
+ "authenticationToken": {
+ "type": "string"
+ },
+ "credentialIssueTemplateKey": {
+ "type": "string"
+ },
+ "credentialData": {
+ "type": "string"
+ },
+ "formData": {
+ "type": "string"
+ },
+ "sessionData": {
+ "type": "string"
+ },
+ "showCredential": {
+ "type": "boolean",
+ "default": true
+ },
+ "buttonLabel": {
+ "type": "string",
+ "default": "Generate credential"
+ }
+ },
+ "supports": {
+ "color": {
+ "background": false,
+ "text": true
+ },
+ "html": false,
+ "typography": {
+ "fontSize": true
+ }
+ },
+ "textdomain": "openid4vc-issue",
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css",
+ "render": "file:./render.php"
+}
diff --git a/src/credentialIssueRaw/index.asset.php b/src/credentialIssueRaw/index.asset.php
new file mode 100644
index 0000000..996bf75
--- /dev/null
+++ b/src/credentialIssueRaw/index.asset.php
@@ -0,0 +1,2 @@
+ array('react', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n'), 'version' => 'c38a64d9058b58876565');
+
diff --git a/src/credentialIssueRaw/index.js b/src/credentialIssueRaw/index.js
new file mode 100644
index 0000000..cc5de9d
--- /dev/null
+++ b/src/credentialIssueRaw/index.js
@@ -0,0 +1,2 @@
+(()=>{"use strict";const e=window.wp.blocks,n=window.wp.i18n,t=window.wp.blockEditor,a=window.wp.components,o=(window.React,window.ReactJSXRuntime),i=JSON.parse('{"UU":"openid4vci-plugin/openid4vc-issue-raw"}'),s=(0,o.jsx)("svg",{fill:"#000000",width:"800px",height:"800px",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",children:(0,o.jsx)("path",{d:"M4,4h6v6H4V4M20,4v6H14V4h6M14,15h2V13H14V11h2v2h2V11h2v2H18v2h2v3H18v2H16V18H13v2H11V16h3V15m2,0v3h2V15H16M4,20V14h6v6H4M6,6V8H8V6H6M16,6V8h2V6H16M6,16v2H8V16H6M4,11H6v2H4V11m5,0h4v4H11V13H9V11m2-5h2v4H11V6M2,2V6H0V2A2,2,0,0,1,2,0H6V2H2M22,0a2,2,0,0,1,2,2V6H22V2H18V0h4M2,18v4H6v2H2a2,2,0,0,1-2-2V18H2m20,4V18h2v4a2,2,0,0,1-2,2H18V22Z"})});(0,e.registerBlockType)(i.UU,{icon:s,edit:function({attributes:e,setAttributes:i}){const{openidEndpoint:s,credentialIssueTemplateKey:l,authenticationHeaderName:r,authenticationToken:d,credentialData:c,formData:v,sessionData:h,showCredential:u=!0,buttonLabel:p="Genereer credential"}=e;return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.InspectorControls,{children:(0,o.jsxs)(a.PanelBody,{title:(0,n.__)("Settings","openid4vc-issue"),children:[(0,o.jsx)(a.TextControl,{label:(0,n.__)("Credential issue template key","openid4vc-issue"),value:l,onChange:e=>i({credentialIssueTemplateKey:e})}),(0,o.jsx)(a.TextareaControl,{label:(0,n.__)("Credential data","openid4vc-issue"),value:c,onChange:e=>i({credentialData:e})}),(0,o.jsx)(a.TextareaControl,{label:(0,n.__)("Form data","openid4vc-issue"),value:v,onChange:e=>i({formData:e})}),(0,o.jsx)(a.TextareaControl,{label:(0,n.__)("Session data","openid4vc-issue"),value:h,onChange:e=>i({sessionData:e})}),(0,o.jsx)(a.ToggleControl,{label:(0,n.__)("Toon credential in blok","openid4vc-issue"),checked:u,onChange:e=>i({showCredential:e})}),(0,o.jsx)(a.TextControl,{label:(0,n.__)("Knoptekst","openid4vc-issue"),value:p,onChange:e=>i({buttonLabel:e})})]})}),(0,o.jsxs)(t.InspectorAdvancedControls,{children:[(0,o.jsx)(a.TextControl,{label:(0,n.__)("Raw issue endpoint","openid4vc-issue"),value:s,onChange:e=>i({openidEndpoint:e})}),(0,o.jsx)(a.TextControl,{label:"Authentication header",value:r,onChange:e=>i({authenticationHeaderName:e})}),(0,o.jsx)(a.TextControl,{label:"Authentication token",value:d,onChange:e=>i({authenticationToken:e})})]}),(0,o.jsx)("p",{...(0,t.useBlockProps)(),children:(0,o.jsx)("strong",{children:(0,n.__)("Raw issue block – configure template ID en endpoint in de instellingen.","openid4vc-issue")})})]})},save:function(){return null}})})();
+
diff --git a/src/credentialIssueRaw/render.php b/src/credentialIssueRaw/render.php
new file mode 100644
index 0000000..7e424b1
--- /dev/null
+++ b/src/credentialIssueRaw/render.php
@@ -0,0 +1,200 @@
+key, $item->mapping, $item->type ) ) {
+ continue;
+ }
+ $type = $item->type;
+ $mapping = $item->mapping;
+
+ $val = null;
+ if ( isset( $presentationResponse[ $type ]['claims'] ) && is_array( $presentationResponse[ $type ]['claims'] ) ) {
+ $val = getByPath( $presentationResponse[ $type ]['claims'], (string) $mapping );
+ }
+
+ if ( null !== $val ) {
+ setByPath( $claims, (string) $item->key, $val );
+ }
+ }
+ } else {
+ echo 'Er kunnen geen credentials opgehaald worden.
';
+ return;
+ }
+ } else {
+ echo 'SessionData is niet geldig.
';
+ return;
+ }
+}
+
+do_action( 'wp_enqueue_script' );
+
+$html = '';
+$form = false;
+$show_credential = array_key_exists( 'showCredential', $attributes ) ? (bool) $attributes['showCredential'] : true;
+
+if ( isset( $attributes['formData'] ) && ! empty( $attributes['formData'] ) ) {
+ $formData = json_decode( $attributes['formData'] );
+ if ( json_last_error() === JSON_ERROR_NONE && ( is_object( $formData ) || is_array( $formData ) ) ) {
+ $form = true;
+ $html .= '';
+ }
+}
+
+
+$issue_and_render = function () use ( &$claims, $attributes, $show_credential ) {
+ $response = sendRawIssueRequest( $claims, $attributes );
+
+ if ( $response['success'] === false ) {
+ echo $response['error'];
+ return true;
+ }
+
+ $result = $response['result'];
+ if ( isset( $result->credential ) ) {
+ if ( is_string( $result->credential ) ) {
+ $payload = $result->credential;
+ } elseif ( is_object( $result->credential ) && isset( $result->credential->token ) && is_string( $result->credential->token ) ) {
+ $payload = $result->credential->token;
+ } else {
+ $payload = wp_json_encode( $result->credential, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
+ }
+ } elseif ( isset( $result->token ) && is_string( $result->token ) ) {
+ $payload = $result->token;
+ } else {
+ $payload = wp_json_encode( $result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
+ }
+
+ error_log( '[openid4vci] rawIssue credential response: ' . $payload );
+ $_SESSION['RawCredential'] = $payload;
+
+ $block_content = '';
+ if ( $show_credential ) {
+ $block_content .= '
' . __( 'SD-JWT credential:', 'openid4vc-issue' ) . '
'
+ . '
';
+ }
+ $block_content .= '
';
+
+ echo $block_content;
+
+ return true;
+};
+
+if ( isset( $_GET['rawissuerequest'] ) ) {
+ $originalFormKeys = [];
+ if ( isset( $attributes['formData'] ) && ! empty( $attributes['formData'] ) ) {
+ $decodedForm = json_decode( $attributes['formData'] );
+ if ( $decodedForm && ( is_object( $decodedForm ) || is_array( $decodedForm ) ) ) {
+ foreach ( $decodedForm as $k => $label ) {
+ $originalFormKeys[] = (string) $k;
+ }
+ }
+ }
+
+ foreach ( $_GET as $name => $value ) {
+ if ( 'rawissuerequest' === $name ) {
+ continue;
+ }
+
+ $originalKey = $name;
+ foreach ( $originalFormKeys as $formKey ) {
+ if ( $formKey === $name ) {
+ $originalKey = $formKey;
+ break;
+ }
+ if ( str_replace( array( '.', '/' ), '_', $formKey ) === $name ) {
+ $originalKey = $formKey;
+ break;
+ }
+ }
+
+ setByPath( $claims, $originalKey, sanitize_text_field( wp_unslash( $value ) ) );
+ }
+
+ $issue_and_render();
+} elseif ( $form ) {
+ echo '' . $html . '
';
+} else {
+ $submit_label = esc_html( $attributes['buttonLabel'] ?? __( 'Genereer credential', 'openid4vc-issue' ) );
+ echo ''
+ . ''
+ . '
';
+}