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

AuthenticatorAttestationRawResponse attestationResponse getting null. #338

Closed
anandsb84 opened this issue Nov 9, 2022 · 13 comments · Fixed by #450
Closed

AuthenticatorAttestationRawResponse attestationResponse getting null. #338

anandsb84 opened this issue Nov 9, 2022 · 13 comments · Fixed by #450

Comments

@anandsb84
Copy link

anandsb84 commented Nov 9, 2022

Hi,

I had taken a reference from sample demo for passwordless.

where we have a method called MakeCredential

[HttpPost]
[Route("/makeCredential")]
public async Task MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse, CancellationToken cancellationToken)

In demo we do get the attestationResponse value but when we try to implement the same thing in our project,
the response which we get for attestationResponse is null although when checked in network tab the payload is passed.
Below is the screen shot of payload which we are passing.

Could you please help us to figure out this issue?

image

@abergs
Copy link
Collaborator

abergs commented Nov 9, 2022

Hello,
What .net version and framework are you using?
Are you using JSON.net or Newtonsoft json?

@anandsb84
Copy link
Author

Hello
we are using .net 6.0 and Newtonsoft Json

@abergs
Copy link
Collaborator

abergs commented Nov 15, 2022

@anandsb84 And have you made any progress here?

You could try to manually deserialize the data, byt most probably there is a base64 encoding issues
I would advise you to test using System.Text.Json since that is what the Demo is using.

@anandsb84
Copy link
Author

Hello,
Tried manually deserialize the data, but for id when we deserialize in byte[] we get encoding issue.
Is there any other approach, so that we can manually deserialize it.

@abergs
Copy link
Collaborator

abergs commented Nov 21, 2022

@anandsb84 Make sure you decode the byte[] as Base64url not just Base64.

@namespacedevbox
Copy link
Contributor

Hello everyone, in one of the projects i migrate to lib v3, encountered that during deserialization clientDataJson is null, although it is present in the json

make response

    let authData = new Uint8Array(credential.response.authenticatorData);
    let clientDataJSON = new Uint8Array(credential.response.clientDataJSON);
    let rawId = new Uint8Array(credential.rawId);
    let sig = new Uint8Array(credential.response.signature);
    const data = {
        id: credential.id,
        rawId: coerceToBase64Url(rawId),
        type: credential.type,
        extensions: credential.getClientExtensionResults(),
        response: {
            authenticatorData: coerceToBase64Url(authData),
            clientDataJson: coerceToBase64Url(clientDataJSON),
            signature: coerceToBase64Url(sig)
        }
    };
    return JSON.stringify(data);
 "response": {
        "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAYw",
        "clientDataJson": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiQW15c2hyUEVTbXdab2FQS2owWFV3QSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjQ0MzI0IiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ",
        "signature": "Jw_WJH6U56S6Ld6Fa6rfajar8BNwz4HDSO8FvDNNxEwnrsU_mvpLSCckhydpbKcaksntWtdkq2-gpYL59qqa-Z2OF2jPlafm63vxn8s0B4TXTJtNNsgLVn8VbRThBkrASU-p84ezokvd8M6bDooIMtLPrWIHvHAeWgr6WRdovzxQqBmoXl3fyJMWJCdnN3-xQcXkc0kZLy8pgHveWKL0FqXhyRste9eF2vzOHjrLzlTeY_7zxhKfxjVOFIl2AfWI6v4qP_BXbt8Uszi0xghY1FiRKJuMFe5URGnOAza9Zo-O_F_lZAQ6F9nb-FaI8aLfiOrv7l5u6-L0zMfkywe9kg"
    }
  var assertionRawResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(json);
  assertionRawResponse.Response.AuthenticatorData // not null
  assertionRawResponse.Response.ClientDataJson // null
  assertionRawResponse.Response.Signature // not null

If you do the conversion manually everything works.

var jsonObject = JsonSerializer.Deserialize<JsonObject>(json);
assertionRawResponse.Response.ClientDataJson = Base64Url.Decode(jsonObject["response"]["clientDataJson"].GetValue<string>());

What did I miss? The project has newtonsoft json, can it influence?

@abergs
Copy link
Collaborator

abergs commented Nov 29, 2022

@namespacedevbox In v3 we shiften towards using System.Text.Json.

