Skip to content

Instantly share code, notes, and snippets.

@rawilk
Last active September 29, 2022 15:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rawilk/93ef860c06cb6d534258c18aeb472bb4 to your computer and use it in GitHub Desktop.
Save rawilk/93ef860c06cb6d534258c18aeb472bb4 to your computer and use it in GitHub Desktop.
WebAuthn Key Registration - Laravel WebAuthn Package
{{-- note: you will need to implement your own "modal" component for this to work properly. see laravel/jetstream for modal example --}}
<div>
@if (\Rawilk\Webauthn\Facades\Webauthn::webauthnEnabled())
@webauthnScripts
<button type="button" wire:click="showAddKey">
Add security key
</button>
<!-- register modal -->
<div x-data="{
keyName: @entangle('newKeyName').defer,
showInstructions: @entangle('showInstructions').defer,
showName: false,
webAuthn: new WebAuthn,
webAuthnSupported: true,
errorMessages: {{ Js::from($this->errorMessages) }},
errorMessage: null,
notifyCallback() {
return (errorName, defaultMessage) => {
this.errorMessage = this.errorMessages[errorName] || defaultMessage;
};
},
keyData: null,
publicKey: @entangle('publicKey').defer,
init() {
this.webAuthnSupported = this.webAuthn.supported();
if (! this.webAuthnSupported) {
this.errorMessage = this.errorMessages[this.webAuthn.notSupportedType()];
}
// Register a callback when errors happen so we can notify the user.
this.webAuthn.registerNotifyCallback(this.notifyCallback());
},
// Prompt user to register their security key.
register() {
if (! this.webAuthnSupported) {
return;
}
this.errorMessage = null;
this.keyData = null;
this.showInstructions = false;
this.showName = false;
// This is the most important part!
this.webAuthn.register(JSON.parse(this.publicKey), (publicKeyCredential, deviceName) => {
// WebAuthn script will try to guess the device's name.
this.keyName = deviceName;
this.keyData = publicKeyCredential;
this.showName = true;
setTimeout(() => this.$refs.name.focus(), 250);
});
},
// Send the key data to the server.
sendKey() {
if (! this.keyData) {
return;
}
@this.registerKey(this.keyData);
},
}">
<x-dialog-modal wire:model.defer="showAddKey">
<x-slot name="title">Register Security Key</x-slot>
<x-slot name="content">
<div x-show="showInstructions && webAuthnSupported">
<p>Some instructions on how to use a security key here...</p>
</div>
<div x-show="! showInstructions || ! webAuthnSupported">
<!-- we are waiting for user to interact with their authenticator -->
<div x-show="! showName && ! errorMessage && webAuthnSupported">
<p>Interact with your authenticator...</p>
</div>
<!-- an error ocurred (user probably canceled) -->
<div x-show="errorMessage">
<p x-html="errorMessage"></p>
<button type="button" x-on:click="register">Retry</button>
</div>
<!-- registration successful, now name key -->
<div x-show="showName">
<label for="newKeyName">Name your key</label>
<input
x-model="keyName"
name="newKeyName"
id="newKeyName"
required
x-ref="name"
x-on:keydown.enter.prevent.stop="keyName && sendKey"
>
</div>
</div>
</x-slot>
<x-slot name="footer">
<!-- button to open webauthn prompt -->
<button type="button" x-show="showInstructions" x-on:click="register">Next</button>
<!-- button to complete registration -->
<button type="button"
x-show="! showInstructions && showName"
x-on:click="sendKey"
x-bind:disabled="! keyName"
>
Register key
</button>
</x-slot>
</x-dialog-modal>
</div>
@endif
</div>
<?php
use Illuminate\Support\Facades\Lang;
use Livewire\Component;
use Rawilk\Webauthn\Actions\PrepareKeyCreationData;
class RegisterWebauthnKey extends Component
{
/**
* This will be the public key credential options required for the front-end.
*/
public ?string $publicKey = null;
/**
* Indicates whether or not to show the key registration modal.
*/
public bool $showAddKey = false;
/**
* The name for the newly registered security key.
*/
public string $newKeyName = '';
/**
* Indicates security key instructions should be shown in the modal.
*/
public bool $showInstructions = true;
public function getErrorMessagesProperty(): array
{
return [
...Lang::get('webauthn::alerts.auth') ?? [],
'InvalidStateError' => __('webauthn::alerts.login_not_allowed_error'),
'notSupported' => __('webauthn::alerts.browser_not_supported'),
'notSecured' => __('webauthn::alerts.browser_not_secure'),
];
}
/**
* Show the register security key modal dialog.
*/
public function showAddKey(): void
{
$this->resetErrorBag();
$this->showInstructions = true;
$this->showAddKey = true;
}
public function render()
{
if (! $this->publicKey) {
$this->publicKey = json_encode(app(PrepareKeyCreationData::class)(auth()->user()));
}
return view('livewire.register-webauthn-key');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment