DEV Community

Hossein Hezami
Hossein Hezami

Posted on

Beyond Passkeys and OTP: Why I Built a Protocol for the Authentication Gaps We Keep Ignoring

Passkeys are winning the browser login race.

And they should.

WebAuthn and FIDO2 solved a huge part of the authentication problem: phishing-resistant, browser-native, public-key authentication.

But the more I worked on real systems, the more I noticed a gap.

Not every authentication problem happens inside a browser.

Some happen on a point-of-sale terminal.

Some happen offline.

Some happen inside a legacy OTP form that cannot be rewritten overnight.

Some happen during a high-value transaction where the user must visually confirm an amount.

Some happen under coercion.

And some require approval from more than one device.

That is the gap that led me to PulseProof Sentinel Protocol, or PPS.

PPS is an experimental open authentication protocol that replaces shared-secret TOTP-style codes with signed, time-bound, asymmetric proofs called Pulses.

You can explore the project here:

https://pps-protocol.github.io


The Problem With TOTP

TOTP is everywhere.

It is simple, widely supported, and easy to deploy.

But it has a structural weakness:

code = HMAC(shared_secret, time)
Enter fullscreen mode Exit fullscreen mode

The server knows the secret.

If the server database is breached, the attacker can generate valid codes.

That is not ideal.

Public-key authentication solves this. The server should not need to know the secret that proves identity.

That is the core idea behind PPS:

Pulse = Sign(private_key, time + rp + nonce + counter + context + policy)
Enter fullscreen mode Exit fullscreen mode

The user’s device holds the private key.

The server stores only the public key.

The proof is signed, time-bound, replay-protected, and bound to the relying party.


PPS Is Not a WebAuthn Replacement

This is important.

PPS is not trying to replace WebAuthn.

If you are building browser-based passwordless login, WebAuthn is usually the right answer.

PPS is designed to complement WebAuthn.

It targets the operational gaps where WebAuthn is unavailable, impractical, or insufficient:

  • offline authentication
  • legacy OTP input forms
  • QR-code and deep-link authentication
  • constrained hardware terminals
  • point-of-sale and IoT devices
  • human-visible transaction confirmation
  • silent duress signaling
  • offline multi-device threshold approval

In other words:

WebAuthn is for passkeys.

PPS is for the authentication flows that still live outside the passkey world.


What Is a Pulse?

A Pulse is a signed authentication statement.

It can be bound to several things:

  • a relying-party identifier
  • a time epoch
  • a server nonce
  • a monotonic counter
  • an expiration window
  • a security policy
  • a transaction hash
  • environmental context

This makes PPS flexible.

A Pulse can be used for simple login.

It can also be used for transaction signing, offline terminal authentication, or multi-device approval.

The protocol uses deterministic CBOR encoding and Ed25519 signatures.

The mandatory profile is:

PPS-ED25519-CBOR30
Enter fullscreen mode Exit fullscreen mode

You can read the full specification here:

https://pps-protocol.github.io/pulseproof-sentinel/SPEC.html

The documentation is available here:

https://pps-protocol.github.io/pulseproof-sentinel/docs/

The protocol is also submitted as an IETF Internet-Draft:

https://datatracker.ietf.org/doc/draft-hezami-pulseproof-sentinel/


The Developer Story: PHP and Laravel

A protocol is only useful if people can implement it.

That is why the first sample implementation is available in PHP.

The core package is framework-agnostic:

https://github.com/pps-protocol/pps-php

You can install it with Composer:

composer require pps-protocol/pps-php
Enter fullscreen mode Exit fullscreen mode

Package website:

https://pps-protocol.github.io/pps-php/

Documentation:

https://pps-protocol.github.io/pps-php/docs/

Packagist:

https://packagist.org/packages/pps-protocol/pps-php

For Laravel applications, there is a dedicated integration package:

https://github.com/pps-protocol/laravel-pps

Install it with:

composer require pps-protocol/laravel-pps
Enter fullscreen mode Exit fullscreen mode

Then run:

php artisan pps:install
Enter fullscreen mode Exit fullscreen mode

The Laravel package provides:

  • service provider
  • publishable configuration
  • API routes
  • HTTP controller
  • facade
  • Artisan commands
  • storage backend integration
  • transaction challenge endpoints
  • silent duress handling support

Laravel package website:

https://pps-protocol.github.io/laravel-pps/

Packagist:

https://packagist.org/packages/pps-protocol/laravel-pps


A Small Example

At the protocol level, the idea is simple.

The client signs a Pulse:

use Pps\Client\AuthenticatorClient;

$client = new AuthenticatorClient($clientState);

