f-23 / react-native-passkey

Passkeys for React Native
MIT License
121 stars 25 forks source link

Base64 JWT #20

Closed SDAChess closed 6 months ago

SDAChess commented 9 months ago

Hello,

It seems that the library is transforming the base64Url into a base64. I wonder why? Since most data are JWTs, this makes them incorrect and doesn't work with some authentication systems.

Could there be an option to get the base64url content?

f-23 commented 8 months ago

You are right, iOS uses base64, Android uses base64Url. Under the hood the challenge is being converted to base64Url on Android anyways. It should be the other way around, taking base64Url in and converting it to base64 for iOS.

I'll put it on my todo.

espenhogbakk commented 8 months ago

@f-23 i've just started implemented Passkeys on iOS using this package and I've stumbled into a problem that relates to this issue i believe.

On the server side I'm using SimpleWebAuthn and it returns a challenge encoded using Base64Url supposedly, and looking at the code i certainly looks like it does.

Here is an example of the resulting registration options from my server:

{
  "challenge": "2WvTHQ8DYhZWMLbKdIguWhWiS64EDx5Hzr5gqfi89ss",
  "rp": {
    "name": "Rp Name",
    "id": "example.com"
  },
  "user": {
    "id": "a3faeb24-1772-4146-9c01-bc174422ba49",
    "name": "John Doe",
    "displayName": "John Doe"
  },
  "pubKeyCredParams": [
    {
      "alg": -7,
      "type": "public-key"
    },
    {
      "alg": -257,
      "type": "public-key"
    }
  ],
  "timeout": 60000,
  "attestation": "none",
  "excludeCredentials": [],
  "authenticatorSelection": {
    "residentKey": "required",
    "requireResidentKey": true
  },
  "extensions": {
    "credProps": true
  }
}

When i use the SimpleWebAuthn accompanying startRegistration method it works fine, and i have a working solution for using passkeys on the website.

But when i pass the same registration options into react-native-passkey's Passkey.register method I get the following error: { error: 'InvalidChallenge', message: 'The provided challenge was invalid' }.

After some debugging it seems that error originates from this bit of code:

guard let challengeData: Data = Data(base64Encoded: challenge) else {
      reject(PassKeyError.invalidChallenge.rawValue, PassKeyError.invalidChallenge.rawValue, nil);
      return;
    }

And after looking a bit in the code of this library i don't see anywhere (but i might be missing it of course) where it converts the base64Url encoded challenge into base64 before it tries to create a Swift Data object, which i guess then would fail...

Am i doing something wrong, or is this is this a bug with this library? I could try to tackle the bug myself, but just want to confirm i'm on the right track.

f-23 commented 6 months ago

Hi @espenhogbakk

sorry for the late response, did you manage to find a solution? The challenge should not be converted to base64 when passing it in the request on iOS.

espenhogbakk commented 6 months ago

Hi @espenhogbakk

sorry for the late response, did you manage to find a solution? The challenge should not be converted to base64 when passing it in the request on iOS.

Yeah I figured it out, I don't remember exactly what i did now, but seem to remember the problem was that the SimpleWebAuthn package always uses Base64URL, and if i understand it correctly the WebAuthn spesification also says to do so, but it seems Apple does not want Base64URL just Base64 in their passkeys implementation.

So i had to write some code on the server that checked if the client was an Apple iOS device, if so converted the Base64URL to Base64 and the other way around.