Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions content/collections/tags/user-delete_passkey_form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
id: d0ed4ac0-536a-47a1-965b-202b52baddb2
blueprint: tag
title: 'User:Delete_Passkey_Form'
description: 'Creates a form to delete a passkey'
intro: 'As the tag name suggests, it allows you to delete a passkey.'
parameters:
-
name: id
type: string
description: 'The passkey ID to delete. Required.'
-
name: redirect
type: string
description: Where the user should be taken after successfully deleting a passkey.
-
name: HTML Attributes
type:
description: 'Set HTML attributes as if you were in an HTML element. For example, `class="delete-form"`.'
related_entries:
- 38323438-4719-4a7b-ba5a-8abfe0d7dfc0
- 7a958307-4cdb-47f3-a689-0c7de57e3ff7
- 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a
---
## Overview

The `user:delete_passkey_form` tag renders a form to delete a passkey.

### Example

The tag is typically used inside a [`{{ user:passkeys }}`](/tags/user-passkeys) loop:

::tabs

::tab antlers
```antlers
{{ user:passkeys as="passkeys" }}
{{ passkeys }}
<div>
{{ name }}

{{ user:delete_passkey_form :id="id" }}
<button type="submit">Delete</button>
{{ /user:delete_passkey_form }}
</div>
{{ /passkeys }}
{{ /user:passkeys }}
```
::tab blade
```blade
<s:user:passkeys as="passkeys">
@foreach ($passkeys as $passkey)
<div>
{{ $passkey['name'] }}

<s:user:delete_passkey_form :id="$passkey['id']">
<button type="submit">Delete</button>
</s:user:delete_passkey_form>
</div>
@endforeach
</s:user:passkeys>
```
::
50 changes: 50 additions & 0 deletions content/collections/tags/user-login_form.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ variables:
name: success
type: string
description: A success message.
-
name: passkey_options_url
type: string
description: URL to fetch WebAuthn assertion options for passkey login.
-
name: passkey_verify_url
type: string
description: URL to verify passkey login.
id: 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a
---
## Overview
Expand Down Expand Up @@ -101,3 +109,45 @@ The tag will render the opening and closing `<form>` HTML elements for you. The
</s:user:login_form>
```
::

## Passkeys

You can add passkey authentication to your login form using Statamic's frontend JavaScript helpers.

1. First, include the helpers script on your page:
```html
<script src="/vendor/statamic/frontend/js/helpers.js"></script>
```

2. Use the provided variables to add a passkey login option:
```antlers
{{ user:login_form }}
<input type="email" name="email" value="{{ old:email }}" />
<input type="password" name="password" value="{{ old:password }}" />
<button type="submit">Log in with Password</button>

