A world without passwords: Windows Hello in Microsoft Edge


In this Microsoft Edge blog post, my team and I discuss bringing biometric authentication to the web:

At Build 2016, we announced that Microsoft Edge is the first browser to natively support Windows Hello as a more personal, seamless, and secure way to authenticate on the web. This experience is powered by an early implementation of the Web Authentication (formerly FIDO 2.0) specification, and we are working closely with industry leaders in both the FIDO Alliance and W3C Web Authentication working group to standardize these APIs. Try out this Test Drive demo in Microsoft Edge on recent Windows Insider builds to experience Windows Hello on the web today!

WindowsHello

Passwords can be a hassle. Most people don’t create strong passwords or make sure to maintain a different one for every site. People create easy-to-remember passwords and typically use the same passwords across all of their accounts. Surprisingly – and if it’s not surprising to you, you may want to change your password – passwords like “123456” and “password” are very common. Malicious actors can use social engineering, phishing, or key logging techniques to steal passwords from your machine, or they can compromise the server where the passwords are stored. When the same password is used across several sites, compromising one account can expose many others to abuse.

We look forward to a web where the user doesn’t need to remember a password, and the server doesn’t need to store a password in order to authenticate that user. Windows Hello, combined with Web Authentication, enables this vision with biometrics and asymmetric cryptography. In order to authenticate a user, the server sends down a plain text challenge to the browser. Once Microsoft Edge is able to verify the user through Windows Hello, the system will sign the challenge with a private key previously provisioned for this user and send the signature back to the server. If the server can validate the signature using the public key it has for that user and verify the challenge is correct, it can authenticate the user securely.

Screen Capture showing Windows Hello prompt to log in to a web page

These keys are not only stronger credentials – they also can’t be guessed and can’t be re-used across origins. The public key is meaningless on its own and the private key is never shared. Not only is using Windows Hello a delightful user experience, it’s also more secure by preventing password guessing, phishing, and keylogging, and it’s resilient to server database attacks.

Web Authentication: Passwordless and Two Factor Authentication

We’ve been working at the FIDO Alliance with organizations from across the industry to enable strong credentials and help move the web off of passwords. The main goal of the FIDO Alliance is to standardize these interfaces, so websites can use Windows Hello and other biometric devices across browsers. The FIDO Alliance had recently submitted the FIDO 2.0 proposal to the W3C and the newly formed Web Authentication working group is standardizing these APIs in the W3C Web Authentication specification.

FIDO Alliance logo

The Web Authentication specification defines two authentication scenarios: passwordless and two factor. In the passwordless case, the user does not need to log into the web page using a user name or password – they can login solely using Windows Hello. In the two factor case, the user logs in normally using a username and password, but Windows Hello is used as a second factor check to make the overall authentication stronger.

In traditional password authentication, a user creates a password and tells the server, which stores a hash of this password. The user, or an attacker who obtains the password, can then use the same password from any machine to authenticate to the server. Web Authentication instead uses asymmetric key authentication. In asymmetric key authentication, the user’s computer creates a strong cryptographic key pair, consisting of a private key and a public key. The public key is provided to the server, while the private key can be held by the computer in dedicated hardware such as a TPM, so that it cannot be moved from that computer. This protects the users against attacks on both the client and the server – client attacks cannot be used to let an attacker authenticate from elsewhere, and server attacks will only give the attacker a list of public keys.

Microsoft Edge supports an early implementation of the Web Authentication spec – in fact, the latest editor’s draft has already been updated beyond our implementation and we expect the spec to continue to change as it goes through the standardization process. We have implemented our APIs with the ms-prefix to indicate that these APIs are very likely to change in the future. We’ll continue to update the APIs in future releases as the standard finalizes – so be sure to watch for changes.

The Web Authentication API is very simple – it supports two methods: window.webauthn.makeCredential and window.webauthn.getAssertion. You will need to make both server and client sides changes to enable Web Auth authentication in your web application. Let’s talk through how to use these methods.

Registering the user

To use Web Auth, you, the identity provider, will first need to create a Web Auth credential for your user using the window.webauthn.makeCredential method.

When you use the makeCredential method, Microsoft Edge will first ask Windows Hello to use face or fingerprint identification to verify that the user is the same user as the one logged into the Windows account. Once this step is completed, Microsoft Passport will generate a public/private key pair and store the private key in the Trusted Platform Module (TPM), the dedicated crypto processor hardware used to store credentials. If the user doesn’t have a TPM enabled device, these keys will be stored in software. These credentials are created per origin, per Windows account, and will not be roamed because they are tied to the device. This means that you’ll need to make sure the user registers to use Windows Hello for every device they use. This makes the credentials even stronger – they can only be used by a particular user on a particular origin on a particular device.

