Project
cbpasskeys
WebAuthn passkeys for ColdBox on Lucee and Adobe ColdFusion: registration and sign-in with no password, using only JDK crypto and no third-party jars. It's the CFML sibling of bx-passkeys, with the same checks and the same API.
Two engines
Tested on Lucee 6.2 and Adobe ColdFusion 2023 on Java 21, against Derby and MySQL 8.4, with a test harness and server file per engine.
WireBox-native
Inject RelyingParty@cbpasskeys and call four methods from your handlers. Swap in your own credential store by naming its WireBox id in the settings.
JDK crypto only
ES256, Ed25519 and RS256 verified with java.security, called through public interfaces so Adobe CF needs no JVM flags on Java 17+.
Checked end to end
Single-use, expiring challenges; origin and rpId checks; signature counters that refuse cloned keys, with races between two sign-ins on one counter settled in the store.
Install
bashbox install cbpasskeys
Requires ColdBox 7+, Lucee 6+ or Adobe ColdFusion 2023+, and Java 17+. Create the credential table from sql/mssql.sql, sql/mysql.sql or sql/postgres.sql.
Settings
javascript// config/ColdBox.cfc
moduleSettings = {
cbpasskeys : {
rpId : "example.com",
rpName : "Example",
origins : [ "https://example.com" ],
store : { datasource: "app", table: "passkey_credentials" }
}
};
Routes and a handler
javascript// config/Router.cfc
post( "/passkeys/options" ).to( "passkeys.registrationOptions" );
post( "/passkeys" ).to( "passkeys.register" );
post( "/sign-in/passkey/options" ).to( "passkeys.signInOptions" );
post( "/sign-in/passkey" ).to( "passkeys.signIn" );
Signing in, with cbauth for the signed-in user — any sign-in works the same way:
javascriptproperty name="passkeys" inject="RelyingParty@cbpasskeys";
function signInOptions( event, rc, prc ) {
return event.renderData( type = "json", data = passkeys.signInOptions( session ) );
}
function signIn( event, rc, prc ) {
var result = {};
try {
result = passkeys.verify( session, event.getHTTPContent( json = true ) );
} catch ( "Passkeys.Invalid" e ) {
return event.renderData( type = "json", data = { "error": e.message }, statusCode = 401 );
}
sessionRotate();
auth().login( userService.retrieveUserById( result.userId ) );
return event.renderData( type = "json", data = { "signedIn": true } );
}
Registration follows the same shape with registrationOptions() and register(). test-harness/handlers/Passkeys.cfc is a complete handler with list and remove. Protect these routes from CSRF the way you protect your other POST routes.
Engine notes
- The options keep their JSON types, so Adobe CF's
serializeJSONkeeps key case and doesn't turn a numeric-looking name into a number. - Adobe CF's
?:treats0andfalseas missing. The module avoids it where either can occur, so a user id of0works. - To list or remove a user's passkeys, call
passkeys.getStore().listForUser( userId )and compare ids withcompare(), not==, which ignores case.
Tests
The test harness is a small ColdBox app in test-harness/. With a Derby 10.15+ jar in test-harness/lib/, run every engine or just one:
bashcd test-harness && box install && cd ..
bash scripts/test.sh # every engine
bash scripts/test.sh lucee@6 # one engine
bash scripts/coverage.sh # line coverage on Lucee 6