Users of U2F APIs should migrate to Webauthn

619 views
Skip to first unread message

Adam Langley

unread,
May 29, 2019, 1:31:04 PM5/29/19
to blink-dev, security-dev
The U2F API provides a way for web sites to interact with security keys, which enable strong, phishing-resistant authentication. While Chromium has never supported the U2F API directly, it has been possible for many years to post messages to an internal extension which implemented it. Several major sites have implemented support for security keys based on this.

The W3C has recently published level one of the Web Authentication standard (“WebAuthn”). This official web API subsumes the U2F API and web sites that use the U2F API should now start to plan their transition. While we are not yet ready to announce a specific timeline for the removal of the internal extension that supports the U2F API, that is our eventual intent.

WebAuthn [MDN documentation] supports U2F authenticators and credentials created via the U2F API can be used with WebAuthn via the appid extension. Since credentials created with WebAuthn cannot be used via U2F, sites should first transition their sign-in flow to WebAuthn then, once that is settled, transition their registration flow. Once a given user no longer has any credentials that were created via the U2F APIs, the appid extension can be dropped for that user’s assertion requests.

In the context of the example on MDN, setting the appid extension would involve updating one of the definitions thus:

var getCredentialDefaultArgs = {
   publicKey: {
       timeout: 60000,
       // allowCredentials: [newCredential] // see below
       challenge: new Uint8Array([…]).buffer,
       extensions: {
           'appid': 'https://example.com',
       },

   },
};

(Where example.com is replaced with the AppID that you used for registering U2F credentials.)

We invite feedback about experiences and timelines from sites that are planning or undergoing this transition.


AGL

bart.d...@shopify.com

unread,
May 30, 2019, 1:56:12 PM5/30/19
to Security-dev, blin...@chromium.org
For the Ruby WebAuthn library I've started a document on how applications can migrate from U2F: https://github.com/cedarcode/webauthn-ruby/pull/211

Lucas Garron

unread,
May 30, 2019, 4:05:44 PM5/30/19
to Adam Langley, lga...@github.com, blink-dev, security-dev
GitHub is currently working on migrating to WebAuthn!

