Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

U2F API is being deprecated #947

Closed
f0zi opened this issue Oct 21, 2021 · 32 comments · Fixed by #1013
Closed

U2F API is being deprecated #947

f0zi opened this issue Oct 21, 2021 · 32 comments · Fixed by #1013

Comments

@f0zi
Copy link

f0zi commented Oct 21, 2021

Google Chrome is starting to deprecate the U2F API and is asking developers to use WebAuthn instead.

U2F devices are still supported through WebAuthn and existing U2F device credentials can be challenged through the WebAuthn API.

Also note that

Edge is (very) supportive of this deprecation/removal.

Details:
https://groups.google.com/a/chromium.org/g/blink-dev/c/xHC3AtU_65A?pli=1
https://www.yubico.com/blog/google-chrome-u2f-api-decommission-what-the-change-means-for-your-users-and-how-to-prepare/

@z3ntu
Copy link

z3ntu commented Nov 28, 2021

Chromium is displaying this message in v96:

image

@ChristophWurst
Copy link
Member

Credentials that were originally registered via the U2F API can be challenged via WebAuthn. USB security keys that are supported by the U2F API are also supported by the WebAuthn API.

so this app doesn't have to die yet, we just have to change the way we access the devices if I understand correctly.

@ChristophWurst
Copy link
Member

we'll have to see if it makes more sense to migrate from webauthn to u2f here or invest into webauthn as full first and second authentication method as per nextcloud/server#21215 and deprecate this app entirely

@My1
Copy link

My1 commented Dec 3, 2021

I think having WebAuthn in U2F style as second factor still has some use especially considering external storage kinda breaks down on passwordless if it's set to use the user's credentuals.

the good thing is that migrating credentials really doesnt seem to be hard.

