WebAuthn across multiple subdomain
Asked Answered
C

1

8

I'm trying to set up a WebAuthn authentication flow on my website, but I'm bumping into an issue. I want my users to be able to register their devices on the main website (www.domain.com) so it's easily accessible through their user settings. Authentication itself happens on a different subdomain through an IdP (sso.domain.com). This is where trouble began.

There's a couple of things I've tried:

  • Registration and authentication on www.scoutswechel.be, passing 'www.scoutswechel.be' as the rp.id for both => works.
  • Registration on www.scoutswechel.be, authentication on sso.scoutswechel.be, passing 'www.scoutswechel.be' as the rp.id for both => the authentication step fails as authenticator doesn't return any keys.
  • Registration on www.scoutswechel.be, authentication on sso.scoutswechel, passing those domains respectively as the rp.id => the authentication step fails as authenticator doesn't return any keys.
  • Registration on www.scoutswechel.be, authentication on sso.scoutswechel.be, passing 'scoutswechel.be' as rp.id for both => registration step already fails with differing errors for different browsers (eg 'key returned something unexpected (2)' in chrome).

If I understand the spec correctly, passing 'scoutswechel.be' should actually work as both domains are subdomains of the main domain (correct me if I'm wrong here).

I prepare my challenges with PHP and pass them to the page with ajax calls. My PHP scripts returns for example following value:

{
   "publicKey":{
      "challenge":[105,107,101,103,105,49,119,115,98,108,119,109,48,109,105,53],
      "user":{
         "name":"walter",
         "displayName":"Wouter Henderickx",
         "id":[49,48,51]
      },
      "rp":{
         "id":"scoutswechel.be",
         "name":"scoutswechel.be"
      },
      "pubKeyCredParams":[
         {
            "alg":-7,
            "type":"public-key"
         }
      ],
      "authenticatorSelection":{
         "authenticatorAttachment":"cross-platform",
         "requireResidentKey":false,
         "userVerification":"preferred"
      },
      "attestation":null,
      "timeout":60000,
      "excludeCredentials":[],
      "extensions":{
         "exts":true
      }
   },
   "b64challenge":"aWtlZ2kxd3NibHdtMG1pNQ"
}

I then convert the challenge and user.id to Uint8Array and pass the publickey part to:

navigator.credentials.create({publicKey: key.publicKey}).
	then(function (aNewCredentialInfo) {
		do.stuff()
	})

How should I go about this? What values can I pass to the rp.id to get this working across both subdomains?

Charley answered 10/3, 2019 at 17:48 Comment(4)
Yes, scoutswechel.be should be the RP ID you need to choose so the scope of the credential you create during registration can be used during authentication also.Misfortune
I re-read the spec and you're indeed right: if I register my credentials with scoutswechel.be as the RP ID, it can be used on all of it's subdomains. The problem is that I want to perform the registration on www.scoutswechel.be, and the registration will fail because the RP ID should be equal to the domain in the browser or be a subdomain of what's in the browser. The workaround I'll be trying now is to perform the actual registration on sso.scoutswechel.be as well (by using simplesaml). I'll keep this question updated with my feedback.Charley
RP ID does not necessarily need to be equal to the domain. It can be overridden to be a registrable domain suffix, which is what you need to do in your case. Reference: w3.org/TR/webauthn/#relying-party-identifier.Misfortune
That makes sense from a pure spec standpoint, but my implementation is still not working. You can check this out if you want on scoutswechel.be/webauthn/register. The console will log the challenge I'm passing and the error you get from the credentials.create function.Charley
C
2

In the end, I figured it out. These were all implementation issues on my end:

  • In the library I based my project of (https://github.com/davidearl/webauthn), there was a piece of javascript that compared the value of the rp.name to the origin. Since I was passing a non-domain name, this check was blocking further execution.
  • I was passing 'rpid' for the relying party identifier. However, this should have been 'rpId'. Since my authenticator couldn't find any credentials without an rpid, it didn't return anything and I wasn't authenticated.
Charley answered 27/5, 2019 at 10:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.