<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: eidas-pro</title>
    <description>The latest articles on DEV Community by eidas-pro (@eidas-pro).</description>
    <link>https://dev.to/eidas-pro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F13075%2F2875330c-7a29-4747-9026-ef2af73c9455.jpeg</url>
      <title>DEV Community: eidas-pro</title>
      <link>https://dev.to/eidas-pro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eidas-pro"/>
    <language>en</language>
    <item>
      <title>OpenEUDI SDK Quickstart: Your First EUDI Wallet Verification in 5 Minutes</title>
      <dc:creator>eidas-pro</dc:creator>
      <pubDate>Sat, 30 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/eidas-pro/openeudi-sdk-quickstart-your-first-eudi-wallet-verification-in-5-minutes-2c6k</link>
      <guid>https://dev.to/eidas-pro/openeudi-sdk-quickstart-your-first-eudi-wallet-verification-in-5-minutes-2c6k</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OpenEUDI is open source (MIT) and live on npm.&lt;/strong&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/openeudi" rel="noopener noreferrer"&gt;https://github.com/openeudi&lt;/a&gt; · npm: &lt;a href="https://www.npmjs.com/package/@openeudi/core" rel="noopener noreferrer"&gt;&lt;code&gt;@openeudi/core&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@openeudi/openid4vp" rel="noopener noreferrer"&gt;&lt;code&gt;@openeudi/openid4vp&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Originally published on &lt;a href="https://eidas-pro.com/blog/openeudi-sdk-quickstart-tutorial" rel="noopener noreferrer"&gt;eidas-pro.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 20+ installed&lt;/li&gt;