We hit a *lot* of snags. Some things that would help others avoid our pain points:
  • Examples of how to migrate from U2F.
    • Information about the appid extension (this used to be much harder to find!).
    • Examples of how RP ID scoping works (when I read the spec, I originally assumed that "registrable domain suffix" was a formal synonym for eTLD+1, but it's not).
    • Which old values (e.g. key handle) can be reused? Does any conversion need to happen?
  • A good library for the relevant server language, with:
    • Support for setting RP ID as well as using the appid extension.
    • Ergonomics (e.g. automatically serializing webauthn requests to/from JSON — see below).
    • Test fixtures.
  • A de facto/official convention for how to serialize the WebAuthn API input/output on the wire, and perhaps some small libraries for this. (WebAuthn API input/output looks essentially like JSON, with the awkward detail that binary data values are ArrayBuffers. It seems really minor, but this forces every implementation to make a custom design decision where U2F "just worked". Sample implementations often convert to base 64, but don't agree on websafe vs. normal base 64. It's also tempting to "flatten" the JSON while implementing a transformation, but that could hurt forward compatibility.)
  • Up-to-date type definitions in TypeScript and Flow.
    • TypeScript has a few stale details from older spec versions.
    • Flow landed some partial definitions that made it harder for us than when there were no definitions at all. There are still no full definitions. 😔
And some extra things that would be nice:
  • WebAuthn mock implementations in JS for headless browser tests.
  • An explainer on how the different physical access concepts interact: user presence vs. user verification vs. platform authentication vs. prevent silent access. This is a little confusing.
  • A reference on future considerations — if we first migrate from U2F to webauthn with feature parity, do we need to keep anything in mind if we want to support "single-factor" auth in the future? Is it worth storing attestations from registrations?
  • How strong is the recommendation for 64 bytes as the user handle? If we were to use hashed/encrypted/encoded values (or even just reused public user IDs), what would that do to the threat model?
  • An explanation of what algorithm IDs should be used. Is it fine to just assume consumer security keys will use -7 for the indefinite future, or might that paint us into a corner?
  • A reference on browser behaviours, e.g.
    • In practice, what kind of browser UI will cover our web UI? Is there a best practice for what context to give in the browser?
      • We have some in-page modal dialogs are almost completely covered by Chrome's UI. Should we still have a UI, or should we trust every browser to "do the talking"?
      • We have some flows where the U2F request happens "in the background" — we show a password field along with a spinner stating "Alternatively, press the button on your security key…". Is it reasonable to try to adapt this to WebAuthn?
    • How should we split the responsibility of explaining what went wrong to the user? (U2F provided more detailed errors than WebAuthn does. When WebAuthn gives us a generic error, can we trust the browser to explain what happened instead so that we don't have to recap it?)
    • Is there anything that would work fine on one platform but not others? (For example, I believe the attestation format is different on Android. You need to pick a server library that handles this.)
We've contributed a few miscellaneous fixes for these issues, and hope to share more of what we learned. For now, though, we're focusing on shipping WebAuthn for github.com accounts — I can't share an exact timeline, but we would almost certainly be ready before any practical deprecation date for Chrome.

»Lucas
(GitHub Product Security)

Adam Langley

unread,
May 30, 2019, 4:16:38 PM5/30/19
to Lucas Garron, lga...@github.com, blink-dev, security-dev
On Thu, May 30, 2019 at 1:05 PM Lucas Garron <lga...@chromium.org> wrote:
GitHub is currently working on migrating to WebAuthn!

We hit a *lot* of snags. Some things that would help others avoid our pain points:
  • Examples of how to migrate from U2F.
    • Information about the appid extension (this used to be much harder to find!).
    • Examples of how RP ID scoping works (when I read the spec, I originally assumed that "registrable domain suffix" was a formal synonym for eTLD+1, but it's not).
    • Which old values (e.g. key handle) can be reused? Does any conversion need to happen?
  • A good library for the relevant server language, with:
    • Support for setting RP ID as well as using the appid extension.
    • Ergonomics (e.g. automatically serializing webauthn requests to/from JSON — see below).
    • Test fixtures.
  • A de facto/official convention for how to serialize the WebAuthn API input/output on the wire, and perhaps some small libraries for this. (WebAuthn API input/output looks essentially like JSON, with the awkward detail that binary data values are ArrayBuffers. It seems really minor, but this forces every implementation to make a custom design decision where U2F "just worked". Sample implementations often convert to base 64, but don't agree on websafe vs. normal base 64. It's also tempting to "flatten" the JSON while implementing a transformation, but that could hurt forward compatibility.)
  • Up-to-date type definitions in TypeScript and Flow.
    • TypeScript has a few stale details from older spec versions.
    • Flow landed some partial definitions that made it harder for us than when there were no definitions at all. There are still no full definitions. 😔
Thank you for that! That may make excellent fodder for some updates to MDN or documentation that we host ourselves.

A few points where I can give specific responses:

And some extra things that would be nice:
  • WebAuthn mock implementations in JS for headless browser tests.
In progress.
  • An explainer on how the different physical access concepts interact: user presence vs. user verification vs. platform authentication vs. prevent silent access. This is a little confusing.
  • A reference on future considerations — if we first migrate from U2F to webauthn with feature parity, do we need to keep anything in mind if we want to support "single-factor" auth in the future? Is it worth storing attestations from registrations?
  • How strong is the recommendation for 64 bytes as the user handle? If we were to use hashed/encrypted/encoded values (or even just reused public user IDs), what would that do to the threat model?
64 bytes is a maximum length. It can certainly be shorter. The important requirement is that it not contain PII (so don't use public user IDs, at least encrypt them first). When creating resident credentials (i.e. what I think you're referring to as "single-factor") the user-id is what you get back to identify the user, and it may be accessible to anyone who has physical access to the device. (Thus the requirement not to include PII.)
  • An explanation of what algorithm IDs should be used. Is it fine to just assume consumer security keys will use -7 for the indefinite future, or might that paint us into a corner?
  • A reference on browser behaviours, e.g.
    • In practice, what kind of browser UI will cover our web UI? Is there a best practice for what context to give in the browser?
      • We have some in-page modal dialogs are almost completely covered by Chrome's UI. Should we still have a UI, or should we trust every browser to "do the talking"?
      • We have some flows where the U2F request happens "in the background" — we show a password field along with a spinner stating "Alternatively, press the button on your security key…". Is it reasonable to try to adapt this to WebAuthn?
    • How should we split the responsibility of explaining what went wrong to the user? (U2F provided more detailed errors than WebAuthn does. When WebAuthn gives us a generic error, can we trust the browser to explain what happened instead so that we don't have to recap it?)
    • Is there anything that would work fine on one platform but not others? (For example, I believe the attestation format is different on Android. You need to pick a server library that handles this.)
We've contributed a few miscellaneous fixes for these issues, and hope to share more of what we learned. For now, though, we're focusing on shipping WebAuthn for github.com accounts — I can't share an exact timeline, but we would almost certainly be ready before any practical deprecation date for Chrome.


Cheers

AGL 

Lucas Garron

unread,
May 30, 2019, 4:23:54 PM5/30/19
to Adam Langley, Lucas Garron, blink-dev, security-dev
On Thu, May 30, 2019 at 1:16 PM Adam Langley <a...@chromium.org> wrote:
On Thu, May 30, 2019 at 1:05 PM Lucas Garron <lga...@chromium.org> wrote:
GitHub is currently working on migrating to WebAuthn!

We hit a *lot* of snags. Some things that would help others avoid our pain points:
  • Examples of how to migrate from U2F.
    • Information about the appid extension (this used to be much harder to find!).
    • Examples of how RP ID scoping works (when I read the spec, I originally assumed that "registrable domain suffix" was a formal synonym for eTLD+1, but it's not).
    • Which old values (e.g. key handle) can be reused? Does any conversion need to happen?
  • A good library for the relevant server language, with:
    • Support for setting RP ID as well as using the appid extension.
    • Ergonomics (e.g. automatically serializing webauthn requests to/from JSON — see below).
    • Test fixtures.
  • A de facto/official convention for how to serialize the WebAuthn API input/output on the wire, and perhaps some small libraries for this. (WebAuthn API input/output looks essentially like JSON, with the awkward detail that binary data values are ArrayBuffers. It seems really minor, but this forces every implementation to make a custom design decision where U2F "just worked". Sample implementations often convert to base 64, but don't agree on websafe vs. normal base 64. It's also tempting to "flatten" the JSON while implementing a transformation, but that could hurt forward compatibility.)
  • Up-to-date type definitions in TypeScript and Flow.
    • TypeScript has a few stale details from older spec versions.
    • Flow landed some partial definitions that made it harder for us than when there were no definitions at all. There are still no full definitions. 😔
Thank you for that! That may make excellent fodder for some updates to MDN or documentation that we host ourselves.

Thanks! We were thinking of putting some of this in a blog post, but a canonical reference would be better.
 

A few points where I can give specific responses:

And some extra things that would be nice:
  • WebAuthn mock implementations in JS for headless browser tests.
In progress.

😍 
  • An explainer on how the different physical access concepts interact: user presence vs. user verification vs. platform authentication vs. prevent silent access. This is a little confusing.
  • A reference on future considerations — if we first migrate from U2F to webauthn with feature parity, do we need to keep anything in mind if we want to support "single-factor" auth in the future? Is it worth storing attestations from registrations?
  • How strong is the recommendation for 64 bytes as the user handle? If we were to use hashed/encrypted/encoded values (or even just reused public user IDs), what would that do to the threat model?
64 bytes is a maximum length. It can certainly be shorter. The important requirement is that it not contain PII (so don't use public user IDs, at least encrypt them first). When creating resident credentials (i.e. what I think you're referring to as "single-factor") the user-id is what you get back to identify the user, and it may be accessible to anyone who has physical access to the device. (Thus the requirement not to include PII.)

Excellent, that matches what we understand! Some of these conclusions just felt very tentative at first.

nst...@duosecurity.com

unread,
May 30, 2019, 5:03:01 PM5/30/19
to Security-dev, a...@chromium.org, lga...@github.com, blin...@chromium.org
Hey Lucas, cool to hear that GitHub is moving to WebAuthn!

Some additional answers:

> Is it worth storing attestations from registrations?

If you're doing attestation, it could be worth storing the AAGUID from the authenticator, but it probably isn't necessary to store the statement after verifying it.

> What algorithm IDs should be used
The IANA Cose Registry has a list of what is supported, you can see an example of what I support in my example library here: https://github.com/duo-labs/webauthn/blob/master/protocol/webauthncose/webauthncose.go#L195

Adam Langley

unread,
May 30, 2019, 5:48:10 PM5/30/19
to nst...@duosecurity.com, Security-dev, lga...@github.com, blink-dev
On Thu, May 30, 2019 at 2:03 PM <nst...@duosecurity.com> wrote:
If you're doing attestation, it could be worth storing the AAGUID from the authenticator, but it probably isn't necessary to store the statement after verifying it.

We recommend not requesting attestation in consumer cases.


Cheers

AGL

bart.d...@shopify.com

unread,
May 31, 2019, 8:31:53 AM5/31/19
to Security-dev, a...@chromium.org, lga...@github.com, blin...@chromium.org
> An explanation of what algorithm IDs should be used. Is it fine to just assume consumer security keys will use -7 for the indefinite future, or might that paint us into a corner?

The WebAuthn gem is configured (via `WebAuthn.configuration.algorithms`) to allow -7 and -257 (for Windows Hello: https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/windows-integration/web-authentication#special-considerations-for-windows-hello) by default, which to my knowledge are the two required ones. A PR for -37 is open, opt-in support for -65535 available.

The FIDO conformance tools also allow testing for other ECDSA curves and EdDSA. And https://tools.ietf.org/html/draft-ietf-cose-webauthn-algorithms-00 was announced on the WebAuthn mailing list last March to provide some guidance.

Craig Francis

unread,
Jun 1, 2019, 5:29:55 PM6/1/19
to Adam Langley, blink-dev, security-dev
On Wed, 29 May 2019 at 6:31 pm, Adam Langley <a...@chromium.org> wrote:
> We invite feedback about experiences and timelines from sites that are planning or undergoing this transition.



Similar to one of the points raised by Lucas, I’ve found Uint8Array/ArrayBuffer and CBOR encoding problematic.

I’ve started implementing WebAuthn, got some of it working, but had to move on to other projects before I had a chance to complete it (so I might be missing some details).

Most programming languages and programmers already know how to deal with JSON and base64 encoding, so I think that would be a better approach.

To use CBOR in PHP today, I’ll have to pull in separate library/code to process it, and a bit more code to covert it to/from an array of integers:

challenge = new Uint8Array([<?= (implode(',', unpack('C*', $challenge))) ?>])

(I wouldn’t recommend writing JavaScript like this, as it implies unsafe-inline).

And I have a feeling I’m missing something, as UInt8Array requires 2 to 4 bytes to represent each byte (200-400%), whereas base64 encoding is just 133%.

I’d much rather provide the challenge and user.id as a base64 encoded string; and the browser either return an attestationObject as an actual object, or as a JSON string, with the individual binary fields being base64 encoded values.

This would mean the browser has done the parsing for all websites; the JavaScript can easily send it back to the server (where I could use JSON.stringify if it was an object); and the server can simply json_decode and base64_decode the fields, then go straight into checking the origin, hash, public key, etc.

Hope that’s helpful.

Craig




--
To unsubscribe from this group and stop receiving emails from it, send an email to security-dev...@chromium.org.

Jeff Hodges

unread,
Jun 3, 2019, 5:57:39 PM6/3/19
to Security-dev, a...@chromium.org, lga...@github.com, blin...@chromium.org
Hi Lucas,

On Thursday, May 30, 2019 at 1:05:44 PM UTC-7, Lucas Garron wrote:
GitHub is currently working on migrating to WebAuthn!

super!

 
We hit a *lot* of snags. Some things that would help others avoid our pain points:

:(

[...]
 
We've contributed a few miscellaneous fixes for these issues,

just curious where?
 

and hope to share more of what we learned. 

great, thanks!

=JeffH
 

da...@alkaline-solutions.com

unread,
Jun 4, 2019, 9:31:16 PM6/4/19
to Security-dev, a...@chromium.org, lga...@github.com, blin...@chromium.org
On Thursday, May 30, 2019 at 2:05:44 PM UTC-6, Lucas Garron wrote:
<snip>
> A reference on future considerations — if we first migrate from U2F to webauthn with feature parity, do we need to keep anything in mind if we want to support "single-factor" auth in the future?

If you wanted to deploy this today, I personally would recommend requesting a resident key gated on isUser​Verifying​Platform​Authenticator​Available(). Most roaming authenticators would use PIN support for user verification, which is lagging.

You should be able to use the key handle returned the same as always, and mark the key as resident within your database to control the user experience/expectations.

Eventually, you should be able to say you would *prefer* resident keys, and PIN support will be there to the point needed to support roaming authenticators.

Actual doing single-factor login will require resident keys and FIDO 2 authenticators, although you can of course support the old keys with a different method of session *initiation*, and use the key possession as an intermittent authentication challenge against that session.

> Is it worth storing attestations from registrations?

Even requesting attestations is not worth it unless you plan on blocking certain authenticators in the future based on make and model. Reasons for doing this may include the need for hitting certain security requirements (NIST 800-63 AAL3) and/or certain certifications (FIPS).

IMHO - if you don't see blocking Soft U2F for your users, you probably don't need attestations at all.

Requesting attestation usually involves extra user consent, which they might not give - in which case you have to decide whether to accept the key without that attestation, or fail and give the user appropriate information to understand what you require. All attestation-related UX winds up being the RP responsibility.

Whenever you change your policy on what attestations are appropriate, you'll have a user migration period. So unless you would impact all users with such a migration, it is probably not worth requiring attestations now. If it is a matter of an enterprise requiring a particular vendor or model of key for their users to participate against an organization, your UX will be simpler and have higher user conversion by ignoring attestations altogether. I would do this even if it means the user re-registers an existing key later to get access to some additional scopes. However, if they are requiring something like AAL3, they may require IAL2/3 identity proofing before allowing a new attested authenticator to be added.

FWIW in my implementation I collect attestations. The none, self-signed, and unsupported attestation formats are all equivalent to a non-attested key. All data related to the original registration is saved so that the attestation can be fully re-evaluated in the future.

Hans Nielsen

unread,
Jun 11, 2019, 12:11:15 PM6/11/19
to Security-dev, blin...@chromium.org
The "FIDO AppID Extension" (https://www.w3.org/TR/webauthn/#sctn-appid-extension) clearly calls out that the WebAuthn API will not create backwards-compatible registrations. Empirically, both Chrome and Firefox will reject "navigator.credentials.Create()" calls that specify this extension.

The problem is that we no longer have a way of putting existing U2F registrations on the "excludeCredentials" list. While you can include the key handle, there's no way to pass the AppID. This means that the RP will never test if an authenticator responds to the legacy AppID, and thus the existing credential is effectively ignored.

In practice, this means you can make a second registration for the same token via WebAuthn. There's no apparent way to distinguish this registration server-side, leading to a confusing experience for users who accidentally re-register their authenticator.

I'd love to see some way to support deconflicting existing U2F registrations during the WebAuthn credential creation process. As a simple first proposal, there could be an "appid" registration extension for use with the credential exclusion list only. The extension would provide no output. This supports the goal of not allowing new U2F registrations while also detecting existing U2F registrations correctly.


I'd also like to echo the feedback about marshaling / unmarshalling ArrayBuffers being somewhat painful and unintuitive. Having all the data already encoded in U2F made for a very easy integration experience.

Hans

da...@alkaline-solutions.com

unread,
Jun 11, 2019, 9:25:59 PM6/11/19
to blink-dev, securi...@chromium.org


On Tuesday, June 11, 2019 at 1:06:06 PM UTC-6, Hans Nielsen wrote:
The problem is that we no longer have a way of putting existing U2F registrations on the "excludeCredentials" list. While you can include the key handle, there's no way to pass the AppID. This means that the RP will never test if an authenticator responds to the legacy AppID, and thus the existing credential is effectively ignored.

In practice, this means you can make a second registration for the same token via WebAuthn. There's no apparent way to distinguish this registration server-side, leading to a confusing experience for users who accidentally re-register their authenticator. 

I'd love to see some way to support deconflicting existing U2F registrations during the WebAuthn credential creation process. As a simple first proposal, there could be an "appid" registration extension for use with the credential exclusion list only. The extension would provide no output. This supports the goal of not allowing new U2F registrations while also detecting existing U2F registrations correctly.

Would a process for migration to WebAuthn registrations also be appropriate?
 
I'd also like to echo the feedback about marshaling / unmarshalling ArrayBuffers being somewhat painful and unintuitive. Having all the data already encoded in U2F made for a very easy integration experience.

FWIW - In my implementation, I used a custom JSON replacer/reviver pair. I then could represent the buffers in transport as objects containing a type key set to urlsafe-base64-binary and a corresponding value.   

-DW

Adam Langley

unread,
Jun 17, 2019, 12:48:48 PM6/17/19
to Hans Nielsen, Security-dev, blink-dev
On Tue, Jun 11, 2019 at 9:11 AM 'Hans Nielsen' via Security-dev <securi...@chromium.org> wrote:
The "FIDO AppID Extension" (https://www.w3.org/TR/webauthn/#sctn-appid-extension) clearly calls out that the WebAuthn API will not create backwards-compatible registrations. Empirically, both Chrome and Firefox will reject "navigator.credentials.Create()" calls that specify this extension.

The problem is that we no longer have a way of putting existing U2F registrations on the "excludeCredentials" list. While you can include the key handle, there's no way to pass the AppID. This means that the RP will never test if an authenticator responds to the legacy AppID, and thus the existing credential is effectively ignored.

In practice, this means you can make a second registration for the same token via WebAuthn. There's no apparent way to distinguish this registration server-side, leading to a confusing experience for users who accidentally re-register their authenticator.

I'd love to see some way to support deconflicting existing U2F registrations during the WebAuthn credential creation process. As a simple first proposal, there could be an "appid" registration extension for use with the credential exclusion list only. The extension would provide no output. This supports the goal of not allowing new U2F registrations while also detecting existing U2F registrations correctly.

Thank you for that feedback. I raised it at the weekly W3C call and the issue is reflected at https://github.com/w3c/webauthn/issues/1235. If sites have input, there would be the best place to express it.

I think that a change to the spec is probably called for to address this and expect to propose something in the coming weeks.


Cheers

AGL
Reply all
Reply to author
Forward
0 new messages