|
@@ -44,13 +44,16 @@ function coerceToBase64Url(value) {
|
|
|
|
|
|
|
|
class Registration {
|
|
class Registration {
|
|
|
constructor() {
|
|
constructor() {
|
|
|
- document.getElementById('start').addEventListener('click', this.registerAccount.bind(this));
|
|
|
|
|
|
|
+ document.getElementById('register').addEventListener('click', this.registerAccount.bind(this));
|
|
|
|
|
+ this.usernameInput = document.getElementById('username');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async registerAccount(event) {
|
|
async registerAccount(event) {
|
|
|
- event.preventDefault();
|
|
|
|
|
- const username = 'Foo! That Bar';
|
|
|
|
|
- const login = base64UrlEncode(username);
|
|
|
|
|
|
|
+ if (this.usernameInput.value.length < 3) {
|
|
|
|
|
+ console.error("Username must at least be 3 characters long");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const login = base64UrlEncode(this.usernameInput.value);
|
|
|
|
|
|
|
|
let options;
|
|
let options;
|
|
|
|
|
|
|
@@ -155,4 +158,108 @@ class Registration {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+class Login {
|
|
|
|
|
+ constructor() {
|
|
|
|
|
+ document.getElementById('login').addEventListener('click', this.login.bind(this));
|
|
|
|
|
+ this.usernameInput = document.getElementById('username');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async login(event) {
|
|
|
|
|
+ const login = base64UrlEncode(this.usernameInput.value);
|
|
|
|
|
+
|
|
|
|
|
+ let assertionOptions;
|
|
|
|
|
+ try {
|
|
|
|
|
+ assertionOptions = await this.buildAssertionOptions(login);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error("Building assertion options failed with exception", e);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ask browser for credentials (browser will ask connected authenticators)
|
|
|
|
|
+ let credential;
|
|
|
|
|
+ try {
|
|
|
|
|
+ credential = await navigator.credentials.get({ publicKey: assertionOptions })
|
|
|
|
|
+ console.log("credentials.get result", credential);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error("credentials.get failed with exception", e);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let result;
|
|
|
|
|
+ try {
|
|
|
|
|
+ result = await this.verifyCredential(login, credential);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error("Verifying credentials with the server failed with exception", e);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log("New login created", result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async buildAssertionOptions(login) {
|
|
|
|
|
+ let assertionOptions;
|
|
|
|
|
+ const response = await fetch(`/buildAssertionOptions?login=${login}`, {
|
|
|
|
|
+ method: 'GET',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Accept': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ assertionOptions = await response.json();
|
|
|
|
|
+
|
|
|
|
|
+ console.log("Raw Assertion Options", assertionOptions);
|
|
|
|
|
+
|
|
|
|
|
+ assertionOptions.challenge = coerceToArrayBuffer(assertionOptions.challenge);
|
|
|
|
|
+
|
|
|
|
|
+ // fix escaping. Change this to coerce
|
|
|
|
|
+ assertionOptions.allowCredentials.map((listItem) => {
|
|
|
|
|
+ listItem.id = coerceToArrayBuffer(listItem.id);
|
|
|
|
|
+ return listItem;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log("Assertion Options", assertionOptions);
|
|
|
|
|
+
|
|
|
|
|
+ return assertionOptions;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async verifyCredential(login, credential) {
|
|
|
|
|
+ let authData = new Uint8Array(credential.response.authenticatorData);
|
|
|
|
|
+ let clientDataJSON = new Uint8Array(credential.response.clientDataJSON);
|
|
|
|
|
+ let rawId = new Uint8Array(credential.rawId);
|
|
|
|
|
+ let sig = new Uint8Array(credential.response.signature);
|
|
|
|
|
+
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ id: credential.id,
|
|
|
|
|
+ rawId: coerceToBase64Url(rawId),
|
|
|
|
|
+ type: credential.type,
|
|
|
|
|
+ extensions: credential.getClientExtensionResults(),
|
|
|
|
|
+ response: {
|
|
|
|
|
+ authenticatorData: coerceToBase64Url(authData),
|
|
|
|
|
+ clientDataJSON: coerceToBase64Url(clientDataJSON),
|
|
|
|
|
+ signature: coerceToBase64Url(sig)
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let response = await fetch("/verifyCredential", {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ body: JSON.stringify(data),
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Accept': 'application/json',
|
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+
|
|
|
|
|
+ console.log("Assertion Object", result);
|
|
|
|
|
+
|
|
|
|
|
+ if (result.status !== "ok") {
|
|
|
|
|
+ throw new Error(`Error in verifyCredential: ${result.errorMessage}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
window.registration = new Registration();
|
|
window.registration = new Registration();
|
|
|
|
|
+window.login = new Login();
|