Lukas Angerer преди 2 години
родител
ревизия
2e17a7955e
променени са 2 файла, в които са добавени 120 реда и са изтрити 6 реда
  1. 9 2
      wwwroot/index.html
  2. 111 4
      wwwroot/main.js

+ 9 - 2
wwwroot/index.html

@@ -1,11 +1,18 @@
 <!DOCTYPE html>
-<html>
+<html lang="en">
 <head>
     <title>Passwordless Demo</title>
 </head>
 <body>
     <h1>Passwordless Demo</h1>
-    <button id="start">Start</button>
+    <div>
+        <label for="username">Login:</label>
+        <input id="username" type="text" value="My UserName" minlength="3" />
+    </div>
+    <div>
+        <button id="register">Register</button>
+        <button id="login">Login</button>
+    </div>
     <script src="main.js"></script>
 </body>
 </html>

+ 111 - 4
wwwroot/main.js

@@ -44,13 +44,16 @@ function coerceToBase64Url(value) {
 
 class Registration {
     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) {
-        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;
         
@@ -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.login = new Login();