AzureAD / microsoft-authentication-library-for-java

Microsoft Authentication Library (MSAL) for Java http://aka.ms/aadv2
MIT License
285 stars 143 forks source link

[Bug] Fail to run in native image #849

Open luooyii opened 2 months ago

luooyii commented 2 months ago

Library version used

1.16.0

Java version

21

Scenario

ConfidentialClient - service to service (AcquireTokenForClient)

Is this a new or an existing app?

The app is in production, I haven't upgraded MSAL, but started seeing this issue

Issue description and reproduction steps

I am trying to AOT compile a legacy service to native image. Everything works well when it runs in JIT(jar) mode. But when it runs in AOT mode. I throws this exception.

Cannot construct instance of com.microsoft.aad.msal4j.AadInstanceDiscoveryResponse: cannot deserialize from Object value (no delegate- or property-based Creator): this appears to be a native image, in which case you may need to configure reflection for the class that is to be deserialized

After that, I used native-image-agent to try to capture all reflections. And here are all msal4j reflections in my project.

{
  "name":"com.microsoft.aad.msal4j.AadInstanceDiscoveryResponse",
  "allDeclaredFields":true,
  "queryAllDeclaredMethods":true,
  "queryAllDeclaredConstructors":true,
  "methods":[{"name":"<init>","parameterTypes":[] }]
},
{
  "name":"com.microsoft.aad.msal4j.ConfidentialClientApplication"
},
{
  "name":"com.microsoft.aad.msal4j.InstanceDiscoveryMetadataEntry",
  "allDeclaredFields":true,
  "queryAllDeclaredMethods":true,
  "queryAllDeclaredConstructors":true,
  "methods":[{"name":"<init>","parameterTypes":[] }]
},
{
  "name":"[Lcom.microsoft.aad.msal4j.InstanceDiscoveryMetadataEntry;"
},

Normally, I just need to configure them at compile time to fix this. However, in general, in Java projects, we usually choose to configure reflection programmatically. However many classes are not public.

'com.microsoft.aad.msal4j.AadInstanceDiscoveryResponse' is not public in 'com.microsoft.aad.msal4j'. Cannot be accessed from outside package

I was wondering if these classes could be exposed? Or maybe we can introduce native image meta info in msal4j lib? Because if I use the native-image-agent, it only captures the reflections which are running. But I cannot cover all the scenarios of Azure AD Auth. It means that each time I want to introduce a new scenario, I have to reuse to native-image-agent and compare the difference with the legacy reflection configurations.

Relevant code snippets

No response

Expected behavior

No response

Identity provider

Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)

Regression

No response

Solution and workarounds

No response

bgavrilMS commented 2 months ago

Right, MSAL 4j uses json deserialization for those objects. I reckon the AOT compiler uses an aggressive linker and strips away the objects AadInstanceDiscoveryResponse etc. These objects are not directly constructed by MSAL, but the json library will initiaze them via reflection. This ussually trips linkers up.

We cannot make these objects public. And I am not aware of a json serializer library that can use source generation (pls suggest one), but from experience this is a lot of work. We had to do this for MSAL.NET to be AOT-able.

Recommendation is to read up on the AOT linker and see if you can manually instruct it to not remove those objects. Or just ask the linker to not optimize MSAL 4j, it's not such a big library anyway.

luooyii commented 2 months ago

Right, MSAL 4j uses json deserialization for those objects. I reckon the AOT compiler uses an aggressive linker and strips away the objects AadInstanceDiscoveryResponse etc. These objects are not directly constructed by MSAL, but the json library will initiaze them via reflection. This ussually trips linkers up.

We cannot make these objects public. And I am not aware of a json serializer library that can use source generation (pls suggest one), but from experience this is a lot of work. We had to do this for MSAL.NET to be AOT-able.

Recommendation is to read up on the AOT linker and see if you can manually instruct it to not remove those objects. Or just ask the linker to not optimize MSAL 4j, it's not such a big library anyway.

I am not sure if it is the one you wanted. This is a embeded command in gravvl vm. You can use this command to run a JIT jar file. And then it would collect relections or resources. Please note that finally you should use ctrl + c to stop the program, rather than close or kill the program directly. java -agentlib:native-image-agent=config-merge-dir=./agent-output -jar ./app.jar

Finally some json config would be generated. After trimed the json file and copy them to META-INF/native-image, I started the AOT output successfully. This can be used as a temporary solution, still expect to be able to get the final solution from the official.

bgavrilMS commented 2 months ago

We will keep the request on the backlog @luooyii and look for others requesting this feature. Not planning to address this soon.

luooyii commented 2 months ago

Thanks @bgavrilMS

yogeshVU commented 1 week ago

This will be great feature to add to have. Our use-case requires uploading bunch of data to the blobstorage and uses confidential client. However, the startup time for invoking the jar slows down the upload process for each invocation. Having the AOT will be great.