I have a theory why this is happening and people are getting null object:

  1. The converted for base64url that is working well with System.Text.Json is not working well with newtonsoft.
  2. During deserialization, newtonsoft fails to map the incoming base64url to the byte[].
  3. (because newtonsoft will try to use base64 instead of base64url).
  4. Because Newtonsoft fails to bind incoming values to the model, it will error out "The incoming data does not bind to the model I was given, return null"
  5. Your controller modelbinding returns null.

To solve it, you either need to either:
A. Use System.Text.Json as your default
B. Deserialize using System.Text.Json specifically in this controller/action
C. Add a Base64Url converter to newtonsoft.

Ideally, we should make this a non-issue by providing a way to make it compatible with newtonsoft.

@namespacedevbox
Copy link
Contributor

@abergs Thanks for the answer, newtonsoft is used for only for validations, fido2 objects deserializing by system.text.json, in raw json i have a ClientDataJson, but after JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(json) is null.
Сreated a console application(without newtosoft), and desirilized my text, and got ClientDataJson null.

{
    "id": "nDFERsL0KwoQdFpuhvdISQKwqrDaD4OPMd6W1B5Khqs",
    "rawId": "nDFERsL0KwoQdFpuhvdISQKwqrDaD4OPMd6W1B5Khqs",
    "type": "public-key",
    "extensions": {},
    "response": {
        "authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAYw",
        "clientDataJson": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiQW15c2hyUEVTbXdab2FQS2owWFV3QSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjQ0MzI0IiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ",
        "signature": "Jw_WJH6U56S6Ld6Fa6rfajar8BNwz4HDSO8FvDNNxEwnrsU_mvpLSCckhydpbKcaksntWtdkq2-gpYL59qqa-Z2OF2jPlafm63vxn8s0B4TXTJtNNsgLVn8VbRThBkrASU-p84ezokvd8M6bDooIMtLPrWIHvHAeWgr6WRdovzxQqBmoXl3fyJMWJCdnN3-xQcXkc0kZLy8pgHveWKL0FqXhyRste9eF2vzOHjrLzlTeY_7zxhKfxjVOFIl2AfWI6v4qP_BXbt8Uszi0xghY1FiRKJuMFe5URGnOAza9Zo-O_F_lZAQ6F9nb-FaI8aLfiOrv7l5u6-L0zMfkywe9kg"
    }
}

@abergs
Copy link
Collaborator

abergs commented Nov 29, 2022

Huh, that's interesting... Can you share that console app to help me diagnose it?

@abergs
Copy link
Collaborator

abergs commented Nov 29, 2022

I have another guess on what might be wrong, but I have not verified it so take it with a grain of salt.
System.Text.Json is case sensitive.

When I was looking at the AuthenticatorAssertionRawResponse line 43, I noticed we've named the field clientDataJSON (Note the all caps JSON). In your payload example, you are sending clientDataJson (Note the lower case Json).

This didn't matter when using newtonsoft, but will not work using System.Text.Json (Unless you change the option to allow case-insensitive property matching!) https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/character-casing#case-insensitive-property-matching

Tricky one to discover, definitely not intuitive. Would be interesting to explore ways to make this more discoverable and easy to diagnose.

@namespacedevbox
Copy link
Contributor

It really worked, problem solved. But I look in your sample verifyAssertionWithServer 'clientDataJson'

const data = {
        id: assertedCredential.id,
        rawId: coerceToBase64Url(rawId),
        type: assertedCredential.type,
        extensions: assertedCredential.getClientExtensionResults(),
        response: {
            authenticatorData: coerceToBase64Url(authData),
            clientDataJson: coerceToBase64Url(clientDataJSON),
            signature: coerceToBase64Url(sig)
        }
    };

and in lib 'clientDataJSON'

    [JsonPropertyName("clientDataJSON")]

When it passes through the controller, it ignores the case, and if it is explicitly deserialized, there will be an error.
Сan be used new JsonSerializerOptions { PropertyNameCaseInsensitive = true}, but not worth it.

@abergs
Copy link
Collaborator

abergs commented Nov 30, 2022

Thanks @namespacedevbox. We should update the sample to minimize the risk of setting people up for failure.

@13thirteen
Copy link

Just as a note:
The same case issue also exists with property AttestationObject vs. attestationObject.

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

Successfully merging a pull request may close this issue.

4 participants