so for example while I use this block (simplified) on U2F (using Yubico's JS lib):

req=[
    {
        "version": "U2F_V2",
        "challenge": "B36vTVQfpvspDnNGOVAPaTQAusU_jTVdX4uxoWMwbDo",
        "keyHandle": "2Yc13KwhGcZ2eD3Sj9n99MyVmswB-SKopJfv8FefyKC3cG9WZrrIINhMWumg1QoznRUTSKdgz7o7aI03v-oxWA",
        "appId": "https:\/\/my1.dev"
    }
];

  // Getting AppID
  var appid = req[0].appId;
  // Getting Challenge
  var challenge = req[0].challenge;
u2f.sign(appid,challenge,req, function(data)

yeah it's not pretty but I had made this in a sandbox project, whatever requesting something from the same credential over Webauthn goes a little like this (also simplified):

args= {
    "publicKey": {
        "timeout": 180000,
        "challenge": "?BINARY?B?uCQzdDRiKWE2dP9ZeVf1baq8YPc5KXRgBEjIEB+MpHQ=?=",
        "userVerification": "discouraged",
        "rpId": "my1.dev",
        "allowCredentials": [
            {
                "id": "?BINARY?B?2Yc13KwhGcZ2eD3Sj9n99MyVmswB+SKopJfv8FefyKC3cG9WZrrIINhMWumg1QoznRUTSKdgz7o7aI03v+oxWA==?=",
                "transports": [
                    "usb",
                    "nfc",
                    "ble",
                    "internal"
                ],
                "type": "public-key"
            }
        ],
        "extensions": {
            "appid": "https:\/\/my1.dev"
        }
    }
}

recursively_inplace_decode_binary_marked_b64_strings(args);
navigator.credentials.get(args)
.then(...)

?BINARY?B? basically means "this is binary, decode this" because you obviously cannot really throw binary into JS code, and the function basically goes through the argument object and decodes everything it finds with that marker on.

there are 3 things to note:

  1. you have to supply the appid which can be different from the rpID, but in simple setups the appid just has "https://" tacked on.
  2. the keyhandle gets suppled as the credential ID but base64-decoded.
  3. this works no matter whether the credential has been registered with old U2F or Webauthn, as long as both the RP-ID as well as the appid are valid for the given site.

as backend any decent webauthn backend should work but might need accomodation for the fact that the App-ID obviously changes the hash of the "site" that has been signed for. basically you need to check if the clientdatajson->extensions has the appid active and if yes basically make sure it doesnt check for the rpid hash but the appid hash. I on my simple sandbox setup basically just replace the rpidhash with sha256(clientdata->origin), as it already has been checked as a valid origin in the lib I use

@grueneedv
Copy link

I think having WebAuthn in U2F style as second factor still has some use especially considering external storage kinda breaks down on passwordless if it's set to use the user's credentuals.

+1 here with this (a Vote to continue this Plugin just using WebAuthn instead of U2F). We use external storage which breaks using NC WebAuthn auth.

Also when you configure WebAuthn from Nextcloud and have 2nd Factor forced for Users through the twofactor Plugin you first authenticate using WebAuthn from NC and then again through the twofactor module with the Security Key.

Simply rely on Nextcloud only gives you not the possibility to enforce 2nd Factor and kinda breaks the ability to give TOTP as alternative 2nd Factor or backup codes.

Looking at the current implementation (version 22) of NC WebAuthn only (as already mentionend in the Issue from NC) you enter the Username and use the Security Key. This is paswordless Login but not really a second Factor in my opinion.

@My1
Copy link

My1 commented Jan 12, 2022

Also when you configure WebAuthn from Nextcloud and have 2nd Factor forced for Users through the twofactor Plugin you first authenticate using WebAuthn from NC and then again through the twofactor module with the Security Key.

that's because they actually have set the UV method to discouraged so basically U2F-style but without password, which is a known issue. so as long as the sticks arent enforced to use UV (pin, biometrics or whatever to verify the user is the one intended) the second factor is sadly kinda needed

@st3iny
Copy link
Member

st3iny commented Feb 1, 2022

I'm currently working on fixing this issue. However, I might need some technical advice.

@My1 Let me ask you a question, as you seem to have some deep insights into Webauthn and U2F:

Let's pretend I implement a feature that lets users migrate to Webauthn seamlessly. Do I need access to the U2F api for that? If so, it would result in bad UX because all users would have to migrate very soon. Or is it sufficient to use only the Webauthn API for that?

If there is a need to access the U2F api for the migration, it would make more sense to completely deprecate twofactor_u2f and implement a new app twofactor_webauthn.

@My1
Copy link

My1 commented Feb 1, 2022

the U2F API should not be needed anymore.

In Fact I have a U2F frontend which have the yubico JS and PHP libs for the script and backend processing, as well as a WebAuthn side which is made with a modified copy of lbuchs/Webauthn as a backend (as that thing was obviously not built with U2F backwards compat in mind so I fiddled around, quite a bit), with a little js which is based on his sample, and both use a PHP script I threw on for the storage management and stuff.

talking about storage here you might hit a snag, not sure if extensions on nextcloud are more or less isolated and especially if the data they make is just removed when uninstalling the extension, so maybe keep it running as twofactor_u2f but make it use webauthn.

https://my1.dev/u2f/my1.php -> register and try to login (or not) here using u2f
https://my1.dev/wa-u2f/my1.php -> use webauthn to sign in using these credentials.

you just need to know/do a few things (non-exhaustive, I only wanted it to get running, this is a sandbox, not alcatraz):

  1. the original "appID" used with U2F and throw the option parameter into the request both for signing in (because obvious) and on registration as the "excludeAppId" or whatever it was called to make sure devices dont get double-registered
  2. with the yubico lib you get the keyhandle directly back as base64url, the lib I use uses binary for webauthn credentialIDs (functionally identical to the keyhandle), so in this case I need to have a base64_url decode handy, check your library and storage stuff and test well.
  3. check the storage format for the public key used in both libs and convert if needed, I needed to throw on some special header (and assume we were talking ECDSA) as the lib was using a minified form of the key (which was something you actually need to find out in the first place.
  4. on login, you need to handle the client data's appid marker, which should say true if the appid was used and thereby use the hash of the appid rather than the rpID's hash.
  5. unrelated to U2F specifics I would recommend to use non-internal authenticators only for registration, especially because windows is a pain and doesnt tell you that you can use a USB device, you have to cancel the WinHello-based prompt to get one that allows external devices
  6. also unrelated to U2F but I'd also recommend setting uv to "discouraged" so clients dont ask for a PIN, especially windows is super pedantic that it even asks the user to set a pin, even if the default preferred offers practically no security in most scenarios (I wrote about that)

and the rest should be doable by any decent webauthn implementation.

@ChristophWurst
Copy link
Member

twofactor_webauthn already exists: https://github.com/michib/nextcloud_twofactor_webauthn

@reimer-atb
Copy link

@ChristophWurst what's the plan for this now?

@ChristophWurst
Copy link
Member

@st3iny is having a deeper look. We'll update this ticket when we know more.

@reimer-atb
Copy link

That seems a bit late considering that this app is basically broken now for 90% of all users.

Maybe add a prominent warning to the README and the NC APP store?

@ChristophWurst
Copy link
Member

I'm doing what's in my power to resolve this.

The warning would not be read by many nor does it help anyone who's now logged out because of this.

@st3iny
Copy link
Member

st3iny commented Feb 4, 2022

That seems a bit late considering that this app is basically broken now for 90% of all users.

The API hast not yet been removed. For now, it has just been disabled. If required, the API can be enabled again at chrome://flags/#u2f-security-key-api.

@My1
Copy link

My1 commented Feb 4, 2022

wait the API has been moved to flag-level disabled? wow.

@ChristophWurst
Copy link
Member

yes, I've been mislead with their warning. U2F doesn't stop after February, it stops working with the beginning of February …

@My1
Copy link

My1 commented Feb 4, 2022

ouch. if you need any help with implementing Webauthn with U2F creds hit me up and maybe I can help, I dont think I can help with specific programming since it'll likely fail on every test about coding style and whatnot but I think I might be able to assist otherwise.

@ChristophWurst
Copy link
Member

Thank you very much @My1!

Based on the information above and what @st3iny researched in addition we can bring over u2f registration to webauthn, so they continue to work in Chrome. Other browsers, like Firefox, seem to not be effected yet. Nevertheless there might come a point where they also drop this API due to the superseded webauthn standard and it would be cumbersome to support both standards simultaneously.

The app @michib maintains seems to work nicely and it's quite familiar to the current twofactor_u2f due to their common code history.

As we understand, there are two ways we can go

  1. Continue in the existing twofactor_webauthn app and add a migration there that moves over all twofactor_u2f registrations. The migration is not seamless and requires admins to read app readme/description or any other warning that we place somewhere else. I think ticket twofactor_u2f compatibility twofactor_webauthn#6 was meant to tackle this.
  2. Replace the internals of this app to use webauthn instead of u2f, while keeping the app name/ID. Practically that would mean we'd possibly borrow back some code from @michib into this app. The migration would be transparent for the admin. The downside is that we'd have two apps with more or less the same code.

I will have to clarify internally. Nextcloud Gmbh also supports this app for customers. On that note, if you are a customer please drop us a ticket in the support system so we know you are affected.

I'm sorry for any inconveniences caused.

@My1
Copy link

My1 commented Mar 7, 2022

so I guess it cannot just take the old db entries if an actual migration process is needed...

tho kinda sad you need occ to do it as not all have easy access to occ.

@ChristophWurst
Copy link
Member

the entries are rewritten to the new format. we can't do this in a way where we guarantee this won't time out for the raspberry pi users and their 30s max request times, so I rather to the safe path and only offer things that we tested and now that work.

@z3ntu
Copy link

z3ntu commented Mar 7, 2022

Migration worked flawlessly on my personal instance, thanks for making this work!

Also maybe worth adding that installing the app "Two-Factor WebAuthn" (obviously) needs to be done before attempting the migration.

@Marvo2011
Copy link

@My1
Copy link

My1 commented Mar 9, 2022

the entries are rewritten to the new format. we can't do this in a way where we guarantee this won't time out for the raspberry pi users and their 30s max request times, so I rather to the safe path and only offer things that we tested and now that work.

couldnt one do a set of migrations that's practically guaranteed to work within the time (e.g. 10) and then just trigger a 302 or whatever to the next set which has its own timeout? or whatever, the nextcloud updater can also work over web despite not nesecarily being done in 30 sec

@ChristophWurst
Copy link
Member

In theory, yes. Right now migrations happen synchronously in one batch, though.

@nursoda
Copy link

nursoda commented Apr 29, 2022

So the full flow to migrate from twofactor_u2f to twofactor_webauthn is (for each cloud)

php occ app:install twofactor_webauthn
php occ twofactor_webauthn:migrate-u2f -nvv --all
php occ app:disable twofactor_u2f
php occ twofactorauth:cleanup u2f
php occ app:remove twofactor_u2f

That worked fast and smooth in my NC24RC3 instances.

It displayed "Migrating devices of user XY" also for users that never hat a U2F device, but there already is an issue for that and it's cosmetics ;)

In contrast to twofactor_u2f, twofactor_webauthn displays a button (on the 2FA login screen) and waits for the user to plug in the device and press that button. So this is one additional click for a user. Apart from that, I see no difference.

Thanks to all!

@rickgardner077
Copy link

So the full flow to migrate from twofactor_u2f to twofactor_webauthn is (for each cloud)

php occ app:install twofactor_webauthn
php occ twofactor_webauthn:migrate-u2f -nvv --all
php occ app:disable twofactor_u2f
php occ twofactorauth:cleanup u2f
php occ app:remove twofactor_u2f

That worked fast and smooth in my NC24RC3 instances.

It displayed "Migrating devices of user XY" also for users that never hat a U2F device, but there already is an issue for that and it's cosmetics ;)

In contrast to twofactor_u2f, twofactor_webauthn displays a button (on the 2FA login screen) and waits for the user to plug in the device and press that button. So this is one additional click for a user. Apart from that, I see no difference.

Thanks to all!

Migration worked flawlessly in my personal instance for 7 users

@svenseeberg
Copy link

svenseeberg commented May 12, 2022

So the full flow to migrate from twofactor_u2f to twofactor_webauthn is (for each cloud)

php occ app:install twofactor_webauthn
php occ twofactor_webauthn:migrate-u2f -nvv --all
php occ app:disable twofactor_u2f
php occ twofactorauth:cleanup u2f
php occ app:remove twofactor_u2f

That worked fast and smooth in my NC24RC3 instances.

It displayed "Migrating devices of user XY" also for users that never hat a U2F device, but there already is an issue for that and it's cosmetics ;)

In contrast to twofactor_u2f, twofactor_webauthn displays a button (on the 2FA login screen) and waits for the user to plug in the device and press that button. So this is one additional click for a user. Apart from that, I see no difference.

Thanks to all!

It works mostly well. We have some problems though: all devices are now registered twice (or in one case 3 times):

image

Also, users cannot delete their old U2F devices and/or register them again. I guess this is due to the migration. I can provide database excerpts, if that helps.

This is the stack trace when trying to remove a migrated U2F device:

{"reqId":"kSIaG1BhUOReuR1laeDo","level":3,"time":"2022-05-12T07:37:51+00:00","remoteAddr":"REMOTE_IP","user":"USERID","app":"index","method":"POST","url":"/index.php/apps/twofactor_webauthn/settings/remove","message":"Call to a member function getUserHandle() on null in file '/var/www/apps/twofactor_webauthn/lib/Service/WebAuthnManager.php' line 362","userAgent":"Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0","version":"24.0.0.12","exception":{"Exception":"Exception","Message":"Call to a member function getUserHandle() on null in file '/var/www/apps/twofactor_webauthn/lib/Service/WebAuthnManager.php' line 362","Code":0,"Trace":[{"file":"/var/www/lib/private/AppFramework/App.php","line":172,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->"},{"file":"/var/www/lib/private/Route/Router.php","line":298,"function":"main","class":"OC\\AppFramework\\App","type":"::"},{"file":"/var/www/lib/base.php","line":1023,"function":"match","class":"OC\\Route\\Router","type":"->"},{"file":"/var/www/index.php","line":36,"function":"handleRequest","class":"OC","type":"::"}],"File":"/var/www/lib/private/AppFramework/Http/Dispatcher.php","Line":165,"Previous":{"Exception":"Error","Message":"Call to a member function getUserHandle() on null","Code":0,"Trace":[{"file":"/var/www/apps/twofactor_webauthn/lib/Controller/SettingsController.php","line":82,"function":"removeDevice","class":"OCA\\TwoFactorWebauthn\\Service\\WebAuthnManager","type":"->"},{"file":"/var/www/lib/private/AppFramework/Http/Dispatcher.php","line":225,"function":"remove","class":"OCA\\TwoFactorWebauthn\\Controller\\SettingsController","type":"->"},{"file":"/var/www/lib/private/AppFramework/Http/Dispatcher.php","line":133,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->"},{"file":"/var/www/lib/private/AppFramework/App.php","line":172,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->"},{"file":"/var/www/lib/private/Route/Router.php","line":298,"function":"main","class":"OC\\AppFramework\\App","type":"::"},{"file":"/var/www/lib/base.php","line":1023,"function":"match","class":"OC\\Route\\Router","type":"->"},{"file":"/var/www/index.php","line":36,"function":"handleRequest","class":"OC","type":"::"}],"File":"/var/www/apps/twofactor_webauthn/lib/Service/WebAuthnManager.php","Line":362},"CustomMessage":"--"}}

@ChristophWurst
Copy link
Member

@svenseeberg I think your problem has also been reported at nextcloud/twofactor_webauthn#176

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Development

Successfully merging a pull request may close this issue.