corbado / flutter-passkeys

Easily provide passkey authentication based on FIDO2 / WebAuthn for Flutter apps (iOS & Android) via a dedicated Flutter package
https://www.corbado.com/passkeys/flutter
BSD 3-Clause "New" or "Revised" License
53 stars 16 forks source link

Can not register passkey on iOS Simulator with local RelyingPartyServer #44

Open grobarko opened 5 months ago

grobarko commented 5 months ago

Code:

final passkeyAuthenticator = PasskeyAuthenticator();

    // initiate sign up by calling the relying party server
    final webAuthnChallenge = await _dio.post(
        'http://localhost:19200/v1/auth/register/passkey/initiate',
        data: {'email': 'vlatko3@test.com'});

    // call the platform authenticator to register a new passkey on the device
    final platformRes = await passkeyAuthenticator.register(webAuthnChallenge); // <--- Exception is thrown here

Error:

PlatformException (PlatformException(decodingChallenge, CustomErrors, Stacktrace: ["0   passkeys_ios                        0x000000010d21d654 $s12passkeys_ios9wrapError33_F74ED722C198BB4137EE305712CCDE86LLySayypSgGypF + 1588", "1   passkeys_ios                        0x000000010d22357f $s12passkeys_ios16PasskeysApiSetupC5setUp15binaryMessenger3apiySo013FlutterBinaryI0_p_AA0cD0_pSgtFZyypSg_yAJctcfU0_ys6ResultOyAA16RegisterResponseVs5Error_pGcfU_ + 559", "2   passkeys_ios                        0x000000010d226f67 $s12passkeys_ios14PasskeysPluginC8register9challenge12relyingParty4user10completionySS_AA07RelyingH0VAA4UserVys6ResultOyAA16RegisterResponseVs5Error_pGctF + 1591", "3   passkeys_ios                        0x000000010d227ed1 $s12passkeys_ios14PasskeysPluginCAA0C3ApiA2aDP8register9challenge12relyingParty4user10completionySS_AA07RelyingI0VAA4UserVys6ResultOyAA16RegisterResponseVs5Error_pGctFTW + 113", "4   passkeys_ios                        0x000000010d2232e3 $s12passkeys_ios16PasskeysApiSetupC5setUp15binaryMessenger3apiySo013FlutterBinaryI0_p_AA0cD0_pSgtFZyypSg_yAJctcfU0_ + 1459", "5   passkeys_ios                        0x000000010d222c31 $sypSgAAIegn_Iegng_yXlSgABIeyBy_IeyByy_TR + 225", "6   Flutter                             0x00000001187f863d __48-[FlutterBasicMessageChannel setMessageHandler:]_block_invoke + 171", "7   Flutter                             0x00000001181b0222 ___ZN7flutter25PlatformMessageHandlerIos21HandlePlatformMessageENSt21_LIBCPP_ABI_NAMESPACE10unique_ptrINS_15PlatformMessageENS1_14default_deleteIS3_EEEE_block_invoke + 94", "8   libdispatch.dylib                   0x00007ff800156a90 _dispatch_call_block_and_release + 12", "9   libdispatch.dylib                   0x00007ff800157d3a _dispatch_client_callout + 8", "10  libdispatch.dylib                   0x00007ff800166ac0 _dispatch_main_queue_drain + 1420", "11  libdispatch.dylib                   0x00007ff800166526 _dispatch_main_queue_callback_4CF + 31", "12  CoreFoundation                      0x00007ff8003f5850 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9", "13  CoreFoundation                      0x00007ff8003f018b __CFRunLoopRun + 2463", "14  CoreFoundation                      0x00007ff8003ef409 CFRunLoopRunSpecific + 557", "15  GraphicsServices                    0x00007ff80fcdd187 GSEventRunModal + 137", "16  UIKitCore                           0x00007ff805b703a2 -[UIApplication _run] + 972", "17  UIKitCore                           0x00007ff805b74e10 UIApplicationMain + 123", "18  Runner                              0x000000010be632bf main + 63", "19  dyld                                0x000000010bf3c3ee start_sim + 10", "20  ???                                 0x00000001180763a6 0x0 + 4698104742"], null))

Some of the RP server data for registration options:

const options = await generateRegistrationOptions({
      "Vlatko",
      "localhost",
      userID: user.id,
      userName: user.email,
      // Don't prompt users for additional information about the authenticator
      // (Recommended for smoother UX)
      attestationType: "none",
      // See "Guiding use of authenticators via authenticatorSelection" below
      authenticatorSelection: {
        // Defaults
        residentKey: "preferred",
        userVerification: "preferred",
        // Optional
        authenticatorAttachment: "platform"
      },
    });
KiddoKodes commented 2 weeks ago

Hey, Having the same problem here. Is there any solution to this?

KiddoKodes commented 2 weeks ago

I found a solution. To anybody still having this issue it might be your register passkey options check if your user.id is base64 or base64URL (includes '_' or '-') and replace them with '/' and '+' respectively

File causing the error: passkeys_ios/ios/classes/PasskeysPlugin.swift

       guard let decodedChallenge = Data.fromBase64Url(challenge) else {
            completion(.failure(CustomErrors.decodingChallenge))
            return
        }

        guard let decodedUserId = Data.fromBase64(user.id) else {
            completion(.failure(CustomErrors.decodingChallenge))
            return
        }

Flutter code fixed:

RegisterRequestType(
          attestation: result.attestation,
          authSelectionType: AuthenticatorSelectionType(
              authenticatorAttachment: 'platform',
              requireResidentKey:
                  result.authenticatorSelection.requireResidentKey,
              residentKey: result.authenticatorSelection.residentKey,
              userVerification: result.authenticatorSelection.userVerification),
          challenge: result.challenge,
          excludeCredentials: result.excludeCredentials
              .map((e) =>
                  CredentialType(type: 'public-key', id: e.id, transports: []))
              .toList(),
          pubKeyCredParams: result.pubKeyCredParams
              .map((e) => PubKeyCredParamType(type: e.type, alg: e.alg))
              .toList(),
          relyingParty:
              RelyingPartyType(id: result.rp.id, name: result.rp.name),
          timeout: result.timeout,
          user: UserType(
              displayName: result.user.name,
              id: result.user.id.replaceAll("-", "+").replaceAll("_", "/"), // this is the fix
              name: result.user.name));