Before registering the credential to a user on your server, you will need to confirm the identity of the user. This can be done by sending the user an email confirmation or asking them to use their traditional login method.

The below code sample shows how you would use the makeCredential API. When you call the makeCredential API, you will need to supply as parameters data structures containing the user account information, crypto parameters, and an attestation challenge. The user account information contains information on the user’s name, profile image, site in which the user is logging into, and user identifier information on the site. We’ll cover later how this information is used. The crypto parameter is a data structure that contains the crypto algorithm you which to use. The attestation challenge is used by the authenticator to produce an attestation statement, which tells the server what security measures the authenticator implements for its credentials. There are a number of other optional parameters, which we’ll ignore here. The methods are all implemented as promises. When the promise returns, it will include an object that contains information on the credential ID, public key, crypto algorithm, and the attestation challenge. The credential ID will be used to identify the public and private key pairs. You will then send this information back to the server for validating future authentications.

function makeCredential() {
  try {
    var accountInfo = {
      rpDisplayName: 'Contoso',  // Name of relying party
      displayName: 'John Doe',  // Name of user account in relying partying
      name: 'johndoe@contoso.com',// Detailed name of account
      id: 'joed',                 // Account identifier
      imageUri: imgUserProfile,  // user’s account image
    }; 
    
    var cryptoParameters = [
      {
        type: 'ScopedCred',
        algorithm: 'RSASSA-PKCS1-v1_5'
      }      
    ];
 
    var timeout = { };
    var denyList = { };
    var ext = { };
 
    var attestationChallenge = getChallengeFromServer();
 
    window.webauthn.makeCredential(
           accountInfo, 
                  cryptoParameters, 
                  attestationChallenge, 
                  timeout, 
                  denyList, 
                  ext)
      .then(function (creds) {
       // If promised succeeds, send credential information to the server
        sendToServer(creds);
      })
      .catch(function(reason) {
        // User may have cancelled the Windows Hello dialog
      });
  } catch(ex) {
     // The user may not have setup Windows Hello, show instructions
   }
}

The Microsoft Edge implementation is ms-prefixed, so you’ll need to call window.msCredentials.makeCredential instead of window.webauthn.makeCredential. The Microsoft Edge implementation is also based on an earlier draft of the specification, so there are a number of other differences in implementation as well, like the credential type is “FIDO_2_0” instead of “ScopedCred”, we don’t yet implement the optional timeout, denylist, or ext parameters, or require the attestation challenge to make the credential. In Microsoft Edge, you would instead make this call using the following code:

    var accountInfo = {
      rpDisplayName: 'Contoso',        // Name of relying party
      userDisplayName: 'John Doe'      // Name of user account in relying partying
    }; 
    
    var cryptoParameters = [
      {
        type: 'FIDO_2_0',
        algorithm: 'RSASSA-PKCS1-v1_5'
      }      
    ];
 
    window.msCredential.makeCredential(accountInfo, cryptoParameters)
      .then(function (cred) {
       // If promised succeeds, send credential information to the server
        sendToServer({
                   credential: {type: 'ScopedCred', id: cred.id},
                   algorithm: cred.algorithm,
                   publicKey: JSON.parse(cred.publicKey),
                   attestation: cred.attestation
               }
     );
      });

Authenticating the user

Once the credential is created on the client, the next time the user attempts to log into the site, you can offer to sign them in using Windows Hello instead of a password. You will authenticate the user using the window.webauthn.getAssertion call.

The getAssertion call has a number of optional parameters, but the only required parameter is the challenge. This is the challenge that the server will send down to the client. This challenge is a random quantity generated by the server. Since the challenge is not predictable by an attacker, the server can be assured that any assertions it receives were freshly generated in response to this challenge and are not replays of earlier assertions. The allowList parameter also takes an optional list of credential ID information to locate the correct private key. This information is useful if you’re doing two factor auth and you can share the id from the server, where it is stored. In the passwordless case, you don’t want to share the id from the server because the user hasn’t yet authenticated.

If the user has multiple credentials for an origin in the TPM, the browser will show a user experience allowing the user to pick the account they meant to use (assuming they have multiple accounts with an application, and you don’t provide a credential ID). This is why we collect the user profile picture and account name upon registration.

Once the getAssertion call is made, Microsoft Edge will show the Windows Hello prompt, which will verify the identity of the user using biometrics. After the user is verified, the challenge will be signed within the TPM and the promise will return with an assertion object that contains the signature and other metadata. You will then send this data to the server. We’ll check the server code next to see how you verify the challenge is the same that you had sent.

