The previous guide showed how to integrate IdentityServer with Authsignal in order to add MFA after a traditional username & password sign-in.

In this guide, we’ll demonstrate how to let the user create a passkey and use it instead of their password the next time they sign in.

Using passkey autofill to sign in

Configuring passkeys

In the Authsignal Portal we have configured localhost as the Relying Party for the purposes of local development. We also whitelist both the login server and the application server as expected origins.

Configuring the relying party ID and expected origins

This means the user can enroll a passkey directly from our web app running on https://localhost:5002, then reauthenticate with the same passkey when signing in to IdentityServer running on https://localhost:5001.

Enrolling a passkey

Once the user has logged in with MFA, we can use the Authsignal Web SDK to allow the user to add a passkey.

This can be done by adding a button to the application server (i.e. the WebClient).

Adding a passkey

To implement this we add a new page /src/WebClient/Pages/Passkeys.cshtml.

@page
@model PasskeysModel

@Html.HiddenFor(m => m.enrollmentToken)

<button id="add-passkey" onClick="addPasskey()">Add a passkey</button>

<script src="https://unpkg.com/@@authsignal/browser@@0.3.0/dist/index.min.js"></script>
<script src="~/js/passkeys.js"></script>

The enrollmentToken here is fetched server-side in /src/WebClient/Pages/Passkeys.cshtml.cs when the page loads.

public async Task<IActionResult> OnGet()
{
  var trackRequest = new TrackRequest(
    UserId: User.Claims.First(x => x.Type == "sub").Value!,
    Action: "add-passkey",
    Attributes: new TrackAttributes(
      Scope: "add:authenticators"
    )
  );

  var trackResponse = await _authsignal.Track(trackRequest);

  this.enrollmentToken = trackResponse.Token;

  return Page();
}

Finally, we need to add the client-side JS implementation for the addPasskey function which uses the Authsignal Web SDK.

function addPasskey() {
  var client = new window.authsignal.Authsignal({
    tenantId: "YOUR_TENANT_ID",
    baseUrl: "https://api.authsignal.com/v1", // Update for your region
  });

  var token = document.getElementById("enrollmentToken").value;

  client.passkey.signUp({ token }).then((resultToken) => {
    if (resultToken) {
      alert("Passkey added");
    }
  });
}

Signing in with a passkey

Now that the user has enrolled a passkey, the next time they sign in via IdentityServer we would like to give them the option to use their passkey instead of their username & password.

This can be achieved simply by adding a small client-side JavaScript snippet which initializes the existing username field to allow passkeys.

First we need to ensure that the input field has the value username webauthn in the autocomplete attribute.

<input
  id="passkey-username"
  class="form-control"
  placeholder="Username"
  asp-for="Input.Username"
  autocomplete="username webauthn"
  autofocus
/>

Then we add some JavaScript to run when the page loads.

function initPasskeyAutofill() {
  var client = new window.authsignal.Authsignal({
    tenantId: "YOUR_TENANT_ID",
    baseUrl: "https://api.authsignal.com/v1", // Update for your region
  });

  client.passkey.signIn({ autofill: true }).then((token) => {
    if (token) {
      var returnUrl = document.getElementById("Input_ReturnUrl").value;

      window.location = `https://localhost:5001/Account/Login/Callback?returnUrl=${returnUrl}&token=${token}`;
    }
  });
}

if (document.readyState === "loading") {
  document.addEventListener("DOMContentLoaded", initPasskeyAutofill);
} else {
  initPasskeyAutofill();
}

This will initialize the input field for passkey autofill, which means if a user has enrolled a passkey then they should be able to select it when focusing the text field.

Signing in with a passkey

The validation process here is the same as for signing in with MFA in the previous guide - the only difference here is that we now receive a token directly from the Authsignal Web SDK. In both cases, we pass this token to our callback page, which uses the token to validate the result of the challenge server-side and log the user in.

For more information, check out our passkeys documentation or to test out passkey compatibility on your browser you can play with our demo site.