<?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: abdulrahman abubakar</title>
    <description>The latest articles on DEV Community by abdulrahman abubakar (@chirograph).</description>
    <link>https://dev.to/chirograph</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4095754%2F1ac131b4-56bb-4e26-b028-c6c6614b1e17.png</url>
      <title>DEV Community: abdulrahman abubakar</title>
      <link>https://dev.to/chirograph</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chirograph"/>
    <language>en</language>
    <item>
      <title>Building passkey-based verification without trusting the browser.</title>
      <dc:creator>abdulrahman abubakar</dc:creator>
      <pubDate>Fri, 30 Oct 2026 08:05:26 +0000</pubDate>
      <link>https://dev.to/chirograph/building-passkey-based-verification-without-trusting-the-browser-ji0</link>
      <guid>https://dev.to/chirograph/building-passkey-based-verification-without-trusting-the-browser-ji0</guid>
      <description>&lt;p&gt;Most applications need to answer a deceptively simple question: should this request be allowed to continue?&lt;/p&gt;

&lt;p&gt;Historically, many applications have relied on CAPTCHAs, SMS one-time passwords, or custom browser-side checks. Each approach comes with trade-offs involving user friction, delivery costs, phishing risk, or the difficulty of determining what a client can actually prove.&lt;/p&gt;

&lt;p&gt;We have been building Chirograph Verify around a different approach: WebAuthn-based device verification, with the verification decision made on the server.&lt;/p&gt;

&lt;p&gt;We're launching Chirograph Verify on October 30, 2026, and I wanted to share the engineering ideas behind it.&lt;/p&gt;

&lt;p&gt;The browser should not be the authority&lt;/p&gt;

&lt;p&gt;One principle shaped the implementation from the beginning: the browser should collect the authentication response, but it should not be trusted to decide whether verification succeeded.&lt;/p&gt;

&lt;p&gt;A client can display a success message. That does not make the result trustworthy.&lt;/p&gt;

&lt;p&gt;Chirograph Verify uses WebAuthn assertions generated through a user's passkey or supported security key. The server validates the submitted assertion and makes the verification decision.&lt;/p&gt;

&lt;p&gt;This gives the application a cryptographic result to evaluate rather than relying solely on a client-side boolean.&lt;/p&gt;

&lt;p&gt;What the verification flow looks like&lt;/p&gt;

&lt;p&gt;At a high level, the flow has five stages:&lt;/p&gt;

&lt;p&gt;An application initiates a verification request.&lt;/p&gt;

&lt;p&gt;The server creates the appropriate challenge or hosted flow.&lt;/p&gt;

&lt;p&gt;The user completes the WebAuthn ceremony on their device.&lt;/p&gt;

&lt;p&gt;The server validates the response.&lt;/p&gt;

&lt;p&gt;The application receives a verification result and, where applicable, a device-trust score.&lt;/p&gt;

&lt;p&gt;The distinction between registration and authentication is important. A new user needs to enroll a passkey first; a returning user can authenticate using an enrolled credential.&lt;/p&gt;

&lt;p&gt;Integrating a hosted widget&lt;/p&gt;

&lt;p&gt;We built a hosted widget so developers do not have to implement every browser-side ceremony themselves.&lt;/p&gt;

&lt;p&gt;The browser loads the widget bundle:&lt;/p&gt;

&lt;p&gt;It then initializes the widget with a publishable key:&lt;/p&gt;