function getAssertion() {
  try {
    var challenge = getChallengeFromServer(); 
    var allowList = 
          [
            {
              type: 'ScopedCred',
              id:  getCredentialID()
            }
          ];
    var timeout = { };
    var ext = { };
 
    window.webauthn.getAssertion(challenge, timeout, allowList, ext)
      .then(function(assertion) {
         // Send signed challenge and meta data to server
        sendToServer(assertion);
      }, function (e) {
        // No credential in the store. Fallback to password
      });    
  } catch (ex) {
    // Log failure
  }
}

In Microsoft Edge, you will need to call window.msCredentials.getAssertion instead of window.webauthn.getAssertion. The Microsoft Edge implementation also requires the credential ID and we don’t yet support the account picker experience. A side effect of this is that for the passwordless case, you’ll need to store your credential ID information in local storage on the client, either in indexDB or localStorage when making your credential. This mean that if a user deletes their browsing history, including local storage, they will need to re-register to use Windows Hello the next time they log in. We will very likely fix this issue in a future release.

Here’s how you would make the getAssertion call in Microsoft Edge. Note how the accept object is required for the filter parameter.

var filters = {
        accept:
          [
            {
              type: 'FIDO_2_0',
              id:  getCredentialIDFromLocalStorage()
            }
          ]
        };
window.msCrendentials.getAssertion(challenge, filters)
.then(function(attestation) {
    // Send signed challenge and meta data to server
    sendToServer({
                   credential: {type: 'ScopedCred', id: attestation.id},
                   clientData: attestation.signature.clientData,
                   authnrData: attestation.signature.authnrData,
                   signature: attestation.signature.signature
                   };
    );
  });   

Server side authentication

Once you receive the assertion on the server, you will need to validate the signature. The below Node.JS code shows how you would validate the signature to authenticate the user on the server. We also have the same code available in C# and PHP.

var jwkToPem = require('jwk-to-pem')
var crypto = require('crypto');
 
var webAuthAuthenticator = {
   validateSignature: function (publicKey, clientData, authnrData, signature, challenge) {
       // Make sure the challenge in the client data 
       // matches the expected challenge
       var c = new Buffer(clientData, 'base64');
       var cc = JSON.parse(c.toString().replace(/\0/g,''));
       if(cc.challenge != challenge) return false;
 
       // Hash data with sha256
       const hash = crypto.createHash('sha256');
       hash.update(c);
       var h = hash.digest();
 
       // Verify signature is correct for authnrData + hash
       var verify = crypto.createVerify('RSA-SHA256');
       verify.update(new Buffer(authnrData,'base64'));
       verify.update(h);
       return verify.verify(jwkToPem(JSON.parse(publicKey)), signature, 'base64');
   }
};   

Evolving Web Authentication standard and Microsoft Edge implementation

As mentioned above, Microsoft Edge has an early implementation of Web Authentication and there are a number of differences between our implementation and the April 2016 spec.

  • Microsoft Edge APIs are ms- prefixed
  • Microsoft Edge does not yet support external credentials like USB keys or Bluetooth devices. The current API is limited to embedded credentials stored in the TPM.
  • The currently logged in Windows user account must be configured to support at least a PIN, preferably face or fingerprint biometrics. This is to ensure that we can authenticate the access to the TPM.
  • We do not support all of the options in the current Web Auth spec draft, like extensions or timeouts.
  • As mentioned earlier, our implementation requires that the list of acceptable credential IDs be included in every getAssertion call.

The specification is also going through the W3C standardization process and we expect a number of changes in the specification, like the object name recently changing from window.fido to window.webauthn in the latest’s editor’s draft.

We have a number of resources available to help you prototype and experiment with these APIs:

    • Webauthn.js polyfill. Using this polyfill, you can code to the standard instead of our early implementation. We’ll update this polyfill for every major published version of the specification.
    • Windows Hello in Microsoft Edge test drive sample. This test drive sample shows you the typical client side registration and assertion flow.
    • Server and client side WebAuth This sample code shows the end to end client and server side flow for registration and assertion.
    • C#, PHP, and JS server side sample. These code samples show how could implement your server side logic in a number of language options.
    • Web Authentication MSDN documentation and dev guide.
    • Edge Summit talk on Windows Hello in Microsoft Edge.

Call to Action

We’re excited to support Windows Hello and Web Authentication in Microsoft Edge and innovate in the biometric authentication and passwordless space in the web. Now is a great time to start prototyping and experimenting with these APIs and sharing your feedback with us in the comments below or on Twitter. We look forward to hearing from you!

Rob Trace, Program Manager, Microsoft Edge
Jatinder Mann, Program Manager Lead, Microsoft Edge
Vijay Bharadwaj, Software Engineering Lead, Security
Anoosh Saboori, Program Manager, Security

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s