Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom handler for expectedOrigin and expectedRPID #102

Closed
Mikescops opened this issue Feb 15, 2021 · 11 comments
Closed

Custom handler for expectedOrigin and expectedRPID #102

Mikescops opened this issue Feb 15, 2021 · 11 comments

Comments

@Mikescops
Copy link
Contributor

Mikescops commented Feb 15, 2021

Hello,

As a followup of #90, when using a browser extension, having multiple origin/rpID possible was really cool but we got into facing another issue which is that some browsers like Firefox are using random IDs for their extensions that means it's impossible to maintain a hard-coded list of origins/rpID.

For instance here is the way to validate a FF origin:

    const originIsMozillaExtension = origin.match(
        /^moz-extension:\/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
    );

I know it's yet another addition but would you allow to have a custom function handler to validate origins and rpIDs?
Let me know if you have a better solution but I guess anyone using SWA with browser extension will face this issue.

@MasterKale
Copy link
Owner

MasterKale commented Feb 15, 2021

After bootstrapping a new extension using Mozilla's "baby's first extension" tutorial I tried to find out if it's possible for an extension to discover its internal UUID. It seems it is indeed possible to get the current UUID via the runtime.getURL() API:

const imgURL = browser.runtime.getURL('manifest.json');
const idRegEx = /^moz-extension:\/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;

console.log(`extension UUID: ${idRegEx.exec(imgURL)}`);

Screen Shot 2021-02-15 at 8 10 38 AM

Which matches with the UUID that's visible from the extensions page:

Screen Shot 2021-02-15 at 8 03 27 AM

Can you use this technique to get a given instance's UUID string, which you can then pass in as rpID for options or expectedRPID for verification?

Edit: Have you figured out what the RP ID is that comes out of an attestation that takes place in a browser extension?

@Mikescops
Copy link
Contributor Author

So we've made some tests and we found out that the RP ID is similar to the origin. See this assertion in the SWA debugger here.

If you take the rpID hash it's "e71bb73760cf488dcef0732f92017e7f05f2d92b99dc76b7b604a517ebb60793"

Now trying to found this hash from the uuid:

> a = crypto.createHash('SHA256').update(Buffer.from('gnmebdeegofghllmmlngmdmoghkebpmf', 'ascii')).digest(); console.log(a.toString('hex'));
372c27c61b49497b4dd746f02ce598d4d07efe605858c05d578aae148bc3ed83

> a = crypto.createHash('SHA256').update(Buffer.from('chrome-extension://gnmebdeegofghllmmlngmdmoghkebpmf', 'ascii')).digest(); console.log(a.toString('hex'));
e71bb73760cf488dcef0732f92017e7f05f2d92b99dc76b7b604a517ebb60793

So in this example the RPID === origin. But what's interesting is that the parameter passed to webauthn.create was only "gnmebdeegofghllmmlngmdmoghkebpmf". So that means something changed it on it's way.

And here is a interesting PR on webauthn project: w3c/webauthn#1033 that may explain this behavior.

We also tried to pass different things to the webauthn.create but ended up with DOMException: Public-key credentials are only available to HTTPS origin or HTTP origins that fall under 'localhost'..

Concerning the fact that we can get the uuid with runtime.getURL() it's known. But the issue is that the server is not aware of it as we put our expectedOrigin/RPID in our configuration.

I hope this helps but to be honest there is a big lack of documentation around webauthn and extensions 😞

@MasterKale
Copy link
Owner

MasterKale commented Feb 16, 2021

I suspect you're suffering from nuances of the CTAP2 protocol, the part of WebAuthn that dictates how the authenticator generates a response for whatever options the browser gives it.

It seems for Chrome specifically you need to pass in the full extension URI for expectedRPID, not just the extension's ID...this isn't the case with Firefox? Is there the added twist that the rpIdHash in Firefox extensions is generated from just the ID as expected?