<button type="button" id="passkey-login">Login with Passkey</button> {{# [tl! focus:start] #}}

<script>
Statamic.$passkeys.configure({
optionsUrl: '{{ passkey_options_url }}',
verifyUrl: '{{ passkey_verify_url }}',
onSuccess: (data) => window.location = data.redirect || '/',
onError: (error) => alert(error.message)
});

document.getElementById('passkey-login').addEventListener('click', () => {
Statamic.$passkeys.authenticate();
});

// Enable browser autofill for passkeys
Statamic.$passkeys.initAutofill();
</script> {{# [tl! focus:end] #}}
{{ /user:login_form }}
```
3. Add `autocomplete="username webauthn"` to your email input for browser autofill to work.

For more information on managing passkeys on the frontend, see the following docs:
- [`{{ user:passkeys }}`](/tags/user-passkeys)
- [`{{ user:passkey_form }}`](/tags/user-passkey_form)
- [`{{ user:delete_passkey_form }}`](/tags/user-delete_passkey_form)
81 changes: 81 additions & 0 deletions content/collections/tags/user-passkey_form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
id: 7a958307-4cdb-47f3-a689-0c7de57e3ff7
blueprint: tag
title: 'User:Passkey_Form'
description: 'Creates a passkey registration form'
intro: 'Allows authenticated users to set up passkeys'
variables:
-
name: passkey_option_url
type: string
description: 'URL to fetch WebAuthn attestation options for creating a new passkey.'
-
name: passkey_verify_url
type: string
description: 'URL to store the new passkey after registration.'
related_entries:
- 38323438-4719-4a7b-ba5a-8abfe0d7dfc0
- d0ed4ac0-536a-47a1-965b-202b52baddb2
- 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a
---
## Overview

The `user:passkey_form` tag provides the necessary URLs to set up passkeys for authenticated users.

### JavaScript helpers

You'll need to include the frontend helpers script on your page:

```html
<script src="/vendor/statamic/frontend/js/helpers.js"></script>
```

### Example

::tabs

::tab antlers
```antlers
{{ user:passkey_form }}
<input type="text" id="passkey-name" placeholder="Passkey name (e.g., My Laptop)">
<button type="button" id="create-passkey">Create Passkey</button>

<script>
document.getElementById('create-passkey').addEventListener('click', () => {
const name = document.getElementById('passkey-name').value || 'My Passkey';

Statamic.$passkeys.register({
optionsUrl: '{{ passkey_option_url }}',
verifyUrl: '{{ passkey_verify_url }}',
name: name,
onSuccess: () => location.reload(),
onError: (error) => alert(error.message),
// csrfToken (optional)
});
});
</script>
{{ /user:passkey_form }}
```
::tab blade
```blade
<s:user:passkey_form>
<input type="text" id="passkey-name" placeholder="Passkey name (e.g., My Laptop)">
<button type="button" id="create-passkey">Create Passkey</button>

<script>
document.getElementById('create-passkey').addEventListener('click', () => {
const name = document.getElementById('passkey-name').value || 'My Passkey';

Statamic.$passkeys.register({
optionsUrl: '{{ $passkey_option_url }}',
verifyUrl: '{{ $passkey_verify_url }}',
name: name,
onSuccess: () => location.reload(),
onError: (error) => alert(error.message),
// csrfToken (optional)
});
});
</script>
</s:user:passkey_form>
```
::
97 changes: 97 additions & 0 deletions content/collections/tags/user-passkeys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
id: 38323438-4719-4a7b-ba5a-8abfe0d7dfc0
blueprint: tag
title: 'User:Passkeys'
description: 'Lists the current user''s passkeys'
intro: 'Loop through the authenticated user''s registered passkeys.'
variables:
-
name: id
type: string
description: 'The passkey identifier.'
-
name: name
type: string
description: 'The user-defined passkey name.'
-
name: last_login
type: Carbon
description: 'The last time the passkey was used for login, or null if never used.'
related_entries:
- 7432f1cb-7418-4d54-8e65-51b1ae3bcb3a
- 7a958307-4cdb-47f3-a689-0c7de57e3ff7
- d0ed4ac0-536a-47a1-965b-202b52baddb2
---
## Overview

The `user:passkeys` tag loops through the user's passkeys. Useful for building a passkey management page where users can view and delete their passkeys.

### Example

::tabs

::tab antlers
```antlers
{{ user:passkeys as="passkeys" }}
{{ if passkeys }}
<h3>Your Passkeys</h3>
<ul>
{{ passkeys }}
<li>
<strong>{{ name }}</strong>
{{ if last_login }}
<span>Last used: {{ last_login format="M j, Y g:i A" }}</span>
{{ else }}
<span>Never used</span>
{{ /if }}

{{ user:delete_passkey_form :id="id" }}
<button type="submit">Delete</button>
{{ /user:delete_passkey_form }}
</li>
{{ /passkeys }}
</ul>
{{ else }}
<p>You haven't set up any passkeys yet.</p>
{{ /if }}
{{ /user:passkeys }}
```
::tab blade
```blade
<s:user:passkeys as="passkeys">
@if ($passkeys)
<h3>Your Passkeys</h3>
<ul>
@foreach ($passkeys as $passkey)
<li>
<strong>{{ $passkey['name'] }}</strong>
@if ($passkey['last_login'])
<span>Last used: {{ $passkey['last_login']->format('M j, Y g:i A') }}</span>
@else
<span>Never used</span>
@endif

<s:user:delete_passkey_form :id="$passkey['id']">
<button type="submit">Delete</button>
</s:user:delete_passkey_form>
</li>
@endforeach
</ul>
@else
<p>You haven't set up any passkeys yet.</p>
@endif
</s:user:passkeys>
```
::

## Aliasing

You can use the `as` parameter to alias the passkeys into a variable, which allows you to use `{{ if passkeys }}` to check if there are any passkeys before rendering.

```antlers
{{ user:passkeys as="passkeys" }}
{{ if passkeys }}
{{# Render passkeys list #}}
{{ /if }}
{{ /user:passkeys }}
```