&lt;li&gt;TypeScript project (or JavaScript with ESM)&lt;/li&gt;
&lt;li&gt;A terminal and your favorite editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. No API keys, no registration, no WRPAC certificate. DEMO mode works out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @openeudi/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with your preferred package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @openeudi/core
yarn add @openeudi/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Create a Verifier
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InMemorySessionStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@openeudi/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize in DEMO mode — no credentials needed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemorySessionStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mode&lt;/code&gt; option controls how the SDK behaves:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new DemoMode()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-completes after 3 seconds&lt;/td&gt;
&lt;td&gt;Showcases, sales demos, landing pages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new MockMode()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Simulates wallet responses with configurable data&lt;/td&gt;
&lt;td&gt;Integration testing, CI/CD pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new ProductionMode(config)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Real EUDI Wallet verification&lt;/td&gt;
&lt;td&gt;Live verifications (requires WRPAC, Dec 2026+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Create a Verification Session
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// session.qrCodeUrl contains a data URL for the QR code image&lt;/span&gt;
&lt;span class="c1"&gt;// session.sessionId is the unique session identifier&lt;/span&gt;
&lt;span class="c1"&gt;// session.deepLink is for mobile-to-mobile flows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;attributes&lt;/code&gt; array specifies what you want to verify. eIDAS Pro is designed around &lt;strong&gt;privacy-first boolean attributes&lt;/strong&gt; — you get a yes/no answer, never raw personal data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age_over_18&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Is the user 18 or older?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age_over_21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Is the user 21 or older?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age_over_16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Is the user 16 or older?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age_over_14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Is the user 14 or older?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Country compliance (which countries are allowed) is configured in your merchant settings as a whitelist or blacklist — the SDK checks this automatically without requesting personal data from the user.&lt;/p&gt;

&lt;p&gt;Note: In DEMO mode, attributes return synthetic data. In PRODUCTION mode, the wallet cryptographically proves the boolean without revealing the underlying birth date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Display the QR Code
&lt;/h2&gt;

&lt;p&gt;The session includes a QR code that the user scans with their EUDI Wallet app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a web context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrCodeUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qr-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// In React&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VerificationQR&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrCodeUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scan with your EUDI Wallet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In Next.js (using next/image)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VerificationQR&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrCodeUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scan with your EUDI Wallet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Listen for the Result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Event-based (recommended for web apps)&lt;/span&gt;
&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verification successful!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verified:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// true&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Country allowed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;countryAllowed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true (based on your whitelist)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Session ID:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Timestamp:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verifiedAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verification failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// 'timeout' | 'rejected' | 'invalid_credential' | 'network_error'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Session expired — generate a new QR code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the promise-based API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForResult&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// 2 min timeout&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verified:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed or timed out:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Complete Example: Express.js API Route
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InMemorySessionStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@openeudi/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemorySessionStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new verification session&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;qrCodeUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrCodeUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Check session status (or use SSE for real-time)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/verify/:sessionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSessionStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// SSE endpoint for real-time updates&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/verify/:sessionId/events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Happens in DEMO Mode?
&lt;/h2&gt;

&lt;p&gt;When you run this code in DEMO mode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A QR code is generated (visually identical to a real one)&lt;/li&gt;
&lt;li&gt;After 3 seconds, the session automatically completes&lt;/li&gt;
&lt;li&gt;Synthetic verification data is returned (age_over_18: true, countryAllowed: true)&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;onVerified&lt;/code&gt; callback fires with the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No real wallet is involved. This lets you build and test the entire verification UI and backend flow before EUDI Wallets launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Switch to MockMode&lt;/strong&gt; to test with configurable responses (success, failure, timeout)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add the WooCommerce plugin&lt;/strong&gt; to your WordPress store for checkout verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the API reference&lt;/strong&gt; for advanced configuration (custom QR styles, session options, error handling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join the GitHub discussions&lt;/strong&gt; at &lt;a href="https://github.com/openeudi" rel="noopener noreferrer"&gt;https://github.com/openeudi&lt;/a&gt; to report issues or request features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When EUDI Wallets launch in December 2026, switch &lt;code&gt;new DemoMode()&lt;/code&gt; to &lt;code&gt;new ProductionMode(config)&lt;/code&gt; and add your WRPAC credentials — or use eIDAS Pro's managed service to skip the certificate management entirely.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;OpenEUDI is MIT-licensed and available on npm today (&lt;code&gt;@openeudi/core&lt;/code&gt;, &lt;code&gt;@openeudi/openid4vp&lt;/code&gt;). Star the &lt;a href="https://github.com/openeudi" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; to follow releases.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>tutorial</category>
      <category>node</category>
      <category>eidas</category>
    </item>
    <item>
      <title>Introducing OpenEUDI: An Open-Source SDK for EU Digital Identity Verification</title>
      <dc:creator>eidas-pro</dc:creator>
      <pubDate>Fri, 29 May 2026 12:14:52 +0000</pubDate>
      <link>https://dev.to/eidas-pro/introducing-openeudi-an-open-source-sdk-for-eu-digital-identity-verification-1eae</link>
      <guid>https://dev.to/eidas-pro/introducing-openeudi-an-open-source-sdk-for-eu-digital-identity-verification-1eae</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OpenEUDI is open source (MIT) and live on npm.&lt;/strong&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/openeudi" rel="noopener noreferrer"&gt;https://github.com/openeudi&lt;/a&gt; · npm: &lt;a href="https://www.npmjs.com/package/@openeudi/core" rel="noopener noreferrer"&gt;&lt;code&gt;@openeudi/core&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@openeudi/openid4vp" rel="noopener noreferrer"&gt;&lt;code&gt;@openeudi/openid4vp&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Originally published on &lt;a href="https://eidas-pro.com/blog/introducing-openeudi-open-source-sdk" rel="noopener noreferrer"&gt;eidas-pro.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why We're Open-Sourcing the Protocol Layer
&lt;/h2&gt;

&lt;p&gt;The EU Digital Identity Wallet is coming. By December 2026, every EU member state must issue EUDI Wallets to their citizens under Regulation (EU) 2024/1183. Businesses that verify customer identities — from age-restricted e-commerce to financial KYC — will need to speak the OpenID4VP protocol to communicate with these wallets.&lt;/p&gt;

&lt;p&gt;But here's the problem: &lt;strong&gt;there is no production-ready, open-source verification library for the Relying Party side.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The European Commission published a reference wallet app (the holder/citizen side), but not a reference implementation for businesses that need to &lt;em&gt;verify&lt;/em&gt; credentials. Existing SSI toolkits like walt.id (Kotlin/JVM) and SpruceID (Rust) target generic use cases, not the specific EUDI Wallet Architecture Reference Framework.&lt;/p&gt;

&lt;p&gt;We've been building eIDAS Pro — a managed identity verification platform — for the past year. During that work, we built a TypeScript implementation of the OpenID4VP verification protocol. We extracted that protocol layer and released it as &lt;strong&gt;OpenEUDI&lt;/strong&gt;: a free, MIT-licensed SDK that any developer can use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OpenEUDI Is
&lt;/h2&gt;

&lt;p&gt;OpenEUDI is a TypeScript library that handles the technical complexity of verifying EU Digital Identity Wallet credentials. It implements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenID4VP&lt;/strong&gt; (Verifiable Presentations) — the protocol that EUDI Wallets use to share verified attributes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SD-JWT VC&lt;/strong&gt; verification — selective disclosure JSON Web Tokens for remote verification flows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mdoc/mDL&lt;/strong&gt; verification — CBOR-based mobile document format for proximity verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU Trust List integration&lt;/strong&gt; — certificate chain validation against member state trust services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR code session management&lt;/strong&gt; — generate QR codes, track session state, receive results via Server-Sent Events
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InMemorySessionStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@openeudi/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerificationService&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DemoMode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemorySessionStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Display to user&lt;/span&gt;

&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ageOver18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What OpenEUDI Is Not
&lt;/h2&gt;

&lt;p&gt;OpenEUDI is the &lt;strong&gt;protocol library&lt;/strong&gt;, not the full production stack. To run live verifications against real EUDI Wallets (launching December 2026), you also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;WRPAC certificate&lt;/strong&gt; (Wallet Relying Party Access Certificate) from a qualified trust service provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relying Party registration&lt;/strong&gt; with a national authority&lt;/li&gt;
&lt;li&gt;Production infrastructure: rate limiting, monitoring, audit logging, SLA guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what eIDAS Pro's managed service provides. The SDK handles the &lt;em&gt;protocol&lt;/em&gt;; the managed service handles the &lt;em&gt;infrastructure&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like PostgreSQL (free, open-source database engine) vs. a managed database service (hosted, monitored, backed up). Both have value. The open-source tool gets you started; the managed service gets you to production without operational overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Open Core Model
&lt;/h2&gt;

&lt;p&gt;We believe the verification protocol should be open infrastructure — like HTTP libraries or TLS implementations. Everyone benefits when the protocol layer is transparent, auditable, and community-maintained.&lt;/p&gt;

&lt;p&gt;Our business model is straightforward:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;What It Includes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free, MIT license&lt;/td&gt;
&lt;td&gt;OpenEUDI SDK, plugins, documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Paid SaaS&lt;/td&gt;
&lt;td&gt;Managed WRPAC, production API, dashboards, SLA, support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; can build and test verification flows for free&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Businesses&lt;/strong&gt; that want managed compliance pay for the infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ecosystem&lt;/strong&gt; benefits from a shared, auditable protocol implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security researchers&lt;/strong&gt; can inspect the code that handles identity verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Included
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core SDK (&lt;code&gt;@openeudi/core&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The protocol library itself: OpenID4VP request/response handling, credential verification, session management, and EU trust list parsing.&lt;/p&gt;

&lt;p&gt;The OpenEUDI ecosystem consists of two packages: &lt;code&gt;@openeudi/core&lt;/code&gt; handles verification session management, and &lt;code&gt;@openeudi/openid4vp&lt;/code&gt; provides credential parsing for SD-JWT VC and mDOC formats with cryptographic signature validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  E-Commerce Plugins
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WooCommerce plugin&lt;/strong&gt; (GPL v3) — age verification at checkout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify app&lt;/strong&gt; — Checkout UI Extension for identity verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript widget&lt;/strong&gt; — embeddable verification for any website&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reference Server
&lt;/h3&gt;

&lt;p&gt;A demo verification server with three modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DEMO&lt;/strong&gt;: auto-completes in 3 seconds for showcases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MOCK&lt;/strong&gt;: simulated wallet responses for testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TEST&lt;/strong&gt;: full protocol compliance for integration testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Developer guides, API reference, integration tutorials, and a machine-readable EU compliance mapping (JSON) covering all 27 member states.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built in Luxembourg
&lt;/h2&gt;

&lt;p&gt;OpenEUDI is developed in Luxembourg, at the heart of Europe's fintech ecosystem. Luxembourg's strategic focus on digital identity, fintech, and regulatory compliance — combined with programmes like Fit 4 Start, LHoFT, and the SNCI — makes it the ideal base for building pan-European identity infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;OpenEUDI is live on npm. You can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install it today&lt;/strong&gt; and build a verification flow in DEMO mode — no API keys, no WRPAC required:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install&lt;/span&gt; @openeudi/core @openeudi/openid4vp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Star the repository&lt;/strong&gt; on GitHub at &lt;a href="https://github.com/openeudi" rel="noopener noreferrer"&gt;https://github.com/openeudi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try the live demo&lt;/strong&gt; on the &lt;a href="https://eidas-pro.com" rel="noopener noreferrer"&gt;eIDAS Pro homepage&lt;/a&gt; to see the verification flow in action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the &lt;a href="https://eidas-pro.com/blog/openeudi-sdk-quickstart-tutorial" rel="noopener noreferrer"&gt;quickstart tutorial&lt;/a&gt;&lt;/strong&gt; to ship your first verification in 5 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shipped&lt;/strong&gt;: &lt;code&gt;@openeudi/core&lt;/code&gt; and &lt;code&gt;@openeudi/openid4vp&lt;/code&gt; are published on npm (paired releases, currently v0.8.0)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In progress&lt;/strong&gt;: WooCommerce and Shopify plugins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toward the EUDI Wallet launch&lt;/strong&gt;: production-mode verification aligned with national wallet availability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing&lt;/strong&gt;: community contributions, additional framework integrations, language bindings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're building OpenEUDI in the open. If you're working on EUDI Wallet integration, identity verification, or EU regulatory compliance, we'd love to hear from you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;OpenEUDI is MIT-licensed and free forever. The eIDAS Pro managed service provides production infrastructure, WRPAC certificates, and SLA guarantees for businesses that need managed compliance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>eidas</category>
    </item>
    <item>
      <title>eIDAS Summit 2026 Berlin: 5 Takeaways for Merchants</title>
      <dc:creator>eidas-pro</dc:creator>
      <pubDate>Thu, 30 Apr 2026 18:18:09 +0000</pubDate>
      <link>https://dev.to/eidas-pro/eidas-summit-2026-berlin-5-takeaways-for-merchants-3p5j</link>
      <guid>https://dev.to/eidas-pro/eidas-summit-2026-berlin-5-takeaways-for-merchants-3p5j</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlwhj2ekvs6i97na8cz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlwhj2ekvs6i97na8cz8.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated 8 May 2026 with post-Summit notes — see the section at the end of this post. The framing below reflects the agenda as published on 27 April 2026.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Bitkom eIDAS Summit ran 28–29 April 2026 in Berlin. Day 1 was on-site at the Representation of the State of Baden-Württemberg, in German, with a national focus. Day 2 was English and online for a European audience. The keynote line-up was heavier than any previous edition: Federal Digital Minister Dr. Karsten Wildberger for BMDS, Norbert Sagstetter for the European Commission's Digital Identity Unit, Dr. Markus Reichel as rapporteur from the CDU/CSU group, and Christina Raab (Bitkom Vice President, Accenture DACH CEO) opening the industry side.&lt;/p&gt;

&lt;p&gt;The reason this Summit is worth a separate post is not the keynote roster. It is the way the agenda has been structured.&lt;/p&gt;

&lt;p&gt;Earlier eIDAS Summits were protocol-led — sessions on OpenID4VP, on mDoc, on selective disclosure, on cryptographic suites. This one is sector-led: the published programme tracked country wallet demos, large-scale pilots, the European Business Wallet, accessibility, trust services in the cloud, content credentials, and age verification as the structural lens.&lt;/p&gt;

&lt;p&gt;That shift is the signal. Five takeaways every relying party and merchant should leave with — even those who never logged into the live stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Sector-specific use cases are now the lens
&lt;/h2&gt;

&lt;p&gt;The Day 2 programme tracked sectors, not protocols. Country wallet demos. Large-scale pilots. The European Business Wallet. Accessibility. Trust services in the cloud. Content credentials. Age verification. That structure tells you who Day 2 is for: relying parties making integration decisions in the next 8 months, not standards bodies refining the spec.&lt;/p&gt;

&lt;p&gt;For merchants, the implication is simple. The integration scope is no longer "implement OpenID4VP." It is "implement the wallet in your checkout, with your fallback flow, your support training, your localization, your pre-warming sequence." The protocol is settled. The integration is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The 100-company MoU is your integrator directory
&lt;/h2&gt;

&lt;p&gt;Germany's Federal Ministry for Digital and State Modernisation (BMDS) signed a Memorandum of Understanding with 100+ companies on 28 April. The signatories include the major German banks, telcos, healthcare systems, transport companies, and a long tail of integrators.&lt;/p&gt;

&lt;p&gt;If you are a relying party trying to figure out who can deliver a wallet integration that actually ships in 2026, this list is the answer. It is not a procurement guarantee — these are MoU signatories, not a vetted supplier list — but it is a discovery shortcut. If your integrator is not on it, ask why. If they are, ask which other signatories they have already worked with.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The European Business Wallet is the B2B sibling
&lt;/h2&gt;

&lt;p&gt;The European Business Wallet got real airtime on Day 2. It is the B2B sibling to the citizen wallet — same protocol stack, different attribute set, different relying-party expectations.&lt;/p&gt;

&lt;p&gt;Most merchants who covered the Summit last week ignored the EBW. Don't. Procurement workflows, supplier onboarding, KYB (know-your-business), B2B authentication, regulated cross-border B2B services — all of these become wallet-native in 2027. The B2C wallet rollout gets the headlines; the B2B wallet rollout gets the durable margin.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Germany's 2 January 2027 launch sets the cross-border tone
&lt;/h2&gt;

&lt;p&gt;Germany goes live first. First wallet in production, first relying-party register, first regulated verticals binding. Whatever Germany ships becomes the de-facto standard that every other member state's national wallet has to interoperate with.&lt;/p&gt;

&lt;p&gt;For merchants outside Germany, the implication is concrete: even if your home market launches in mid-2027 or 2028, your German customers will arrive with a wallet on 2 January 2027. Cross-border verification flows become the binding constraint, not the German market alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The policy/merchant gap is now visible
&lt;/h2&gt;

&lt;p&gt;The biggest signal from Day 2 is the gap that nobody on stage named directly. The Bitkom 13 April survey put 52% of Germans at "never heard of the EUDI Wallet" and 5% at "can explain it." Day 2 framed the awareness gap as a marketing problem — something for BMDS, the Commission, and Bitkom to address with public campaigns.&lt;/p&gt;

&lt;p&gt;It is not. It is a checkout problem. The 5% who can explain the wallet on 2 January 2027 will be over-represented in your funnel from day one — they are the early adopters, the privacy-conscious, the technically-curious. The other 95% will arrive with confusion. They will need plain checkout copy, a fallback flow, FAQ answers, and support training.&lt;/p&gt;

&lt;p&gt;Merchants who treat this as a marketing-department noise will hand their first 12 months to competitors. Merchants who treat it as integration scope will spend Q1 2027 watching an unfamiliar button outperform every legacy verification path they have ever shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-Summit notes — 8 May 2026
&lt;/h2&gt;

&lt;p&gt;Five things changed between the Summit's close on 29 April and the second week of May. Each one shifts the merchant-side reading of the agenda above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One — the activation gap reframed the Bitkom story.&lt;/strong&gt; Bitkom's &lt;a href="https://www.bitkom.org/Presse/Presseinformation/Mehrheit-Deutschen-will-EUDI-Wallet-nutzen" rel="noopener noreferrer"&gt;follow-up survey on 27 April&lt;/a&gt; shifted the headline from "52% never heard of it" to "54% would use it but only 18% have an activated eID with a working PIN." The Summit's communications-track sessions, written for the awareness gap, landed in the room against an activation gap that is harder to close. We unpack the implications for merchants in our &lt;a href="https://eidas-pro.com/blog/bitkom-54-percent-want-eudi-wallet-but-only-18-percent-activated-eid" rel="noopener noreferrer"&gt;activation-gap post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two — ENISA's hardest quote arrived on 28 April.&lt;/strong&gt; The public-review draft of the EUCC for EUDI Wallets carried the single most consequential admission from the cybersecurity track: "In early 2026, no EUDI Wallet has been deployed or certified, and the specification remains work in progress." &lt;a href="https://www.biometricupdate.com/202604/eu-commission-doubtful-all-member-states-will-be-able-launch-eudi-wallets-this-year" rel="noopener noreferrer"&gt;Biometric Update's coverage&lt;/a&gt; frames the implications for the end-2027 Article 5f acceptance deadline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three — member states broke ranks on the EU age-verification app.&lt;/strong&gt; The Commission's 29 April recommendation to deploy a white-label EU AV app drew immediate pushback from Germany, Ireland, France, Poland, and Estonia, with Germany routing age verification through the wallet's age attribute instead. The political track formalised on 6 May in &lt;a href="https://data.consilium.europa.eu/doc/document/ST-8985-2026-INIT/en/pdf" rel="noopener noreferrer"&gt;Council document 8985/26&lt;/a&gt;. Full analysis in our &lt;a href="https://eidas-pro.com/blog/eu-age-verification-app-member-states-fragmentation-may-2026" rel="noopener noreferrer"&gt;member-state fragmentation post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four — the cross-border critique sharpened.&lt;/strong&gt; Mirko Mollik's &lt;a href="https://mmollik.medium.com/worthless-outside-europe-the-eudi-wallets-global-reality-check-44ce8a26894f" rel="noopener noreferrer"&gt;"Worthless outside Europe?"&lt;/a&gt; (3 May), written from IIW Mountain View, surfaced what the Summit underplayed: outside the EU, the wallet is being routed around rather than through. For B2B and globally-active relying parties, a meaningful 2027 planning input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Five — civil-society critique consolidated.&lt;/strong&gt; The &lt;a href="https://epicenter.works/en/content/five-problems-the-commission-must-fix-in-the-eu-wallet" rel="noopener noreferrer"&gt;epicenter.works "five problems" open letter&lt;/a&gt; (3 May) pulled together the substantive privacy critiques into one document. Two of the five — registration-certificate over-asking and weakened pseudonymity — touch the relying-party flow directly and are worth a read regardless of where you sit on the framing.&lt;/p&gt;

&lt;p&gt;Net for a Q3-2026 integration roadmap: the activation-gap and member-state-fragmentation stories are the two takeaways that change the build order. The ENISA admission and the cross-border critique are useful priors for risk language in internal stakeholder discussions but do not, by themselves, alter the integration plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do Monday morning
&lt;/h2&gt;

&lt;p&gt;We packaged the merchant decision into 5 questions. Score yourself.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are you in scope under Article 5f? Which paragraph applies?&lt;/li&gt;
&lt;li&gt;Have you mapped your wallet relying-party registration path?&lt;/li&gt;
&lt;li&gt;Have you scoped your attributes to the minimum needed?&lt;/li&gt;
&lt;li&gt;Is your wallet+fallback flow designed and tested?&lt;/li&gt;
&lt;li&gt;Have you tested against at least two wallet implementations?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5/5 → on track for a clean Q1 2027 launch. 3–4/5 → at risk. 0–2/5 → treat 2027 as a build year, not a launch year.&lt;/p&gt;

&lt;p&gt;Full readiness checklist: &lt;a href="https://eidas-pro.com/blog/eudi-wallet-readiness-checklist-merchants-5-questions" rel="noopener noreferrer"&gt;https://eidas-pro.com/blog/eudi-wallet-readiness-checklist-merchants-5-questions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we wrote this
&lt;/h2&gt;

&lt;p&gt;We are eIDAS Pro. We build EUDI Wallet verification infrastructure for merchants — drop-in checkout integrations, fallback flows, sector-specific support training, and the open-source SDK that powers them (&lt;code&gt;@openeudi/core&lt;/code&gt;, &lt;code&gt;@openeudi/openid4vp&lt;/code&gt;, Apache 2.0).&lt;/p&gt;

&lt;p&gt;If you are 8 months from launch and behind on integration scope, we can help. If you are not behind, we want to learn what you got right.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://eidas-pro.com/blog/eidas-summit-2026-berlin-bitkom-merchant-takeaways" rel="noopener noreferrer"&gt;https://eidas-pro.com/blog/eidas-summit-2026-berlin-bitkom-merchant-takeaways&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eidas</category>
      <category>eu</category>
      <category>webdev</category>
      <category>eudi</category>
    </item>
    <item>
      <title>EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened</title>
      <dc:creator>eidas-pro</dc:creator>
      <pubDate>Sat, 18 Apr 2026 19:06:10 +0000</pubDate>
      <link>https://dev.to/eidas-pro/eu-age-verification-app-hacked-in-2-minutes-what-actually-happened-2d3p</link>
      <guid>https://dev.to/eidas-pro/eu-age-verification-app-hacked-in-2-minutes-what-actually-happened-2d3p</guid>
      <description>&lt;p&gt;On April 15, 2026, the EU launched the age verification "mini-wallet" app. Within 24 hours, security researcher Paul Moore published a video claiming he bypassed it in 2 minutes. The story went viral across Reddit with tens of thousands of upvotes.&lt;/p&gt;

&lt;p&gt;The headlines write themselves, but the technical reality is more nuanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Flaws
&lt;/h2&gt;

&lt;p&gt;Paul Moore identified three design flaws in the wallet app's local device implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. PIN decoupled from the credential vault&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The PIN protecting the wallet is verified locally, separate from the cryptographic key store that holds the actual credentials. On a rooted device, the PIN can be brute-forced without triggering the key store's protections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Rate limiting stored as plaintext&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lockout counters that prevent repeated PIN attempts are stored as plaintext values in local storage. With root access, these counters can be reset directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Biometric gate is a boolean flag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biometric authentication check resolves to a simple boolean value. On a rooted device, this flag can be toggled without actually completing biometric verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Remains Secure
&lt;/h2&gt;

&lt;p&gt;All three bypasses share a critical prerequisite: &lt;strong&gt;physical access to a rooted device&lt;/strong&gt;. None of them enable remote attacks.&lt;/p&gt;

&lt;p&gt;More importantly, the &lt;strong&gt;OpenID4VP protocol&lt;/strong&gt; that governs the actual verification transaction between wallet and verifier was not compromised. When a verifier checks someone's age, they validate cryptographic proofs signed by the PID (Person Identification Data) provider. These signatures cannot be forged through any of the three local bypasses.&lt;/p&gt;

&lt;p&gt;For developers building age-gated services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The signed attestations your backend validates are still trustworthy&lt;/li&gt;
&lt;li&gt;No changes needed to your verification integration&lt;/li&gt;
&lt;li&gt;The privacy model (verifiers receive yes/no, never the actual birthdate) is intact&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation vs. Protocol
&lt;/h2&gt;

&lt;p&gt;This is a textbook case of the distinction between &lt;strong&gt;protocol soundness&lt;/strong&gt; and &lt;strong&gt;implementation quality&lt;/strong&gt;. The cryptographic architecture — based on OpenID4VP credential presentation — is well-designed. The local device protections that guard access to the wallet were implemented with shortcuts.&lt;/p&gt;

&lt;p&gt;The fixes are straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bind PIN verification to the hardware key store&lt;/li&gt;
&lt;li&gt;Move rate limiting into the secure enclave&lt;/li&gt;
&lt;li&gt;Replace the boolean biometric flag with a challenge-response tied to the key store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these require protocol-level changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structural Concern
&lt;/h2&gt;

&lt;p&gt;The bigger issue is platform lock-in. The wallet app only runs on iOS and Android, with a hard dependency on Google Play Services. There is no libre client, no desktop version, and no way for users of alternative mobile platforms to participate.&lt;/p&gt;

&lt;p&gt;For a system mandated by EU regulation, this exclusivity raises legitimate accessibility and sovereignty questions that outlast the fixable implementation bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Positive Signal
&lt;/h2&gt;

&lt;p&gt;Open-source security auditing worked exactly as designed. The code was auditable, a researcher found flaws on day one, and published them responsibly. This is the model functioning correctly.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Full technical analysis with more detail on the protocol and platform lock-in concerns: &lt;a href="https://eidas-pro.com/blog/eu-age-verification-app-hack-explained" rel="noopener noreferrer"&gt;EU Age Verification App Hack Explained&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>eudi</category>
      <category>webdev</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