&lt;p&gt;const widget = ChirographWidget.init({&lt;br&gt;
  baseUrl: "&lt;a href="https://app.chirographverify.com" rel="noopener noreferrer"&gt;https://app.chirographverify.com&lt;/a&gt;",&lt;br&gt;
  widgetKey: "YOUR_PUBLISHABLE_WIDGET_KEY"&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;For a new user, the application starts a registration flow:&lt;/p&gt;

&lt;p&gt;await widget.start({&lt;br&gt;
  userReference: "&lt;a href="mailto:user@example.com"&gt;user@example.com&lt;/a&gt;",&lt;br&gt;
  purpose: "register",&lt;br&gt;
  redirectUrl: "&lt;a href="https://your-app.example.com/auth/callback" rel="noopener noreferrer"&gt;https://your-app.example.com/auth/callback&lt;/a&gt;"&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;For an enrolled user, the purpose changes to authenticate.&lt;/p&gt;

&lt;p&gt;The widget redirects the browser to the hosted ceremony and returns it to the configured callback URL afterward.&lt;/p&gt;

&lt;p&gt;Why we separate browser keys from server secrets&lt;/p&gt;

&lt;p&gt;A browser integration needs some configuration that can be public. It should never need the application's secret API credential.&lt;/p&gt;

&lt;p&gt;Chirograph Verify therefore separates the publishable widget key from the tenant API key used by trusted servers.&lt;/p&gt;

&lt;p&gt;The widget key starts a flow. The secret tenant API key is used for server-to-server operations, including redeeming a completed widget result.&lt;/p&gt;

&lt;p&gt;That separation matters because code delivered to a browser is visible to the person running it. A secret placed in browser JavaScript is not a secret anymore.&lt;/p&gt;

&lt;p&gt;A callback is not the verification decision&lt;/p&gt;

&lt;p&gt;After the hosted ceremony, the browser receives flow_result and flow_id in the callback URL.&lt;/p&gt;

&lt;p&gt;The application must not treat possession of those parameters as proof of successful verification.&lt;/p&gt;

&lt;p&gt;Instead, its server sends the opaque result token to:&lt;/p&gt;

&lt;p&gt;POST /v1/widget/redeem&lt;/p&gt;

&lt;p&gt;The server authenticates the request with the tenant's secret X-API-Key and sends the result token in the request body.&lt;/p&gt;

&lt;p&gt;The token is tenant-bound, expires, and can be redeemed only once. The application receives the authoritative verification outcome from the server-side exchange.&lt;/p&gt;

&lt;p&gt;This is a small architectural distinction with an important security consequence: the browser transports the result reference, but the trusted server makes the decision.&lt;/p&gt;

&lt;p&gt;What we learned building the integration&lt;/p&gt;

&lt;p&gt;A few principles guided the implementation:&lt;/p&gt;

&lt;p&gt;Validate origins and redirect destinations. A public widget key should not authorize a flow from an arbitrary website.&lt;/p&gt;

&lt;p&gt;Separate credential scopes. Browser widget credentials and server API credentials have different responsibilities.&lt;/p&gt;

&lt;p&gt;Treat results as short-lived capabilities. Result tokens should not become reusable authentication assertions.&lt;/p&gt;

&lt;p&gt;Enforce tenant boundaries server-side. A client-supplied identifier should not override the tenant resolved from its authenticated credential.&lt;/p&gt;

&lt;p&gt;Make the integration understandable. Developers need to know where registration ends, where authentication begins, and where server-side verification occurs.&lt;/p&gt;

&lt;p&gt;These controls do not eliminate every form of abuse, and WebAuthn verification is not a universal replacement for application-specific authorization or fraud rules. It provides a cryptographic verification signal that applications can incorporate into their own decisions.&lt;/p&gt;

&lt;p&gt;Try the developer integration&lt;/p&gt;

&lt;p&gt;Chirograph Verify provides a hosted widget alongside a server-to-server verification API. The widget supports registration and authentication flows, while the API provides a server-verified result.&lt;/p&gt;

&lt;p&gt;Start here:&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://chirographverify.com/" rel="noopener noreferrer"&gt;https://chirographverify.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developer documentation: &lt;a href="https://app.chirographverify.com/developers" rel="noopener noreferrer"&gt;https://app.chirographverify.com/developers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Widget demonstration: &lt;a href="https://app.chirographverify.com/demo" rel="noopener noreferrer"&gt;https://app.chirographverify.com/demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're launching on October 30 and are looking forward to feedback from developers working on authentication, bot prevention, and application security.&lt;/p&gt;

&lt;p&gt;The most useful feedback for us is technical: what is clear, what is missing from the integration experience, and which edge cases we should investigate next.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>api</category>
      <category>auth</category>
    </item>
  </channel>
</rss>