I feel like this isn't related to WebAuthn extensions...it might save you some headache to table that investigation for now. I'm going to look at the CTAP2 part of the spec a bit closer and see if anything in there explains this. Maybe it's a browser implementation issue 🤷‍♂️

@MasterKale
Copy link
Owner

MasterKale commented Feb 16, 2021

I found this part in the spec:

The RP ID is originally received from the client when the credential is created, and again when an assertion is generated. However, it differs from other client data in some important ways. First, unlike the client data, the RP ID of a credential does not change between operations but instead remains the same for the lifetime of that credential. Secondly, it is validated by the authenticator during the authenticatorGetAssertion operation, by verifying that the RP ID that the requested credential is scoped to exactly matches the RP ID supplied by the client.

Authenticators perform the following steps to generate an authenticator data structure:

  • Hash RP ID using SHA-256 to generate the rpIdHash.

Now I'm suspecting Chrome itself will end up being the culprit, as it's responsible for passing along your rpID in the options you provide to its navigator.credential APIs. If it's tacking on chrome-extension:// to the front of the value you provided then that doesn't seem compliant with the spec.

It's worth noting you're in a bit of uncharted waters with all of this because I don't think the spec particularly accounts for the use of this API in browser extensions.

@Mikescops
Copy link
Contributor Author

So I made a series of tests.

For Chrome:

  • Don't work when a protocol different than https is specified.

  • Works only with rpID set with the ID of extension (like nhnjjlokanokjhheekkcgoiblhljealo) and then in the attestation response it was modified (by the browser? 🤷‍♂️ ) to chrome-extension://nhnjjlokanokjhheekkcgoiblhljealo.
    See example here.

For Firefox:

  • Don't work when a protocol different than https is specified.

  • When setting the rpID to the UUID, the ui is triggered but I was unable to validate my Yubikey nothing was happening until timeout.
    image
    So I don't know if it works at all on Firefox or if I missed something.

@Mikescops
Copy link
Contributor Author

I think webextensions fits in this part of the spec:

Other specifications mimicking the WebAuthn API to enable WebAuthn public key credentials on non-Web platforms (e.g. native mobile applications), MAY define different rules for binding a caller to a Relying Party Identifier. Though, the RP ID syntaxes MUST conform to either valid domain strings or URIs [RFC3986] [URL].

https://www.w3.org/TR/webauthn/#note-pkcredscope

@Mikescops
Copy link
Contributor Author

So here is a reply from Google team:

RP IDs are defined to be a domain name, but extensions don't have a domain name. In order to avoid collisions with domain names, different identifier spaces are distinguished by representing them as URLs. See this note in the spec: https://www.w3.org/TR/webauthn-2/#ref-for-webauthn-client%E2%91%A4:~:text=Other%20specifications%20mimicking%20the%20WebAuthn%20API%20to%20enable%20WebAuthn
Chrome extensions are not intended to specify an RP ID in the WebAuthn call at all although it does happen to work if you specify the extension ID.

@Mikescops
Copy link
Contributor Author

Hello again,
I have opened a discussion with Mozilla: https://bugzilla.mozilla.org/show_bug.cgi?id=1693562
There are interesting elements to discuss.

Also I created a test extension: https://github.com/Mikescops/webauthn-extension-playground

@MasterKale
Copy link
Owner

@Mikescops Thank you for the update! Seeing WebAuthn in action in a browser extension is pretty exciting for me, even with Firefox giving you issues (and I totally approve of how you linked to the SimpleWebAuthn debugger 😛). I'll be following that Mozilla bugzilla entry you linked, here's hoping for a speedy resolution 🤞

@Mikescops
Copy link
Contributor Author

Mikescops commented Feb 23, 2021

The debugger is a rich idea you had 👍 hopes we make webauthn a great standard everywhere 😄

@MasterKale
Copy link
Owner

I'm going to go ahead and close this as there's not much in the way of action items for me. I'll take another look at this in the morning and maybe migrate it to a discussion so we don't lose the links you provided earlier to the Closed Issues tab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants