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

Uint8Array --> String --> Uint8Array Conversion from isoUint8Array helper doesn't work #509

Closed
seba9999 opened this issue Jan 21, 2024 · 3 comments

Comments

@seba9999
Copy link

seba9999 commented Jan 21, 2024

Describe the issue

I'm trying to convert the credentialID (Uint8Array) to an UTF-8 string in order to save it in my MongoDB Database.
I've tried multiple packages to do so ... Until I found out that there's some helpers into this library itself ...

But the flow : Uint8Array --> String --> Uint8Array seems ... broken ?
Am I missing something ?

Reproduction Steps

const { isoUint8Array } = require('@simplewebauthn/server/helpers');

------

let verification;
try {
  verification = await verifyRegistrationResponse({
    response: challengeResponse,
    expectedChallenge: user.challenge,
    expectedOrigin: origin,
    expectedRPID: rpID,
  });
  if (verification.verified) {
    // Save authenticator :
    console.log('Generated CredentialID = ', verification.registrationInfo.credentialID);
    const stringifiedCredentialID = isoUint8Array.toUTF8String(
      verification.registrationInfo.credentialID
    );
    console.log('stringifiedCredentialID = ', stringifiedCredentialID);
    const backToUInt8Array = isoUint8Array.fromUTF8String(stringifiedCredentialID);
    console.log('textEncodedCredentialID = ', backToUInt8Array );
    
    const auth = new Authenticator({
      ...verification.registrationInfo,
      credentialID: stringifiedCredentialID,
      credentialPublicKey: '' // @todo,
      transports: challengeResponse?.response?.transports,
    });
    auth.userId = user._id;
    await auth.save();
  }
  res.status(200).json({ ok: verification.verified });
} catch (error) {
  console.error(error);
  return res.status(400).send({ message: error.message });
}

And here is the result :
image

Expected behavior

The converted back Uint8Array should be the same ? Right ? And the String must be UTF-8 no ?

Dependencies

  • OS: Windows
  • Browser: Chrome 120
  • Authenticator: Windows Hello
$ npm list --depth=0 | grep @simplewebauthn
├── @simplewebauthn/browser@8.3.4
├── @simplewebauthn/server@8.3.6
# ...
@seba9999
Copy link
Author

seba9999 commented Jan 21, 2024

I've noticed that it's working with the isoUint8Array.toHex helper :

// Saving :
const hexCredentialID = isoUint8Array.toHex(verification.registrationInfo.credentialID);
const hexCredentialPublicKey = isoUint8Array.toHex(verification.registrationInfo.credentialPublicKey);

// Authentification Option : 
const previousAuthenticator = await Authenticator.findOne({ userId: user._id });
const credentialIDbackFromHex = isoUint8Array.fromHex(previousAuthenticator.credentialID);
console.log('credentialIDbackFromHex = ', credentialIDbackFromHex ); // The "restored" uInt8Array is the same !

const options = await generateAuthenticationOptions({
  rpID,
  allowCredentials: [
    {
      id: credentialIDbackFromHex ,
      type: 'public-key',
      transports: previousAuthenticator.transports,
    },
  ],
  userVerification: 'preferred',
});

I can then trigger the windows hello prompt successfully but then on the verify part I get this error :
Error: No packed values available :

image

Verify part code :

const previousAuthenticator = await Authenticator.findOne({ userId: user?._id });
try {
  const backFromHexCredentialID = isoUint8Array.fromHex(previousAuthenticator.credentialID);
  const backFromHexPublicKey = isoUint8Array.fromHex(previousAuthenticator.credentialID);

  verification = await verifyAuthenticationResponse({
    response: challengeResponse,
    expectedChallenge: user?.challenge,
    expectedOrigin: origin,
    expectedRPID: rpID,
    authenticator: {
      ...previousAuthenticator,
      credentialID: backFromHexCredentialID,
      credentialPublicKey: backFromHexPublicKey,
    },
    requireUserVerification: true,
  });
  res.status(200).json({ ok: verification.verified });
} catch (error) {
  console.error(error);
  return res.status(400).send({ message: error.message });
}

@MasterKale
Copy link
Owner

Credential ID bytes are effectively random so I'm not at all surprised they're not encoding to readable UTF-8. I recommend using the isoBase64URL helper instead, its isoBase64URL.fromBuffer() and isoBase64URL.toBuffer() are better at encoding these bytes into strings (though isoUint8Array.toHex() and isoUint8Array.fromHex() are fine too, though its seemingly unorthodox to me because so much of WebAuthn uses base64url encoding.)

can then trigger the windows hello prompt successfully but then on the verify part I get this error :
Error: No packed values available

Looking at your code sample I think I see the problem:

const backFromHexPublicKey = isoUint8Array.fromHex(previousAuthenticator.credentialID);

previousAuthenticator.credentialID should probably be previousAuthenticator.credentialPublicKey (or whatever it's called on your end)...

@seba9999
Copy link
Author

O.M.G

I can't believe this !
I've changed credentialID to credentialPublicKey and it works ... 💀

Too much copy / paste kills copy / paste 😢

Sorry to made you loose your time ! And a big thank you ! ♥

At least I've learned that I should probably use isoBase64URL.fromBuffer() / isoBase64URL.toBuffer() 😅

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