$pulse = $client
    ->rpId('example.com')
    ->nonce($nonceFromServer)
    ->createPulse();
Enter fullscreen mode Exit fullscreen mode

The server verifies it using the stored public key.

No shared OTP secret is required.

In Laravel, the package exposes endpoints such as:

GET  /pps/health
GET  /pps/challenge
POST /pps/register
POST /pps/verify
POST /pps/transaction/challenge
POST /pps/transaction/verify
Enter fullscreen mode Exit fullscreen mode

This makes it easy to integrate PPS into an existing authentication or transaction-signing flow.


Transaction Signing Is Where PPS Becomes Interesting

One of my favorite PPS features is transaction signing.

Imagine a withdrawal:

Amount: 2,500,067 IRR
Trust Code: 49371867
Enter fullscreen mode Exit fullscreen mode

The last two digits of the Trust Code come from the transaction amount.

This creates a human-visible connection between the cryptographic proof and the transaction being approved.

The user can confirm:

Yes, the code I see matches the amount I requested.

This is especially useful for:

  • banking
  • fintech
  • crypto withdrawals
  • admin approvals
  • high-value payments
  • corporate treasury flows

PPS can bind the transaction hash, amount, session, and policy into the signed Pulse.

That is much stronger than sending a generic OTP code.


Silent Duress: Authentication Under Coercion

Another important area is duress.

What happens if someone is forced to authenticate?

Traditional OTP systems do not have a good answer.

PPS introduces an optional duress mechanism.

The user can authenticate with a hidden duress key.

The resulting Pulse is cryptographically valid, but the server recognizes it as a Honey-Pulse.

The server can return a normal-looking success response while internally:

  • restricting account capabilities
  • blocking high-value operations
  • delaying settlement
  • triggering a silent alert
  • preserving audit logs

This is sensitive territory, and implementation matters a lot.

But for high-risk accounts, it is a powerful concept.


Offline and Multi-Device Flows

PPS also supports offline authentication.

Because a Pulse is signed and time-bound, it can be generated without a live network round-trip.

Offline mode requires stricter rate limiting and careful operational design, but it is useful for:

  • terminals
  • IoT devices
  • field equipment
  • unstable networks
  • emergency access

PPS also supports offline multi-device threshold approval.

For example:

2-of-2: phone + watch
2-of-3: phone + laptop + hardware key
Enter fullscreen mode Exit fullscreen mode

This is useful when a single device should not be enough to approve a sensitive action.


The Ecosystem

The PPS project is organized under a GitHub organization:

https://github.com/pps-protocol

The main repositories are:

PulseProof Sentinel Protocol

The specification repository.

https://github.com/pps-protocol/pulseproof-sentinel

Website:

https://pps-protocol.github.io/pulseproof-sentinel/

Specification:

https://pps-protocol.github.io/pulseproof-sentinel/SPEC.html

Documentation:

https://pps-protocol.github.io/pulseproof-sentinel/docs/

pps-php

The core PHP implementation.

https://github.com/pps-protocol/pps-php

Website:

https://pps-protocol.github.io/pps-php/

Documentation:

https://pps-protocol.github.io/pps-php/docs/

Packagist:

https://packagist.org/packages/pps-protocol/pps-php

laravel-pps

The Laravel integration package.

https://github.com/pps-protocol/laravel-pps

Website:

https://pps-protocol.github.io/laravel-pps/

Packagist:

https://packagist.org/packages/pps-protocol/laravel-pps


Why Explore PPS?

PPS is experimental.

It has not been independently audited.

It is not ready to replace your production authentication system tomorrow.

But if you care about authentication design, it is worth exploring.

It is especially interesting if you are working on:

  • fintech authentication
  • crypto exchange security
  • offline device login
  • POS terminals
  • IoT authentication
  • transaction signing
  • high-value approval workflows
  • duress-aware accounts
  • multi-device authorization
  • legacy OTP migration

PPS asks a simple question:

Why should the server ever store a shared OTP secret when it can verify a signature instead?


Start Here

The best place to start is the project website:

https://pps-protocol.github.io

Then read the specification:

https://pps-protocol.github.io/pulseproof-sentinel/SPEC.html

If you are a PHP developer, try the core package:

composer require pps-protocol/pps-php
Enter fullscreen mode Exit fullscreen mode

If you are using Laravel, install the Laravel integration:

composer require pps-protocol/laravel-pps
Enter fullscreen mode Exit fullscreen mode

And if you are interested in protocol design, review the IETF Internet-Draft:

https://datatracker.ietf.org/doc/draft-hezami-pulseproof-sentinel/

PPS is early, experimental, and open for feedback.

If you believe authentication should move beyond shared secrets, give it a look.

Top comments (0)