ory / sdk

The place where ORY's SDKs are being auto-generated
Apache License 2.0
139 stars 86 forks source link

kratos-client-java: IllegalArgumentException: Expected the field `oauth2_login_challenge` to be a primitive type in the JSON string but got `null` #241

Closed Maragues closed 1 year ago

Maragues commented 1 year ago

Preflight checklist

Describe the bug

I can't start a native registration flow using https://github.com/ory/kratos-client-java

IllegalArgumentException: Expected the field oauth2_login_challenge to be a primitive type in the JSON string but got null

Reproducing the bug

val frontendApi =  FrontendApi(ApiClient()).apply {
        customBaseUrl = "https://playground.projects.oryapis.com"
    }

frontendApi.createNativeRegistrationFlow()

Relevant log output

Caused by: java.lang.IllegalArgumentException: Expected the field `oauth2_login_challenge` to be a primitive type in the JSON string but got `null`
                                                                                                        at sh.ory.kratos.model.RegistrationFlow.validateJsonObject(RegistrationFlow.java:458)
                                                                                                        at sh.ory.kratos.model.RegistrationFlow$CustomTypeAdapterFactory$1.read(RegistrationFlow.java:500)
                                                                                                        at sh.ory.kratos.model.RegistrationFlow$CustomTypeAdapterFactory$1.read(RegistrationFlow.java:490)
                                                                                                        at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
                                                                                                        at com.google.gson.Gson.fromJson(Gson.java:991)
                                                                                                        at com.google.gson.Gson.fromJson(Gson.java:956)
                                                                                                        at com.google.gson.Gson.fromJson(Gson.java:905)
                                                                                                        at sh.ory.kratos.JSON.deserialize(JSON.java:334)
                                                                                                        at sh.ory.kratos.ApiClient.deserialize(ApiClient.java:847)
                                                                                                        at sh.ory.kratos.ApiClient.handleResponse(ApiClient.java:1055)
                                                                                                        at sh.ory.kratos.ApiClient.execute(ApiClient.java:979)
                                                                                                        at sh.ory.kratos.api.FrontendApi.createNativeRegistrationFlowWithHttpInfo(FrontendApi.java:1335)
                                                                                                        at sh.ory.kratos.api.FrontendApi.createNativeRegistrationFlow(FrontendApi.java:1315)

On the server side

time=2022-12-20T10:20:50Z level=info msg=started handling request http_request=map[headers:map[accept:application/json accept-encoding:gzip connection:Keep-Alive user-agent:OpenAPI-Generator/v0.11.0/java] host:10.0.2.2:4433 method:GET path:/self-service/registration/api query:<nil> remote:172.17.0.1:56256 scheme:http]
time=2022-12-20T10:20:50Z level=info msg=completed handling request http_request=map[headers:map[accept:application/json accept-encoding:gzip connection:Keep-Alive user-agent:OpenAPI-Generator/v0.11.0/java] host:10.0.2.2:4433 method:GET path:/self-service/registration/api query:<nil> remote:172.17.0.1:56256 scheme:http] http_response=map[headers:map[cache-control:private, no-cache, no-store, must-revalidate content-type:application/json; charset=utf-8 vary:Cookie] size:1272 status:200 text_status:OK took:2.928785ms]

Relevant configuration

{
  "$id": "schemaV0.0.1",
  "title": "Person",
  "type": "object",
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email",
          "title": "E-Mail",
          "ory.sh/kratos": {
            "credentials": {
              "password": {
                "identifier": true
              },
              "webauthn": {
                "identifier": true
              },
              "totp": {
                "account_name": true
              }
            },
            "recovery": {
              "via": "email"
            },
            "verification": {
              "via": "email"
            }
          },
          "maxLength": 320
        }
      },
      "required": [
        "email"
      ],
      "additionalProperties": false
    }
  }
}

Version

0.11.0

On which operating system are you observing this issue?

macOS

In which environment are you deploying?

Docker Compose

Additional Context

The response from the server is

{
    "id": "2cf93911-2254-414b-a962-05b6c7a31c48",
    "oauth2_login_challenge": null,
    [...]
}

The relevant code in RegistrationFlow.java is

if (jsonObj.get("oauth2_login_challenge") != null && !jsonObj.get("oauth2_login_challenge").isJsonPrimitive()) {
  throw new IllegalArgumentException(String.format("Expected the field `oauth2_login_challenge` to be a primitive type in the JSON string but got `%s`", jsonObj.get("oauth2_login_challenge").toString()));
}

I haven't worked with gson for a long time, but I see that jsonObj.get returns a JsonElement, which has this public check

  /**
   * provides check for verifying if this element represents a null value or not.
   */
  public boolean isJsonNull() {
    return this instanceof JsonNull;
  }

Debugging the JsonObject, this is the members Map

members = {LinkedTreeMap@12411}  size = 7
 "id" -> {JsonPrimitive@12489} ""69437b6c-bc2a-4f03-ba34-8773e80d9b60""
 "oauth2_login_challenge" -> {JsonNull@12491} "null"
...
}

Thus, the generated call should either check isJsonNull() and accept a NPE if member is not present, or do both != null && !.isJsonNull()

This should be done for all java models, I am afraid.

In the past, I used static functions to not having to write so much code. Something like

class GsonParserHelper {
  private final JsonObject jsonObject;

  public GsonParserHelper(JsonObject jsonObject){ this.jsonObject = jsonObject; }

  static Boolean isJsonPrimitive(String memberName) {
    JsonElement element = jsonObject.get(memberName);

    return element != null && !element.isJsonNull() && element.isJsonPrimitive();
  }
}
Maragues commented 1 year ago

it looks like this was fixed in https://github.com/ory/sdk/commit/14ec526e4139d30ecd0cfb8822608b4d98371dd7