Download the PHP package ovenlab/cakephp-passkey without Composer
On this page you can find all versions of the php package ovenlab/cakephp-passkey. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download ovenlab/cakephp-passkey
More information about ovenlab/cakephp-passkey
Files in ovenlab/cakephp-passkey
Package cakephp-passkey
Short Description Passwordless and second-factor authentication for CakePHP 5 using WebAuthn passkeys: registration/assertion ceremonies, credential storage, an Authentication adapter, a browser helper and vanilla JS.
License MIT
Homepage https://github.com/ovenlab/cakephp-passkey
Informations about the package cakephp-passkey
CakePHP Passkey
Passwordless and second-factor authentication for CakePHP 5 using WebAuthn passkeys.
Built on top of web-auth/webauthn-lib
5.x, this plugin gives you the full registration and login ceremonies,
credential storage in your database, a browser client with zero build step, and
a drop-in adapter for cakephp/authentication.
- Registration —
POST /passkey/register/options+/register/verify - Login —
POST /passkey/login/options+/login/verify(usernameless / discoverable credentials supported) - Management —
GET /passkey/credentials,DELETE /passkey/credentials/{id} - Authentication adapter —
PasskeyAuthenticator+PasskeyIdentifier - Browser client —
webroot/js/passkey.js, vanilla, no dependencies
Table of contents
- How it works — the ceremonies
- Requirements
- Installation
- Configuration
- Architecture
- HTTP API
- Browser client
- Integration guides
- How credentials are stored
- Security notes
- Running the tests
- License
How it works — the ceremonies
A passkey is a public/private key pair bound to your site's domain (the Relying Party) and held by an authenticator — the device's secure hardware (Touch ID, Windows Hello, a security key) or a synced provider (iCloud Keychain, Google Password Manager). The private key never leaves the authenticator; your server only ever stores the public key. Authentication is a signed challenge, so there is no shared secret to phish, leak, or reuse.
WebAuthn defines two ceremonies. Each is a two-step round trip: the server issues options containing a fresh random challenge, the browser drives the authenticator, and the server verifies the signed result. The plugin stores the challenge in the session between the two steps.
Registration (attestation) — enrol a new passkey
The user is already logged in and wants to add a passkey to their account.
Maps to RegistrationService::creationOptions() /
RegistrationService::verify().
Login (assertion) — sign in with a passkey
Maps to AssertionService::requestOptions() / AssertionService::verify().
For the full picture — origins, the signature counter, base64url, usernameless flows and the security properties of each step — read docs/ceremonies.md.
Requirements
- PHP 8.2+ (the WebAuthn serializer requires
symfony/serializer7) - CakePHP 5.0+
ext-json(andext-sodium/ext-mbstringrecommended)- HTTPS — WebAuthn only works in a secure context;
http://localhostis exempt for development
Installation
Load the plugin:
Run the migration to create the passkey_credentials table:
Configuration
Copy config/passkey.example.php to your app's config/passkey.php and adjust
it — the plugin loads that file automatically during bootstrap() when it
exists. Never commit secrets — use env():
| Key | Default | Purpose |
|---|---|---|
rp.name |
CakePHP Passkey |
Human-readable Relying Party name shown by the authenticator. |
rp.id |
request host | Registrable domain the credential is bound to. Pin it in production. |
origin |
request scheme+host | Expected origin the ceremony must come from. Pin it in production. |
userVerification |
preferred |
Require a PIN/biometric gesture (required for true passwordless). |
residentKey |
preferred |
Ask the authenticator to store a discoverable credential. |
timeout |
60000 |
Ceremony timeout in milliseconds. |
usernameless |
true |
Allow login to start without a username (discoverable credential). |
mode |
passwordless |
Documents intent; second_factor = passkey on top of a password. |
userModel |
Users |
Table the controller loads to resolve the user id → identity on login. |
userField |
id |
Column matched against the credential's stored user_id. |
displayNameField |
username |
Field used as the WebAuthn user display name during registration. |
When rp.id / origin are omitted they are derived from the incoming request.
That is convenient in development, but pinning them in production is strongly
recommended so the Relying Party identity cannot drift with the Host header.
Architecture
The plugin is a thin, request-agnostic core with a controller and adapters on top, so you can consume it at whichever layer fits your app.
| Layer | Class | Responsibility |
|---|---|---|
| WebAuthn wrapper | Service\WebAuthnService |
(De)serializer, ceremony validators, RP entity, base64url helpers. |
| Registration | Service\RegistrationService |
Build creation options; verify attestation; persist the credential. |
| Login | Service\AssertionService |
Build request options; verify assertion; bump the counter. |
| HTTP | Controller\PasskeyController |
JSON endpoints; session challenge; optional setIdentity on login. |
| Storage | Model\Table\PasskeyCredentialsTable + entity |
passkey_credentials rows (the serialized CredentialRecord). |
| Auth middleware | Authenticator\PasskeyAuthenticator + Identifier\PasskeyIdentifier |
Verify + resolve the user inside the authentication middleware. |
| View / client | View\Helper\PasskeyHelper + webroot/js/passkey.js |
Emit the script; run the ceremonies in the browser. |
The services take the resolved origin and rpId as explicit arguments and
never read the request, which keeps them easy to unit-test and reusable outside
a controller.
HTTP API
All endpoints exchange JSON. The register and management endpoints require an
authenticated user (they act on the current identity); the login endpoints are
public. The challenge issued by an /options call is stored in the session and
consumed — once — by the matching /verify call.
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/passkey/register/options |
yes | Issue creation options for the user |
POST |
/passkey/register/verify |
yes | Verify attestation, store the credential |
POST |
/passkey/login/options |
no | Issue request options |
POST |
/passkey/login/verify |
no | Verify assertion, establish the identity |
GET |
/passkey/credentials |
yes | List the current user's credentials |
DELETE |
/passkey/credentials/{id} |
yes | Delete one of the user's credentials |
/register/verify accepts an optional name alongside the credential so users
can label a passkey ("My laptop"). /login/verify returns the resolved
userId; when the Authentication component is loaded it also calls
setIdentity() so the user is logged in (see the integration guides).
Example — the verify request body the browser client sends:
Browser client
Include the client with the helper and drive the ceremonies. The script exposes
a global window.Passkey:
Passkey.register(opts) and Passkey.login(opts) handle the base64url encoding
and call the plugin endpoints. Options:
| Option | Type | Notes |
|---|---|---|
csrfToken |
string | Sent as the X-CSRF-Token header. Required when CSRF protection is on. |
name |
string | register() only — a human label stored with the passkey ("My laptop"). |
baseUrl |
string | Endpoint base path, default /passkey. Set it if you remounted the plugin. |
CSRF: CakePHP's
CsrfProtectionMiddlewaresets an httponly cookie by default, so JavaScript cannot read the token fromdocument.cookie. Pass it explicitly from a template attribute as shown above, rather than relying on the cookie.
Passkey.isSupported() returns whether the browser exposes the WebAuthn API.
Cancellations (the user dismisses the prompt) surface as a rejected promise —
catch it and treat it as "no passkey used", not an error.
Integration guides
The login ceremony needs to turn a verified assertion into a logged-in session. There are two ways to wire that up:
- docs/integration-authentication.md —
plug into
cakephp/authenticationdirectly. Covers both the controller-drivensetIdentitypath (the default) and the middlewarePasskeyAuthenticator+PasskeyIdentifieradapter, and when to choose each. - docs/integration-cakedc-users.md — add "sign in with a passkey" to an app already using CakeDC/Users: the config flag, the login template button, the permission entries, and the double-load gotcha to avoid.
How credentials are stored
Each row in passkey_credentials keeps the serialized WebAuthn
CredentialRecord (the source of truth for verification) alongside denormalized
columns for lookups: the base64url credential_id, the owning user_id, the
signature sign_count, transports, aaguid, an optional human name, and
last_used. On login the record is deserialized, verified, and its counter and
last_used are written back.
user_id is a string so it can hold any application key (integer id, UUID, …).
credential_id is uniquely indexed; user_id is indexed for the per-user
lookups used by the management endpoints and re-registration exclusion list.
Security notes
- Serve everything over HTTPS; pin
rp.idandoriginin production so the Relying Party identity cannot be influenced by request headers. - The signature counter is verified and persisted on every assertion, which lets the library detect cloned authenticators.
- Registration and management endpoints act on the current identity only — a user can never enumerate or delete another user's credentials.
- The challenge is single-use: it is deleted from the session on
/verifywhether verification succeeds or fails. - Keep
config/passkey.phpout of version control if it carries anything environment-specific; the example usesenv()throughout.
Running the tests
License
MIT. See LICENSE.
All versions of cakephp-passkey with dependencies
cakephp/cakephp Version ^5.0
web-auth/webauthn-lib Version ^5.3
ext-json Version *