Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sign in with a passkey through form autofill #129

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
6 changes: 5 additions & 1 deletion app/javascript/controllers/new_session_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { MDCTextField } from '@material/textfield';
export default class extends Controller {
static targets = ["usernameField"]

connect() {
Credential.autofill();
}

create(event) {
var [data, status, xhr] = event.detail;
console.log(data);
var credentialOptions = data;
Credential.get(credentialOptions);
Credential.get(credentialOptions, "optional");
}

error(event) {
Expand Down
48 changes: 42 additions & 6 deletions app/javascript/credential.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as WebAuthnJSON from "@github/webauthn-json"

import { showMessage } from "messenger";

function getCSRFToken() {
Expand All @@ -10,8 +11,8 @@ function getCSRFToken() {
}
}

function callback(url, body) {
fetch(url, {
function _fetch(url, body) {
return fetch(url, {
method: "POST",
body: JSON.stringify(body),
headers: {
Expand All @@ -20,7 +21,11 @@ function callback(url, body) {
"X-CSRF-Token": getCSRFToken()
},
credentials: 'same-origin'
}).then(function(response) {
})
}

function callback(url, body) {
_fetch(url, body).then(function(response) {
if (response.ok) {
window.location.replace("/")
} else if (response.status < 500) {
Expand All @@ -31,6 +36,37 @@ function callback(url, body) {
});
}

function getCredentialOptions() {
return _fetch("/session/options").then(function(response) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a 404 error because we are trying to fetch /session/options which does not exist. It seems that the idea behind this endpoint was to get a challenge from the server right? So that might mean that we will need some kind of Conditional UI endpoint that sets a challenge and return PublicKeyCredentialRequestOptions

if (response.ok) {
return response.json();
}
if (response.status < 500) {
response.text().then(showMessage);
} else {
showMessage("Sorry, something wrong happened.");
}
});
}

async function autofill() {
if (WebAuthnJSON.supported &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
const cma = await PublicKeyCredential.isConditionalMediationAvailable();
if (cma) {
var credentialOptions = await getCredentialOptions();
get(credentialOptions, "conditional");
}
} catch (e) {
console.error(e);
if (e.name !== "NotAllowedError") {
alert(e.message);
}
}
}
}

function create(callbackUrl, credentialOptions) {
WebAuthnJSON.create({ "publicKey": credentialOptions }).then(function(credential) {
callback(callbackUrl, credential);
Expand All @@ -41,8 +77,8 @@ function create(callbackUrl, credentialOptions) {
console.log("Creating new public key credential...");
}

function get(credentialOptions) {
WebAuthnJSON.get({ "publicKey": credentialOptions }).then(function(credential) {
function get(credentialOptions, mediationOption) {
WebAuthnJSON.get({ "publicKey": credentialOptions, "mediation": mediationOption }).then(function(credential) {
callback("/session/callback", credential);
}).catch(function(error) {
showMessage(error);
Expand All @@ -51,4 +87,4 @@ function get(credentialOptions) {
console.log("Getting public key credential...");
}

export { create, get }
export { autofill, create, get }
2 changes: 1 addition & 1 deletion app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<%= form_with scope: :session, url: session_path, id: "new-session", data: { controller: "new-session", action: "ajax:success->new-session#create ajax:error->new-session#error" } do |form| %>
<div class="form-field">
<div class="mdc-text-field mdc-text-field--fullwidth" data-controller="textfield" data-target="new-session.usernameField">
<%= form.text_field :username, class: "mdc-text-field__input", placeholder: "Username", required: true, autocapitalize: "none", "aria-controls" => "username-helper-text" %>
<%= form.text_field :username, class: "mdc-text-field__input", autocomplete: "username webauthn", placeholder: "Username", required: true, autocapitalize: "none", "aria-controls" => "username-helper-text" %>
<div class="mdc-line-ripple"></div>
</div>

Expand Down