https://slides.com/elpete/itb2024-cbsecurity-passkeys

Competition

Who has the most Passkeys?

What this talk is

  • An intro to passkeys and passwordless authentication
  • Overview of the CBSecurity Passkeys module
  • Demo of integrating passkeys into a new ColdBox application

What this talk isn't

  • Introduction to CBSecurity
  • Deep Dive of all Passkeys options
  • Framework-agnostic (requires CBSecurity and ColdBox)

Authentication

  • Username and Password

  • Single-use Links (Email login)

  • Two Factor Authentication

  • Security Keys or Cards

Authentication

The Problems with Passwords

So what's the solution?

Password Rules?

Password Manager?

Friction

Phishing

How do Passkeys solve the problems with Passwords?

  • No passwords to remember
  • Unique passkey per site
  • Two-factor by default
  • Cannot be phished
  • Built on Public / Private key pairs

Benefits of Passkeys

Webauthn

Passkeys

Webauthn

Passkeys

==

Techincal Name

Marketing

Webauthn

Passkeys

!==

Adoption

In less than a year, passkeys have been used to authenticate people more than 1 billion times across over 400 million Google Accounts.

Paid Solutions

The "Ortus Solutions"

CBSecurity Passkeys

SUPER COOL LOGO

COMING SOON!

CBSecurity Passkeys

  • Builds on CBSecurity and CBAuth
  • Uses your Authentication Provider to log users in
  • Requires CBSecurity 3+ and CBAuth 6.1+
  • Built on top of webauthn-server-core by Yubico

CBSecurity Passkeys

  • All required Java dependencies in a single jar
  • Handlers for registering new Passkeys and authenticating using Passkeys
  • JavaScript resources for registering new Passkeys and authenticating using Passkeys
  • Required interface for storing and retrieving Passkeys from an external store (e.g. database)

Provides:

Installation

box install cbsecurity-passkeys

component {

  
    this.javaSettings = {
        loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
        loadColdFusionClassPath : true,
        reloadOnChange : false
    };
  
}

Load Java Dependencies

schema.create( "cbsecurity_passkeys", ( t ) => {
	t.increments( "id" );
	t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
	t.raw( "credentialId BLOB" );
	t.raw( "publicKey BLOB" );
	t.unsignedInteger( "signCount" );
	t.bit( "backupEligible" );
	t.bit( "backupState" );
	t.raw( "attestationObject BLOB" );
	t.text( "clientDataJSON" );
	t.datetime( "lastUsedTimestamp" ).nullable();
} );

(OPTIONAL)

Migrate Database Table

Configuration

Module Settings

moduleSettings = {
    "cbsecurity-passkeys" : {
    	// WireBox mapping to the component
    	// implementing the ICredentialRepository interface
		"credentialRepositoryMapping": "",
      
        // The identifier for the relying party (your application)
		"relyingPartyId" : CGI.SERVER_NAME,
      
        // The display name for the relying party (your application)
		"relyingPartyName" : controller.getSetting( "appName" ),
      
        // The allowed origins to send you Passkeys.
        // Defaults to the server name, if empty.
        // (Non-standard ports need to be specified explicitly)
		"allowedOrigins": []
	}
};

ICredentialRepository

ICredentialRepository

  • Defines the methods needed to register and authenticate Passkeys
  • An example implementation using Quick is available in the module.
resources/examples/Passkey.cfc

ICredentialRepository

public string function getUsernameForUser( required any user ) {}

public string function getDisplayNameForUser( required any user ) {}

public any function getUserHandleForUser( required any user ) {}

public array function getCredentialIdsForUsername( required string username ) {}

public any function getUserHandleForUsername( required string username ) {}

public string function getUsernameForUserHandle( required any userHandle ) {}

public struct function lookup( required any credentialId, required any userHandle ) {}

public array function lookupAll( required any credentialId ) {}

public void function storeCredentialForUser( /* ... */ ) {}

public void function updateCredentialForUser( /* ... */ ) {}

Usage

Server-side

Implement ICredentialRepository

Client-side

Detecting Passkey Support

<cfoutput>
<div>
	<div class="jumbotron mt-sm-5 p-4">
		<h1>Passkey Demo</h1>
	</div>
	<form id="passkey-form" action="#event.buildLink( "passkeys.new" )#" method="GET" style="display: none;">
		<button type="submit" class="btn btn-primary">Create Passkey</button>
	</form>
</div>
</cfoutput>

<script src="/modules_app/cbsecurity-passkeys/includes/js/passkeys.js"></script>
<script>
if (window.cbSecurity.passkeys.isSupported()) {
	document.getElementById("passkey-form").style.display = "block";
}
</script>

Registering Passkeys

<cfoutput>
	<div>
		<div class="jumbotron mt-sm-5 p-4">
			<h1>Creating a Passkey...</h1>
		</div>
	</div>
	<script src="/modules/cbsecurity-passkeys/includes/passkeys.js"></script>
	<script>
		window.cbSecurity.passkeys.register();
	</script>
</cfoutput>

Logging in with a Passkey

<cfoutput>
    <h3>Log In</h3>
    <small>or <a href="#event.buildLink( "registrations.new" )#">register for an account</a></small>
    <hr />
    <form id="loginForm" method="POST" action="#event.buildLink( "login" )#">
        <input type="hidden" name="_token" value="#csrfGenerateToken()#" />
        <div class="form-group">
            <label for="username">Email Address:</label>
            <input name="email" type="text" class="form-control" id="email" autocomplete="username webauthn" />
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
            <input name="password" type="password" class="form-control" id="password" autocomplete="current-password webauthn"/>
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-primary">Log In</button>
        </div>
    </form>
	<button id="passkeyLoginButton" type="button" class="btn btn-primary" style="display: none;">Log in with Passkey</button>
</cfoutput>

<script src="/modules_app/cbsecurity-passkeys/includes/passkeys.js"></script>
<script>
	if ( window.cbSecurity.passkeys.isSupported() ) {
		const passkeyLoginButton = document.getElementById( "passkeyLoginButton" );
		passkeyLoginButton.style.display = "block";
		passkeyLoginButton.addEventListener( "click", function() {
			window.cbSecurity.passkeys.login(document.forms.loginForm.email.value);
		});
	}
</script>

Demo

Competition Results

So who has the most Passkeys?

ITB 2024 — CBSecurity Passkeys

By Eric Peterson

ITB 2024 — CBSecurity Passkeys

  • 118