Forráskód Böngészése

More documentation

Lukas Angerer 2 éve
szülő
commit
8a32d6108c
5 módosított fájl, 85 hozzáadás és 6 törlés
  1. 1 0
      .gitignore
  2. 1 0
      Program.cs
  3. 61 4
      README.md
  4. 1 1
      data/pixlar.json
  5. 21 1
      wwwroot/index.html

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 ## files generated by popular Visual Studio add-ons.
 ##
 ## Get latest from `dotnet new gitignore`
+data/*
 
 # dotenv files
 .env

+ 1 - 0
Program.cs

@@ -76,6 +76,7 @@ if (app.Environment.IsDevelopment())
     app.UseSwaggerUI();
 }
 
+app.UseDefaultFiles();
 app.UseStaticFiles();
 app.UseHttpsRedirection();
 app.UseAuthorization();

+ 61 - 4
README.md

@@ -1,6 +1,5 @@
-This is a very basic demo of passwordless authentication in the web, also known as "WebAuthn".
-
-# Overview
+# Overview
+This is a very basic demo of passwordless authentication in the web, also known as "WebAuthn".
 
 # Technical Details
 Note that for convenience, the client always sends the base64URL encoded user name from the UI to
@@ -133,8 +132,66 @@ persisted in a file "[username].json".
 
 ## Login
 
+### Assertion Options from Login
+For the login, we create a `AssertionOptions` instance from a registered login name (if that login
+actually exists) which looks like this:
+
+```json
+{
+    "challenge": "VlXKhoI7x3T5LxsKP_WPnw",
+    "timeout": 60000,
+    "rpId": "demo.larcanum.net",
+    "allowCredentials": [
+        {
+            "type": "public-key",
+            "id": "vHJFHVmoY37hrUHFObgkw7QkBB1p44uhYlLc4i9r1BHde5XEVObMB8QOKWKpcf4eWEODN2kK5x84pRlNn8etlQ"
+        }
+    ],
+    "userVerification": "discouraged",
+    "extensions": {
+        "exts": true,
+        "uvm": false
+    },
+    "status": "ok",
+    "errorMessage": ""
+}
+```
+
+The options again need to be post-processed on the client before they can be used with
+`navigator.credentials.get` by converting base64URL encoded strings to byte arrays (`Uint8Array`).
+
+### Verifying the Credentials
+The result of `navigator.credentials.get` looks very similar to the one from
+`navigator.credentials.create` but with the addition of a "signature"
+
+- the `signature` field contains the signed challenge from the request object and is the critical
+  piece of information that the server needs to verify
+
+The raw verification request looks like this:
+```json
+{
+    "id": "vHJFHVmoY37hrUHFObgkw7QkBB1p44uhYlLc4i9r1BHde5XEVObMB8QOKWKpcf4eWEODN2kK5x84pRlNn8etlQ",
+    "rawId": "vHJFHVmoY37hrUHFObgkw7QkBB1p44uhYlLc4i9r1BHde5XEVObMB8QOKWKpcf4eWEODN2kK5x84pRlNn8etlQ==",
+    "type": "public-key",
+    "extensions": {},
+    "response": {
+        "authenticatorData": "oRsUvdxlEqdT6Qnbe1LQ/aqF8rHBT3MzNqBH6nFOcf0BAAAACA==",
+        "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVmxYS2hvSTd4M1Q1THhzS1BfV1BudyIsIm9yaWdpbiI6Imh0dHBzOi8vZGVtby5sYXJjYW51bS5uZXQiLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==",
+        "signature": "MEUCIBanHNIJS6ozJe0Nzf0fMJDEeqr/R2J33izif54zuGH1AiEAyOxPUhYfd/7vWnIDh1a4Dind3dTPPjE5O02olEhLbUE="
+    }
+}
+```
+
+Once the credentials have been verified on the server, it generates a JWT for those credentials
+which can be used to access a simple API protected with JWT bearer authentication.
+
 ## Stored Data
 Currently, we directly store the `AttestationVerificationSuccess` that the client sends to complete
 the registration process.
 
-[my username.json](./data/my username.json)
+[my username.json](./data/my username.json)
+
+## Notes
+- Credentials are **bound** to a specific domain. If the credentials presented to the passkey are
+  from a different domain, then the message "This security key doesn't look familiar. Please try
+  a different one" appears in the passkey selection window.

+ 1 - 1
data/pixlar.json

@@ -10,7 +10,7 @@
   "AttestationCertificate": null,
   "AttestationCertificateChain": [],
   "CredentialId": "AUaVZoW6gV0Z3T3fX9yR2EO49nSwgXphbs67Aqcq/8ttQ6/byO1tW6F9qq6YG08mnluYtsYV9gjI5n0lbKLHeG0=",
-  "Counter": 1,
+  "Counter": 3,
   "status": null,
   "errorMessage": null
 }

+ 21 - 1
wwwroot/index.html

@@ -1,13 +1,29 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Passwordless Demo</title>
 </head>
 <body>
     <h1>Passwordless Demo</h1>
+    <p>
+        This demo uses the FIDO2 browser APIs <code>navigator.credentials.create</code> and
+        <code>navigator.credentials.get</code> to implement a <em>passwordless</em> login with non-resident keys.
+    </p>
+    <p>
+        <strong>Step 1:</strong> Choose a username and click the "Register" button. If the username is still available,
+        your browser will prompt you to select a device to use as a <em>passkey</em>. After authorizing your passkey
+        device your account is created.
+    </p>
+    <p>
+        <strong>Step 2:</strong> With the same username that you used for the registration, click the "Login" button.
+        The same browser prompt will show up to perform the login with your passkey device. If the login was successful,
+        you will receive a JWT from the server that will be shown below.
+    </p>
     <div>
         <label for="username">Login:</label>
-        <input id="username" type="text" value="My UserName" minlength="3" />
+        <input id="username" type="text" placeholder="Choose Username" minlength="3" />
     </div>
     <div>
         <button id="register">Register</button>
@@ -15,6 +31,10 @@
     </div>
     <hr />
     <div>
+        <p>
+            <strong>Step 3:</strong> Once you are authenticated with the server, you can click the
+            "Access Protected API" button to test the effectiveness of your JWT. You have just used 
+        </p>
         <pre><code id="userState">[not authenticated]</code></pre>
         <div>
             <button id="protected">Access Protected API</button>