Implement Passkey registration
- Create Identity credentials - Add management UI and registration flow
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
<% @page_title = "Enter your email" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half">
|
||||
<div class="panel panel--centered flex flex-column gap-half" data-controller="passkey">
|
||||
<h1 class="txt-x-large font-weight-black margin-block-end">Get into Fizzy</h1>
|
||||
|
||||
<%= form_with url: session_path, class: "flex flex-column gap-half txt-medium" do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address…" %>
|
||||
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username webauthn", placeholder: "Enter your email address…" %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<tr style="view-transition-name: <%= dom_id(credential) %>">
|
||||
<td><strong><%= credential.name.presence || "Passkey" %></strong></td>
|
||||
<td><%= local_datetime_tag credential.created_at, style: :datetime %></td>
|
||||
<td>
|
||||
<%= button_to user_credential_path(@user, credential), method: :delete,
|
||||
class: "btn txt-negative btn--circle txt-x-small borderless fill-transparent",
|
||||
data: { turbo_confirm: "Are you sure you want to remove this passkey?" } do %>
|
||||
<%= icon_tag "trash" %>
|
||||
<span class="for-screen-reader">Remove this passkey</span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,34 @@
|
||||
<% @page_title = "Passkeys" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<div class="header__actions header__actions--start">
|
||||
<%= back_link_to "My profile", user_path(@user), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<section class="panel panel--wide shadow center">
|
||||
<% if @credentials.any? %>
|
||||
<p class="margin-none-block-start">Passkeys let you sign in without email codes. They use your device's biometrics, PIN, or security key.</p>
|
||||
<table class="access_tokens_table margin-block-end-double max-width txt-small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Passkey</th>
|
||||
<th>Created</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= render partial: "users/credentials/credential", collection: @credentials %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% else %>
|
||||
<p class="margin-none-block-start">Passkeys let you sign in without email codes. They use your device's biometrics, PIN, or security key.</p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to new_user_credential_path(@user), class: "btn btn--link" do %>
|
||||
<%= icon_tag "add" %>
|
||||
<span>Add a passkey</span>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -0,0 +1,32 @@
|
||||
<% @page_title = "Add a passkey" %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<div class="header__actions header__actions--start">
|
||||
<%= back_link_to "Passkeys", user_credentials_path(@user), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
|
||||
</div>
|
||||
|
||||
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
|
||||
<% end %>
|
||||
|
||||
<article class="panel panel--wide shadow center txt-align-start">
|
||||
<%= form_with url: user_credentials_path(@user),
|
||||
data: { controller: "credential", credential_public_key_value: @creation_options.as_json },
|
||||
html: { class: "flex flex-column gap" } do |form| %>
|
||||
<p>A passkey lets you sign in using your device's biometrics, PIN, or security key — no email code needed.</p>
|
||||
|
||||
<div class="flex flex-column gap-half">
|
||||
<strong><label for="credential_name">Passkey name</label></strong>
|
||||
<input type="text" id="credential_name" name="credential[name]" required autofocus class="input" placeholder="e.g. MacBook Pro, iPhone">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="credential[response][client_data_json]" data-credential-target="clientDataJSON">
|
||||
<input type="hidden" name="credential[response][attestation_object]" data-credential-target="attestationObject">
|
||||
|
||||
<button type="button" data-action="credential#create" class="btn btn--link center txt-medium">
|
||||
<%= icon_tag "add" %>
|
||||
<span>Register passkey</span>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Cancel and go back", user_credentials_path(@user), hidden: true %>
|
||||
</article>
|
||||
@@ -5,6 +5,11 @@
|
||||
<div class="settings__panel panel shadow txt-align-center">
|
||||
<div class="flex flex-column gap position-relative">
|
||||
<% if Current.user == @user %>
|
||||
<%= link_to user_credentials_path(@user), class: "user-edit-link btn", style: "inset: 0 auto auto 0", data: { controller: "tooltip" } do %>
|
||||
🔑
|
||||
<span class="for-screen-reader">Manage passkeys</span>
|
||||
<% end %>
|
||||
|
||||
<%= link_to edit_user_path(@user), class: "user-edit-link btn", data: { controller: "tooltip" } do %>
|
||||
<%= icon_tag "pencil" %>
|
||||
<span class="for-screen-reader">Edit profile</span>
|
||||
|
||||
Reference in New Issue
Block a user