Download the PHP package svss-labs/email-verification-api without Composer
On this page you can find all versions of the php package svss-labs/email-verification-api. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download svss-labs/email-verification-api
More information about svss-labs/email-verification-api
Files in svss-labs/email-verification-api
Package email-verification-api
Short Description Verify Email Verification Protocol (EVT+KB) tokens issued by email providers like Gmail
License MIT
Informations about the package email-verification-api
📧 ✅ Email Verification API for PHP
A PHP library to verify Email Verification Tokens (EVT) issued through the Email Verification Protocol — the proposed web standard that lets browsers verify a user's email address with their email provider directly, without one-time passwords (OTPs) or magic links.
The user picks their email address in a form, the browser obtains a cryptographically signed token from their email provider (e.g. Gmail), and your server verifies it with this package. No email is ever sent.
Requirements
- PHP 8.3+
- Composer
- PHP extensions:
curl,gmp,openssl,sodium(standard on most hosts;sodiumandgmpare required for Ed25519 signature verification and safe IP arithmetic)
Installation
How the protocol works
- Your site renders a form with a hidden field carrying a per-render nonce.
- The user enters (or autocomplete-selects) their email address.
- The browser discovers the issuer via a DNS TXT record (
_email-verification.<email-domain>), confirms the user's session with the provider, and obtains a signed token. - On form submit, the browser attaches the token (an SD-JWT with a Key Binding JWT) to the hidden field.
- Your server verifies the full package: parsing → expected values (email/nonce/audience/age) → DNS delegation → issuer signature (metadata + JWKS) → key binding.
Full protocol details: Chrome developer article.
Usage
Form markup
The browser only attaches a token when the form follows this exact contract:
Laravel example
The token is bound to the nonce that was present when the user's email field changed, which can happen on a page load different from the submit. Store a small pool of recent nonces server-side (here in the session) so the submit handler can match the one the token was bound to.
routes/web.php
app/Http/Controllers/EmailVerificationController.php
resources/views/signup.blade.php
Nonce matching:
pull('evp_nonce')works when the submit happens on the same page load. For the field-change-on-early-load case, keep the pool fromshow()(e.g. in the session) and match the nonce claim embedded in the token's KB-JWT, exactly likeexample/live-demo.phpdoes.
VerifyEmailTokenInput
| Parameter | Type | Default | Description |
|---|---|---|---|
token |
string |
— | Full EVT+KB presentation as submitted by the browser |
nonce |
string |
— | The nonce your site generated for this form render |
email |
string |
— | The email address being verified (compared case-insensitively) |
audience |
string |
— | Your HTTP(S) origin, e.g. https://your-domain.com |
maxTokenAgeSeconds |
int |
300 |
Maximum accepted token age |
clockToleranceSeconds |
int |
60 |
Allowed clock skew |
fetch |
?callable |
null |
Override HTTP fetching (tests). null = real HTTPS via Guzzle |
resolveTxt |
?callable |
null |
Override DNS TXT resolution (tests). null = real dns_get_record |
resolveHost |
?callable |
null |
Override A/AAAA resolution (tests). null = real DNS |
now |
?callable |
null |
Clock override returning Unix milliseconds (tests) |
Result
verifyEmailToken() returns a Result object:
- Success:
$result->ok === true,$result->valueis aVerifiedEmailwithemail,issuer,audience,issuedAt(['evt' => ..., 'keyBinding' => ...]) and the full decodedclaims. - Failure:
$result->ok === false,$result->erroris aVerificationErrorwithstage(input,parse,expected-values,dns,issuer,key-binding),code,messageand optionalcause.
Common error codes:
| Code | Meaning |
|---|---|
TOKEN_MALFORMED |
Token is not a parseable EVT+KB presentation |
EMAIL_MISMATCH / NONCE_MISMATCH / AUDIENCE_MISMATCH |
Expected values don't match token claims |
TOKEN_EXPIRED / TOKEN_NOT_YET_VALID |
iat outside the accepted window |
DNS_DELEGATION_MISSING / DNS_DELEGATION_AMBIGUOUS |
No (or multiple) iss= TXT records at _email-verification.<domain> |
ISSUER_MISMATCH |
DNS-delegated issuer differs from the token issuer |
METADATA_FETCH_FAILED / JWKS_FETCH_FAILED |
Issuer endpoints unreachable |
ALGORITHM_UNSUPPORTED |
EVT signed with an algorithm the issuer doesn't advertise |
EVT_SIGNATURE_INVALID |
Signature doesn't verify against the issuer's JWKS |
KB_SIGNATURE_INVALID |
Key Binding JWT doesn't verify against the cnf.jwk ephemeral key |
SD_HASH_MISMATCH |
sd_hash doesn't match the presented token |
Security notes
- Fail-closed: any internal error results in a verification failure, never a false positive.
- SSRF-safe network calls: issuer endpoints must be HTTPS, on publicly-routable hosts; resolved IPs are validated against global ranges and pinned via cURL
CURLOPT_RESOLVE; redirects are disabled. - Constant-time comparisons (
hash_equals) for signature-relevant hash checks; strict algorithm/key-type compatibility mapping prevents algorithm-confusion attacks. - Nonces should be single-use, random (e.g.
bin2hex(random_bytes(32))) and bound to the user's session.
Testing your integration
1. Offline demo (no browser needed)
Uses test fixtures and mocked network callbacks to walk through a successful verification.
2. Live browser demo (real Google/Gmail verification)
Open http://localhost:8080, enter your Gmail address, and submit — the page verifies a real token against Google's production issuer (real DNS, real JWKS).
⚠️ Browser requirements — enable the Chrome flag
The browser side of this protocol is experimental and disabled by default. If no token is attached to your form:
- Open
chrome://flags/#email-verification-protocoland set Email Verification Protocol to Enabled
Also required:
- Desktop Chrome 150+ (desktop-only up to Chrome 152)
- Signed in to your email provider in the same browser profile (for Gmail: signed into your Google Account)
- The origin trial token active on your origin for production use — sign up at the Chrome origin trials dashboard and either serve it as a
<meta http-equiv="origin-trial" content="...">tag (the live demo auto-injects one from theEVP_OT_TOKENenv var orexample/origin-trial-token.txt) or as a response header - Since Chrome 152, typing or pasting the email triggers verification on field change; earlier versions require selecting from the autocomplete dropdown
- Since Chrome 152, while verification runs Chrome shows a progress indicator (spinner → checkmark) inside the email input field; the form may submit before it finishes, so don't rely on it completing before submit
You can sanity-check your browser setup independently with Google's verifier demo and mock email provider.
Troubleshooting
| Symptom | Fix |
|---|---|
| No token in the POST at all | Enable the chrome://flags entry above; check you're signed in to the provider |
NONCE_MISMATCH |
The token is bound to the nonce present when the email was entered. Keep one nonce per form render and store issued nonces server-side (the live demo keeps a small pool) until submit |
METADATA_INVALID |
Older releases required the JWKS host to equal the issuer host — Gmail hosts its JWKS on a separate domain; update to a fixed version |
DNS_DELEGATION_MISSING |
The email domain has no _email-verification TXT record — only participating providers (e.g. Gmail) can issue tokens |
Verification works on rowan.fyi but not your origin |
Origin trial token missing/expired for your origin |
Development
Compatibility
- Gmail participates in the origin trial; any
@gmail.comaddress works when signed in. - Breaking changes are expected while the protocol is experimental. The most notable one — Chrome 153 switching issuer requests to HTTP Message Signatures — only affects email providers that issue tokens. This library only verifies tokens, so no code changes are required on our side; you can keep this package updated to pick up any refinements to SD-JWT parsing, key binding, or expected-value checks that the protocol evolves.
- Watch the WICG repo and the evp-announce mailing list for ongoing protocol changes.
License
The MIT License (MIT). Please see License File for more information.
All versions of email-verification-api with dependencies
ext-curl Version *
ext-gmp Version *
ext-openssl Version *
ext-sodium Version *
guzzlehttp/guzzle Version ^7.0 || ^8.0
k2gl/sd-jwt Version ^1.0
web-token/jwt-framework Version ^4.1