SlideShare a Scribd company logo
Passkeys and cbSecurity Led by Eric Peterson.pdf
Passkeys and cbSecurity Led by Eric Peterson.pdf
https://slides.com/elpete/itb2024-cbsecurity-passkeys
Competition
Who has the most Passkeys?
https://support.google.com/chrome/answer/13168025?hl=en&co=GENIE.Platform%3DDesktop&oco=1
https://support.apple.com/en-us/HT211146
https://blog.1password.com/how-save-manage-share-passkeys-1password/
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)
Passkeys and cbSecurity Led by Eric Peterson.pdf
Authentication
Username and Password
Single-use Links (Email login)
Two Factor Authentication
Security Keys or Cards
Authentication
The Problems
with Passwords
Source: The United States of P@ssw0rd$
Source: The United States of P@ssw0rd$
Source: The United States of P@ssw0rd$
Source: The United States of P@ssw0rd$ https://haveibeenpwned.com
Passkeys and cbSecurity Led by Eric Peterson.pdf
So what's the
solution?
Password Rules?
https://neal.fun/password-game/
Password Manager?
Source: Password Manager Industry Report and Market Outlook (2023-2024)
Source: Password Manager Industry Report and Market Outlook (2023-2024)
Source: (2019)
The United States of P@ssw0rd$
Friction
Phishing
https://www.youtube.com/embed/u9dBGWVwMMA?si=6nSS-
4IwJBUz8Syo&clip=UgkxMBp_WBB9p6-
WqubtH0RSvSVbNCkIYULY&clipt=EMi7DhiokBI&enablejsapi=1
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
https://webauthn.guide/
https://webauthn.io/
MDN: Web Authentication API
Passkeys
https://passkeys.dev/
https://fidoalliance.org/passkeys/
Webauthn Passkeys
==
Webauthn Passkeys
!==
Techincal Name Marketing
Adoption
“ In less than a year, passkeys have been used
to authenticate people more than 1 billion times
across over 400 million Google Accounts.
Passkeys, Cross-Account Protection and new ways we’re protecting your accounts
Paid Solutions
Auth0
Passage
Passwordless.dev
The "Ortus Solutions"
CBSecurity Passkeys
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 by
webauthn-server-core 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
};
}
1
2
3
4
5
6
7
8
9
10
Load Java
Dependencies
component {
this.javaSettings = {
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
loadColdFusionClassPath : true,
reloadOnChange : false
};
}
1
2
3
4
5
6
7
8
9
10
this.javaSettings = {
component {
1
2
3
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
loadColdFusionClassPath : true,
6
reloadOnChange : false
7
};
8
9
}
10
Load Java
Dependencies
component {
this.javaSettings = {
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
loadColdFusionClassPath : true,
reloadOnChange : false
};
}
1
2
3
4
5
6
7
8
9
10
this.javaSettings = {
component {
1
2
3
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
loadColdFusionClassPath : true,
6
reloadOnChange : false
7
};
8
9
}
10
loadColdFusionClassPath : true,
reloadOnChange : false
};
component {
1
2
3
this.javaSettings = {
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
6
7
8
9
}
10
Load Java
Dependencies
component {
this.javaSettings = {
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
loadColdFusionClassPath : true,
reloadOnChange : false
};
}
1
2
3
4
5
6
7
8
9
10
this.javaSettings = {
component {
1
2
3
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
loadColdFusionClassPath : true,
6
reloadOnChange : false
7
};
8
9
}
10
loadColdFusionClassPath : true,
reloadOnChange : false
};
component {
1
2
3
this.javaSettings = {
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
6
7
8
9
}
10 }
component {
1
2
3
this.javaSettings = {
4
loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ],
5
loadColdFusionClassPath : true,
6
reloadOnChange : false
7
};
8
9
10
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();
} );
1
2
3
4
5
6
7
8
9
10
11
12
(OPTIONAL)
Migrate Database Table
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();
} );
1
2
3
4
5
6
7
8
9
10
11
12
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
t.raw( "credentialId BLOB" );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
3
4
t.raw( "publicKey BLOB" );
5
t.unsignedInteger( "signCount" );
6
t.bit( "backupEligible" );
7
t.bit( "backupState" );
8
t.raw( "attestationObject BLOB" );
9
t.text( "clientDataJSON" );
10
t.datetime( "lastUsedTimestamp" ).nullable();
11
} );
12
(OPTIONAL)
Migrate Database Table
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();
} );
1
2
3
4
5
6
7
8
9
10
11
12
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
t.raw( "credentialId BLOB" );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
3
4
t.raw( "publicKey BLOB" );
5
t.unsignedInteger( "signCount" );
6
t.bit( "backupEligible" );
7
t.bit( "backupState" );
8
t.raw( "attestationObject BLOB" );
9
t.text( "clientDataJSON" );
10
t.datetime( "lastUsedTimestamp" ).nullable();
11
} );
12
t.unsignedInteger( "signCount" );
t.bit( "backupEligible" );
t.bit( "backupState" );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
3
t.raw( "credentialId BLOB" );
4
t.raw( "publicKey BLOB" );
5
6
7
8
t.raw( "attestationObject BLOB" );
9
t.text( "clientDataJSON" );
10
t.datetime( "lastUsedTimestamp" ).nullable();
11
} );
12
(OPTIONAL)
Migrate Database Table
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();
} );
1
2
3
4
5
6
7
8
9
10
11
12
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
t.raw( "credentialId BLOB" );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
3
4
t.raw( "publicKey BLOB" );
5
t.unsignedInteger( "signCount" );
6
t.bit( "backupEligible" );
7
t.bit( "backupState" );
8
t.raw( "attestationObject BLOB" );
9
t.text( "clientDataJSON" );
10
t.datetime( "lastUsedTimestamp" ).nullable();
11
} );
12
t.unsignedInteger( "signCount" );
t.bit( "backupEligible" );
t.bit( "backupState" );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
3
t.raw( "credentialId BLOB" );
4
t.raw( "publicKey BLOB" );
5
6
7
8
t.raw( "attestationObject BLOB" );
9
t.text( "clientDataJSON" );
10
t.datetime( "lastUsedTimestamp" ).nullable();
11
} );
12
t.text( "clientDataJSON" );
t.datetime( "lastUsedTimestamp" ).nullable();
} );
schema.create( "cbsecurity_passkeys", ( t ) => {
1
t.increments( "id" );
2
t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" );
3
t.raw( "credentialId BLOB" );
4
t.raw( "publicKey BLOB" );
5
t.unsignedInteger( "signCount" );
6
t.bit( "backupEligible" );
7
t.bit( "backupState" );
8
t.raw( "attestationObject BLOB" );
9
10
11
12
(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": []
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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": []
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// WireBox mapping to the component
// implementing the ICredentialRepository interface
"credentialRepositoryMapping": "",
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
3
4
5
6
// The identifier for the relying party (your application)
7
"relyingPartyId" : CGI.SERVER_NAME,
8
9
// The display name for the relying party (your application)
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
// The allowed origins to send you Passkeys.
13
// Defaults to the server name, if empty.
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
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": []
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// WireBox mapping to the component
// implementing the ICredentialRepository interface
"credentialRepositoryMapping": "",
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
3
4
5
6
// The identifier for the relying party (your application)
7
"relyingPartyId" : CGI.SERVER_NAME,
8
9
// The display name for the relying party (your application)
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
// The allowed origins to send you Passkeys.
13
// Defaults to the server name, if empty.
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
// The identifier for the relying party (your application)
"relyingPartyId" : CGI.SERVER_NAME,
// The display name for the relying party (your application)
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
// WireBox mapping to the component
3
// implementing the ICredentialRepository interface
4
"credentialRepositoryMapping": "",
5
6
7
8
9
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
// The allowed origins to send you Passkeys.
13
// Defaults to the server name, if empty.
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
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": []
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// WireBox mapping to the component
// implementing the ICredentialRepository interface
"credentialRepositoryMapping": "",
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
3
4
5
6
// The identifier for the relying party (your application)
7
"relyingPartyId" : CGI.SERVER_NAME,
8
9
// The display name for the relying party (your application)
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
// The allowed origins to send you Passkeys.
13
// Defaults to the server name, if empty.
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
// The identifier for the relying party (your application)
"relyingPartyId" : CGI.SERVER_NAME,
// The display name for the relying party (your application)
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
// WireBox mapping to the component
3
// implementing the ICredentialRepository interface
4
"credentialRepositoryMapping": "",
5
6
7
8
9
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
// The allowed origins to send you Passkeys.
13
// Defaults to the server name, if empty.
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
// The allowed origins to send you Passkeys.
// Defaults to the server name, if empty.
moduleSettings = {
1
"cbsecurity-passkeys" : {
2
// WireBox mapping to the component
3
// implementing the ICredentialRepository interface
4
"credentialRepositoryMapping": "",
5
6
// The identifier for the relying party (your application)
7
"relyingPartyId" : CGI.SERVER_NAME,
8
9
// The display name for the relying party (your application)
10
"relyingPartyName" : controller.getSetting( "appName" ),
11
12
13
14
// (Non-standard ports need to be specified explicitly)
15
"allowedOrigins": []
16
}
17
};
18
ICredentialRepository
ICredentialRepository
Defines the methods needed to
register and authenticate Passkeys
Example implementations using Quick
and cbORM are available in the
module.
resources/examples/quick/Passkey.cfc
resources/examples/cborm/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( /* ... */ ) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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=
<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 type="module">
if (await window.cbSecurity.passkeys.isSupported()) {
document.getElementById("passkey-form").style.display = "block";
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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>
1
2
3
4
5
6
7
8
9
10
11
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"/>
</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 type="module">
if ( await 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Autocompleting Passkeys
<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" />
</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 type="module">
window.cbSecurity.passkeys.autocomplete();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Demo
Passkeys and cbSecurity Led by Eric Peterson.pdf
ForgeBox
Live Coding
Competition
Results
So who has the most Passkeys?
Passkeys and cbSecurity Led by Eric Peterson.pdf

More Related Content

More from Ortus Solutions, Corp (20)

BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdfBoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
Ortus Solutions, Corp
 
What's-New-with-BoxLang-Brad Wood.pptx.pdf
What's-New-with-BoxLang-Brad Wood.pptx.pdfWhat's-New-with-BoxLang-Brad Wood.pptx.pdf
What's-New-with-BoxLang-Brad Wood.pptx.pdf
Ortus Solutions, Corp
 
Getting Started with BoxLang - CFCamp 2025.pdf
Getting Started with BoxLang - CFCamp 2025.pdfGetting Started with BoxLang - CFCamp 2025.pdf
Getting Started with BoxLang - CFCamp 2025.pdf
Ortus Solutions, Corp
 
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdfCFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
Ortus Solutions, Corp
 
What's New with BoxLang Led by Brad Wood.pdf
What's New with BoxLang Led by Brad Wood.pdfWhat's New with BoxLang Led by Brad Wood.pdf
What's New with BoxLang Led by Brad Wood.pdf
Ortus Solutions, Corp
 
Vector Databases and the BoxLangCFML Developer.pdf
Vector Databases and the BoxLangCFML Developer.pdfVector Databases and the BoxLangCFML Developer.pdf
Vector Databases and the BoxLangCFML Developer.pdf
Ortus Solutions, Corp
 
Using cbSSO in a ColdBox App Led by Jacob Beers.pdf
Using cbSSO in a ColdBox App Led by Jacob Beers.pdfUsing cbSSO in a ColdBox App Led by Jacob Beers.pdf
Using cbSSO in a ColdBox App Led by Jacob Beers.pdf
Ortus Solutions, Corp
 
Use JSON to Slash Your Database Performance.pdf
Use JSON to Slash Your Database Performance.pdfUse JSON to Slash Your Database Performance.pdf
Use JSON to Slash Your Database Performance.pdf
Ortus Solutions, Corp
 
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Portable CI wGitLab and Github led by Gavin Pickin.pdfPortable CI wGitLab and Github led by Gavin Pickin.pdf
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Ortus Solutions, Corp
 
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdfTame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Ortus Solutions, Corp
 
Supercharging CommandBox with Let's Encrypt.pdf
Supercharging CommandBox with Let's Encrypt.pdfSupercharging CommandBox with Let's Encrypt.pdf
Supercharging CommandBox with Let's Encrypt.pdf
Ortus Solutions, Corp
 
Spice up your site with cool animations using GSAP..pdf
Spice up your site with cool animations using GSAP..pdfSpice up your site with cool animations using GSAP..pdf
Spice up your site with cool animations using GSAP..pdf
Ortus Solutions, Corp
 
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdfLegacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Ortus Solutions, Corp
 
Integrating the OpenAI API in Your Coldfusion Apps.pdf
Integrating the OpenAI API in Your Coldfusion Apps.pdfIntegrating the OpenAI API in Your Coldfusion Apps.pdf
Integrating the OpenAI API in Your Coldfusion Apps.pdf
Ortus Solutions, Corp
 
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdfHidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Ortus Solutions, Corp
 
Geting-started with BoxLang Led By Raymon Camden.pdf
Geting-started with BoxLang Led By Raymon Camden.pdfGeting-started with BoxLang Led By Raymon Camden.pdf
Geting-started with BoxLang Led By Raymon Camden.pdf
Ortus Solutions, Corp
 
From Zero to CRUD with ORM - Led by Annette Liskey.pdf
From Zero to CRUD with ORM - Led by Annette Liskey.pdfFrom Zero to CRUD with ORM - Led by Annette Liskey.pdf
From Zero to CRUD with ORM - Led by Annette Liskey.pdf
Ortus Solutions, Corp
 
Customize your Runtime Creating your first BoxLang Module.pdf
Customize your Runtime Creating your first BoxLang Module.pdfCustomize your Runtime Creating your first BoxLang Module.pdf
Customize your Runtime Creating your first BoxLang Module.pdf
Ortus Solutions, Corp
 
CommandBox WebSockets - and SocketBox.pdf
CommandBox WebSockets - and SocketBox.pdfCommandBox WebSockets - and SocketBox.pdf
CommandBox WebSockets - and SocketBox.pdf
Ortus Solutions, Corp
 
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdfCMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
Ortus Solutions, Corp
 
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdfBoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
BoxLang-Dynamic-AWS-Lambda by Luis Majano.pdf
Ortus Solutions, Corp
 
What's-New-with-BoxLang-Brad Wood.pptx.pdf
What's-New-with-BoxLang-Brad Wood.pptx.pdfWhat's-New-with-BoxLang-Brad Wood.pptx.pdf
What's-New-with-BoxLang-Brad Wood.pptx.pdf
Ortus Solutions, Corp
 
Getting Started with BoxLang - CFCamp 2025.pdf
Getting Started with BoxLang - CFCamp 2025.pdfGetting Started with BoxLang - CFCamp 2025.pdf
Getting Started with BoxLang - CFCamp 2025.pdf
Ortus Solutions, Corp
 
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdfCFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
CFCamp2025 - Keynote Day 1 led by Luis Majano.pdf
Ortus Solutions, Corp
 
What's New with BoxLang Led by Brad Wood.pdf
What's New with BoxLang Led by Brad Wood.pdfWhat's New with BoxLang Led by Brad Wood.pdf
What's New with BoxLang Led by Brad Wood.pdf
Ortus Solutions, Corp
 
Vector Databases and the BoxLangCFML Developer.pdf
Vector Databases and the BoxLangCFML Developer.pdfVector Databases and the BoxLangCFML Developer.pdf
Vector Databases and the BoxLangCFML Developer.pdf
Ortus Solutions, Corp
 
Using cbSSO in a ColdBox App Led by Jacob Beers.pdf
Using cbSSO in a ColdBox App Led by Jacob Beers.pdfUsing cbSSO in a ColdBox App Led by Jacob Beers.pdf
Using cbSSO in a ColdBox App Led by Jacob Beers.pdf
Ortus Solutions, Corp
 
Use JSON to Slash Your Database Performance.pdf
Use JSON to Slash Your Database Performance.pdfUse JSON to Slash Your Database Performance.pdf
Use JSON to Slash Your Database Performance.pdf
Ortus Solutions, Corp
 
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Portable CI wGitLab and Github led by Gavin Pickin.pdfPortable CI wGitLab and Github led by Gavin Pickin.pdf
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Ortus Solutions, Corp
 
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdfTame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Tame the Mesh An intro to cross-platform tracing and troubleshooting.pdf
Ortus Solutions, Corp
 
Supercharging CommandBox with Let's Encrypt.pdf
Supercharging CommandBox with Let's Encrypt.pdfSupercharging CommandBox with Let's Encrypt.pdf
Supercharging CommandBox with Let's Encrypt.pdf
Ortus Solutions, Corp
 
Spice up your site with cool animations using GSAP..pdf
Spice up your site with cool animations using GSAP..pdfSpice up your site with cool animations using GSAP..pdf
Spice up your site with cool animations using GSAP..pdf
Ortus Solutions, Corp
 
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdfLegacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Legacy Code Nightmares , Hellscapes, and Lessons Learned.pdf
Ortus Solutions, Corp
 
Integrating the OpenAI API in Your Coldfusion Apps.pdf
Integrating the OpenAI API in Your Coldfusion Apps.pdfIntegrating the OpenAI API in Your Coldfusion Apps.pdf
Integrating the OpenAI API in Your Coldfusion Apps.pdf
Ortus Solutions, Corp
 
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdfHidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Hidden Gems in FusionReactor for BoxLang, ACF, and Lucee Users.pdf
Ortus Solutions, Corp
 
Geting-started with BoxLang Led By Raymon Camden.pdf
Geting-started with BoxLang Led By Raymon Camden.pdfGeting-started with BoxLang Led By Raymon Camden.pdf
Geting-started with BoxLang Led By Raymon Camden.pdf
Ortus Solutions, Corp
 
From Zero to CRUD with ORM - Led by Annette Liskey.pdf
From Zero to CRUD with ORM - Led by Annette Liskey.pdfFrom Zero to CRUD with ORM - Led by Annette Liskey.pdf
From Zero to CRUD with ORM - Led by Annette Liskey.pdf
Ortus Solutions, Corp
 
Customize your Runtime Creating your first BoxLang Module.pdf
Customize your Runtime Creating your first BoxLang Module.pdfCustomize your Runtime Creating your first BoxLang Module.pdf
Customize your Runtime Creating your first BoxLang Module.pdf
Ortus Solutions, Corp
 
CommandBox WebSockets - and SocketBox.pdf
CommandBox WebSockets - and SocketBox.pdfCommandBox WebSockets - and SocketBox.pdf
CommandBox WebSockets - and SocketBox.pdf
Ortus Solutions, Corp
 
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdfCMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
CMS Switch-a-Rooney! Converting Your MuraMasa Sites to ContentBox.pdf
Ortus Solutions, Corp
 

Recently uploaded (20)

grade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptxgrade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptx
manikumar465287
 
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATIONAI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
miso_uam
 
Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan
OnePlan Solutions
 
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjaraswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
muhammadalikhanalikh1
 
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdfSecure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Northwind Technologies
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdfHow to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
victordsane
 
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
gauravvmanchandaa200
 
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternativesAI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Oliveira2024 - Combining GPT and Weak Supervision.pdf
Oliveira2024 - Combining GPT and Weak Supervision.pdfOliveira2024 - Combining GPT and Weak Supervision.pdf
Oliveira2024 - Combining GPT and Weak Supervision.pdf
GiliardGodoi1
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
Marketing And Sales Software Services.pptx
Marketing And Sales Software Services.pptxMarketing And Sales Software Services.pptx
Marketing And Sales Software Services.pptx
julia smits
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
Agentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptxAgentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptx
MOSIUOA WESI
 
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Prachi Desai
 
Software Risk and Quality management.pptx
Software Risk and Quality management.pptxSoftware Risk and Quality management.pptx
Software Risk and Quality management.pptx
HassanBangash9
 
Micro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Micro-Metrics Every Performance Engineer Should Validate Before Sign-OffMicro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Micro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Tier1 app
 
grade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptxgrade 9 ai project cycle Artificial intelligence.pptx
grade 9 ai project cycle Artificial intelligence.pptx
manikumar465287
 
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATIONAI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
miso_uam
 
Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan Delivering More with Less: AI Driven Resource Management with OnePlan
Delivering More with Less: AI Driven Resource Management with OnePlan
OnePlan Solutions
 
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjaraswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
muhammadalikhanalikh1
 
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdfSecure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Northwind Technologies
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdfHow to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
victordsane
 
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
Risk Management in Software Projects: Identifying, Analyzing, and Controlling...
gauravvmanchandaa200
 
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternativesAI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Oliveira2024 - Combining GPT and Weak Supervision.pdf
Oliveira2024 - Combining GPT and Weak Supervision.pdfOliveira2024 - Combining GPT and Weak Supervision.pdf
Oliveira2024 - Combining GPT and Weak Supervision.pdf
GiliardGodoi1
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire Unlocking the Future of Tech Talent with AI-Powered Hiring Solution...
GirikHire
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
Marketing And Sales Software Services.pptx
Marketing And Sales Software Services.pptxMarketing And Sales Software Services.pptx
Marketing And Sales Software Services.pptx
julia smits
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
Agentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptxAgentic AI Desgin Principles in five slides.pptx
Agentic AI Desgin Principles in five slides.pptx
MOSIUOA WESI
 
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Why Indonesia’s $12.63B Alt-Lending Boom Needs Loan Servicing Automation & Re...
Prachi Desai
 
Software Risk and Quality management.pptx
Software Risk and Quality management.pptxSoftware Risk and Quality management.pptx
Software Risk and Quality management.pptx
HassanBangash9
 
Micro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Micro-Metrics Every Performance Engineer Should Validate Before Sign-OffMicro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Micro-Metrics Every Performance Engineer Should Validate Before Sign-Off
Tier1 app
 
Ad

Passkeys and cbSecurity Led by Eric Peterson.pdf

  • 4. Competition Who has the most Passkeys? https://support.google.com/chrome/answer/13168025?hl=en&co=GENIE.Platform%3DDesktop&oco=1 https://support.apple.com/en-us/HT211146 https://blog.1password.com/how-save-manage-share-passkeys-1password/
  • 5. 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
  • 6. What this talk isn't Introduction to CBSecurity Deep Dive of all Passkeys options Framework-agnostic (requires CBSecurity and ColdBox)
  • 9. Username and Password Single-use Links (Email login) Two Factor Authentication Security Keys or Cards Authentication
  • 11. Source: The United States of P@ssw0rd$
  • 12. Source: The United States of P@ssw0rd$
  • 13. Source: The United States of P@ssw0rd$
  • 14. Source: The United States of P@ssw0rd$ https://haveibeenpwned.com
  • 20. Source: Password Manager Industry Report and Market Outlook (2023-2024)
  • 21. Source: Password Manager Industry Report and Market Outlook (2023-2024) Source: (2019) The United States of P@ssw0rd$
  • 25. How do Passkeys solve the problems with Passwords?
  • 26. No passwords to remember Unique passkey per site Two-factor by default Cannot be phished Built on Public / Private key pairs Benefits of Passkeys
  • 30. Adoption “ In less than a year, passkeys have been used to authenticate people more than 1 billion times across over 400 million Google Accounts. Passkeys, Cross-Account Protection and new ways we’re protecting your accounts
  • 34. 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 by webauthn-server-core Yubico
  • 35. 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:
  • 38. component { this.javaSettings = { loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], loadColdFusionClassPath : true, reloadOnChange : false }; } 1 2 3 4 5 6 7 8 9 10 Load Java Dependencies
  • 39. component { this.javaSettings = { loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], loadColdFusionClassPath : true, reloadOnChange : false }; } 1 2 3 4 5 6 7 8 9 10 this.javaSettings = { component { 1 2 3 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 loadColdFusionClassPath : true, 6 reloadOnChange : false 7 }; 8 9 } 10 Load Java Dependencies
  • 40. component { this.javaSettings = { loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], loadColdFusionClassPath : true, reloadOnChange : false }; } 1 2 3 4 5 6 7 8 9 10 this.javaSettings = { component { 1 2 3 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 loadColdFusionClassPath : true, 6 reloadOnChange : false 7 }; 8 9 } 10 loadColdFusionClassPath : true, reloadOnChange : false }; component { 1 2 3 this.javaSettings = { 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 6 7 8 9 } 10 Load Java Dependencies
  • 41. component { this.javaSettings = { loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], loadColdFusionClassPath : true, reloadOnChange : false }; } 1 2 3 4 5 6 7 8 9 10 this.javaSettings = { component { 1 2 3 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 loadColdFusionClassPath : true, 6 reloadOnChange : false 7 }; 8 9 } 10 loadColdFusionClassPath : true, reloadOnChange : false }; component { 1 2 3 this.javaSettings = { 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 6 7 8 9 } 10 } component { 1 2 3 this.javaSettings = { 4 loadPaths : [ expandPath( "./modules/cbsecurity-passkeys/lib" ) ], 5 loadColdFusionClassPath : true, 6 reloadOnChange : false 7 }; 8 9 10 Load Java Dependencies
  • 42. 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(); } ); 1 2 3 4 5 6 7 8 9 10 11 12 (OPTIONAL) Migrate Database Table
  • 43. 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(); } ); 1 2 3 4 5 6 7 8 9 10 11 12 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); t.raw( "credentialId BLOB" ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 3 4 t.raw( "publicKey BLOB" ); 5 t.unsignedInteger( "signCount" ); 6 t.bit( "backupEligible" ); 7 t.bit( "backupState" ); 8 t.raw( "attestationObject BLOB" ); 9 t.text( "clientDataJSON" ); 10 t.datetime( "lastUsedTimestamp" ).nullable(); 11 } ); 12 (OPTIONAL) Migrate Database Table
  • 44. 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(); } ); 1 2 3 4 5 6 7 8 9 10 11 12 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); t.raw( "credentialId BLOB" ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 3 4 t.raw( "publicKey BLOB" ); 5 t.unsignedInteger( "signCount" ); 6 t.bit( "backupEligible" ); 7 t.bit( "backupState" ); 8 t.raw( "attestationObject BLOB" ); 9 t.text( "clientDataJSON" ); 10 t.datetime( "lastUsedTimestamp" ).nullable(); 11 } ); 12 t.unsignedInteger( "signCount" ); t.bit( "backupEligible" ); t.bit( "backupState" ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); 3 t.raw( "credentialId BLOB" ); 4 t.raw( "publicKey BLOB" ); 5 6 7 8 t.raw( "attestationObject BLOB" ); 9 t.text( "clientDataJSON" ); 10 t.datetime( "lastUsedTimestamp" ).nullable(); 11 } ); 12 (OPTIONAL) Migrate Database Table
  • 45. 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(); } ); 1 2 3 4 5 6 7 8 9 10 11 12 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); t.raw( "credentialId BLOB" ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 3 4 t.raw( "publicKey BLOB" ); 5 t.unsignedInteger( "signCount" ); 6 t.bit( "backupEligible" ); 7 t.bit( "backupState" ); 8 t.raw( "attestationObject BLOB" ); 9 t.text( "clientDataJSON" ); 10 t.datetime( "lastUsedTimestamp" ).nullable(); 11 } ); 12 t.unsignedInteger( "signCount" ); t.bit( "backupEligible" ); t.bit( "backupState" ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); 3 t.raw( "credentialId BLOB" ); 4 t.raw( "publicKey BLOB" ); 5 6 7 8 t.raw( "attestationObject BLOB" ); 9 t.text( "clientDataJSON" ); 10 t.datetime( "lastUsedTimestamp" ).nullable(); 11 } ); 12 t.text( "clientDataJSON" ); t.datetime( "lastUsedTimestamp" ).nullable(); } ); schema.create( "cbsecurity_passkeys", ( t ) => { 1 t.increments( "id" ); 2 t.unsignedInteger( "userId" ).references( "id" ).onTable( "users" ); 3 t.raw( "credentialId BLOB" ); 4 t.raw( "publicKey BLOB" ); 5 t.unsignedInteger( "signCount" ); 6 t.bit( "backupEligible" ); 7 t.bit( "backupState" ); 8 t.raw( "attestationObject BLOB" ); 9 10 11 12 (OPTIONAL) Migrate Database Table
  • 48. 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": [] } }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  • 49. 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": [] } }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // WireBox mapping to the component // implementing the ICredentialRepository interface "credentialRepositoryMapping": "", moduleSettings = { 1 "cbsecurity-passkeys" : { 2 3 4 5 6 // The identifier for the relying party (your application) 7 "relyingPartyId" : CGI.SERVER_NAME, 8 9 // The display name for the relying party (your application) 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 // The allowed origins to send you Passkeys. 13 // Defaults to the server name, if empty. 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18
  • 50. 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": [] } }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // WireBox mapping to the component // implementing the ICredentialRepository interface "credentialRepositoryMapping": "", moduleSettings = { 1 "cbsecurity-passkeys" : { 2 3 4 5 6 // The identifier for the relying party (your application) 7 "relyingPartyId" : CGI.SERVER_NAME, 8 9 // The display name for the relying party (your application) 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 // The allowed origins to send you Passkeys. 13 // Defaults to the server name, if empty. 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18 // The identifier for the relying party (your application) "relyingPartyId" : CGI.SERVER_NAME, // The display name for the relying party (your application) moduleSettings = { 1 "cbsecurity-passkeys" : { 2 // WireBox mapping to the component 3 // implementing the ICredentialRepository interface 4 "credentialRepositoryMapping": "", 5 6 7 8 9 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 // The allowed origins to send you Passkeys. 13 // Defaults to the server name, if empty. 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18
  • 51. 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": [] } }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // WireBox mapping to the component // implementing the ICredentialRepository interface "credentialRepositoryMapping": "", moduleSettings = { 1 "cbsecurity-passkeys" : { 2 3 4 5 6 // The identifier for the relying party (your application) 7 "relyingPartyId" : CGI.SERVER_NAME, 8 9 // The display name for the relying party (your application) 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 // The allowed origins to send you Passkeys. 13 // Defaults to the server name, if empty. 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18 // The identifier for the relying party (your application) "relyingPartyId" : CGI.SERVER_NAME, // The display name for the relying party (your application) moduleSettings = { 1 "cbsecurity-passkeys" : { 2 // WireBox mapping to the component 3 // implementing the ICredentialRepository interface 4 "credentialRepositoryMapping": "", 5 6 7 8 9 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 // The allowed origins to send you Passkeys. 13 // Defaults to the server name, if empty. 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18 // The allowed origins to send you Passkeys. // Defaults to the server name, if empty. moduleSettings = { 1 "cbsecurity-passkeys" : { 2 // WireBox mapping to the component 3 // implementing the ICredentialRepository interface 4 "credentialRepositoryMapping": "", 5 6 // The identifier for the relying party (your application) 7 "relyingPartyId" : CGI.SERVER_NAME, 8 9 // The display name for the relying party (your application) 10 "relyingPartyName" : controller.getSetting( "appName" ), 11 12 13 14 // (Non-standard ports need to be specified explicitly) 15 "allowedOrigins": [] 16 } 17 }; 18
  • 53. ICredentialRepository Defines the methods needed to register and authenticate Passkeys Example implementations using Quick and cbORM are available in the module. resources/examples/quick/Passkey.cfc resources/examples/cborm/Passkey.cfc
  • 54. 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( /* ... */ ) {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  • 55. Usage
  • 59. 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= <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 type="module"> if (await window.cbSecurity.passkeys.isSupported()) { document.getElementById("passkey-form").style.display = "block"; } </script> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  • 60. 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> 1 2 3 4 5 6 7 8 9 10 11
  • 61. 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"/> </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 type="module"> if ( await 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> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  • 62. Autocompleting Passkeys <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" /> </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 type="module"> window.cbSecurity.passkeys.autocomplete(); </script> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  • 63. Demo
  • 67. Competition Results So who has the most Passkeys?