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

[Bug] HoloLens 2 device code flow does not work #2231

Closed
1 of 7 tasks
rfurmaniak opened this issue Nov 23, 2020 · 20 comments
Closed
1 of 7 tasks

[Bug] HoloLens 2 device code flow does not work #2231

rfurmaniak opened this issue Nov 23, 2020 · 20 comments

Comments

@rfurmaniak
Copy link

Logs and Network traces
Error setting value to 'TenantDiscoveryEndpoint' on 'Microsoft.Identity.Client.Instance.Discovery.InstanceDiscoveryResponse'. at Microsoft.Identity.Json.Serialization.ExpressionValueProvider.SetValue (System.Object target, System.Object value) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue (Microsoft.Identity.Json.Serialization.JsonProperty property, Microsoft.Identity.Json.JsonConverter propertyConverter, Microsoft.Identity.Json.Serialization.JsonContainerContract containerContract, Microsoft.Identity.Json.Serialization.JsonProperty containerProperty, Microsoft.Identity.Json.JsonReader reader, System.Object target) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader.PopulateObject (System.Object newObject, Microsoft.Identity.Json.JsonReader reader, Microsoft.Identity.Json.Serialization.JsonObjectContract contract, Microsoft.Identity.Json.Serialization.JsonProperty member, System.String id) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader.CreateObject (Microsoft.Identity.Json.JsonReader reader, System.Type objectType, Microsoft.Identity.Json.Serialization.JsonContract contract, Microsoft.Identity.Json.Serialization.JsonProperty member, Microsoft.Identity.Json.Serialization.JsonContainerContract containerContract, Microsoft.Identity.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Microsoft.Identity.Json.JsonReader reader, System.Type objectType, Microsoft.Identity.Json.Serialization.JsonContract contract, Microsoft.Identity.Json.Serialization.JsonProperty member, Microsoft.Identity.Json.Serialization.JsonContainerContract containerContract, Microsoft.Identity.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader.Deserialize (Microsoft.Identity.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.JsonSerializer.DeserializeInternal (Microsoft.Identity.Json.JsonReader reader, System.Type objectType) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Microsoft.Identity.Json.JsonSerializerSettings settings) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.JsonConvert.DeserializeObject[T] (System.String value, Microsoft.Identity.Json.JsonSerializerSettings settings) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.JsonConvert.DeserializeObject[T] (System.String value) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Client.Utils.JsonHelper.DeserializeFromJson[T] (System.String json) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T] (Microsoft.Identity.Client.Http.HttpResponse response, Microsoft.Identity.Client.Internal.RequestContext requestContext) [0x00000] in <00000000000000000000000000000000>:0 \r\n at Microsoft.Identity.Json.Linq.Extensions+<Convert>d__142[T,U].<>m__Finally1 () [0x00000] in <00000000000000000000000000000000>:0 \r\n at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.InvokeMoveNext (System.Object stateMachine) [0x00000]`

Which Version of MSAL are you using ?
4.22, built from the current MSAL master branch.

Platform
Unity 2019.4.0f1, UWP, IL2CPP, ARM for HoloLens 2

What authentication flow has the issue?

  • Desktop / Mobile
    • Interactive
    • Integrated Windows Auth
    • Username Password
    • Device code flow (browserless)
  • Web App
    • Authorization code
    • OBO
  • Daemon App
    • Service to Service calls

Other? - please describe;

Is this a new or existing app?
We were using some version of MSAL v3, but application in production started having issues with authenticating private accounts. After long support session we learned that we should change a little our flow, this required update to MSAL v4.
Unfortunately, while everything works fine in the editor, version deployed on the HoloLens does not.

Repro

I am attaching whole script that contains our logic for device code.
Controller script only calls SignInWithDeviceFlow() method and this method fails on AcquireToken.

DeviceCodeAuthenticator.txt

Expected behavior
Using AcquireTokenWithDeviceCode should give code to authenticate user.

Actual behavior
Exception is being thrown and no code is given.

Possible Solution
This issue appeared in MSAL v3 we were using previously and link.xml fixed it. In the new version of MSAL, this fix does not work.
I have turned panic mode on and basically put all possible options in link.xml, still does not work. Here is the content:

<linker>    
<assembly fullname="Microsoft.Identity.Client" preserve="all"/>
	<assembly fullname="Microsoft.Identity" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json.Serialization" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json.JsonConvert" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json.Serialization.JsonProperty " preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json.Serialization.JsonSerializerInternalReader" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Instance.Discovery.InstanceDiscoveryResponse" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Utils" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Instance.Discovery" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Utils.JsonHelper" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.OAuth2" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.OAuth2.OAuth2Client" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Internal" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Json.Linq" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Cache" preserve="all"/>
	<assembly fullname="Microsoft.Identity.Client.Cache.TokenCacheJsonSerializer" preserve="all"/>
</linker>

Additional context/ Logs / Screenshots
I have tried to cheat Unity bytestripping, by pasting the library into the build directory and then deploying it to the HoloLens, but it changes nothing.
I have tried using several versions of MSAL:

  • 4.7.1
  • 4.22 built from master branch
  • 4.22 downloaded from NuGet and then copied over to Unity (only ARM version)
@bgavrilMS
Copy link
Member

bgavrilMS commented Nov 23, 2020

Hi @rfurmaniak - when you say that the platform is "Unity 2019.4.0f1, UWP, IL2CPP, ARM for HoloLens 2", how does this resolve to a platform supported by MSAL? https://www.nuget.org/packages/Microsoft.Identity.Client/

Do you target the .netstandard version of MSAL? Or the UWP version?

The root cause is pretty clear - the json serialization fails. This is because it's probably using reflection in an environment where reflection is not possible. As far as I know, if you try to use the .NETStanadard1.3 version of MSAL, we will use a version of Newtonsoft.Json that does not use reflection - https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Newtonsoft.Json.csproj

@rfurmaniak
Copy link
Author

Hi @rfurmaniak - when you say that the platform is "Unity 2019.4.0f1, UWP, IL2CPP, ARM for HoloLens 2", how does this resolve to a platform supported by MSAL? https://www.nuget.org/packages/Microsoft.Identity.Client/

Do you target the .netstandard version of MSAL? Or the UWP version?

The root cause is pretty clear - the json serialization fails. This is because it's probably using reflection in an environment where reflection is not possible. As far as I know, if you try to use the .NETStanadard1.3 version of MSAL, we will use a version of Newtonsoft.Json that does not use reflection - https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Newtonsoft.Json.csproj

Hi @bgavrilMS, thanks for quick response.
To build Microsoft.Identity.Client.dll I was using .NETStandard 1.3 version.

@rfurmaniak
Copy link
Author

rfurmaniak commented Dec 2, 2020

Hi @bgavrilMS,
I have created a separate Unity Project, with only MRTK and MSAL, just to check if the issue can be reproduced there - and it can.
Please find the link to the repository:
https://drive.google.com/file/d/1Bsj1fL04GKABYS_x5QBB_J8Irv1YjqEf/view?usp=sharing
Only think to do is to build it for HoloLens 2 and deploy it (it works ok in the editor). After launching the app on HoloLens, there will be an error information. You can also find additional information in Unity log file.
Expected behavior (the one in the editor) is that a device code should appear, and after using it on microsoft.com/devicelogin and logging with any MS account, username and id of this account should be displayed.

We have created a test app registration, just so you can use it to reproduce this issue. I hope this helps and brings more information.
If you want it somewhere else then Google Drive, please let me know how can I share it with you.

@bgavrilMS
Copy link
Member

@jmprieur - is this something for our friends in CXP to engage with again?

@therealjohn
Copy link

@rfurmaniak

Can you reproduce this issue on the HoloLens 2 emulator? If so, what steps do you take to verify the issue using the emulator?

@rfurmaniak
Copy link
Author

rfurmaniak commented Dec 18, 2020

@rfurmaniak

Can you reproduce this issue on the HoloLens 2 emulator? If so, what steps do you take to verify the issue using the emulator?

We've never really been using the emulator, as it is unreliable for us for testing, due to it being x86 platform and not ARM. But I will try to deploy it on the emulator and let you know as soon as I have results.

@rfurmaniak
Copy link
Author

@rfurmaniak

Can you reproduce this issue on the HoloLens 2 emulator? If so, what steps do you take to verify the issue using the emulator?

I managed to deploy the solution I've sent previously to emulator. The issue can be reproduced there, all there is to do is to deploy the app and run it in the emulator. After the startup, error appears in log and message about not being able to login is displayed on the screen.
Expected behavior is for the device code to appear in front of the user.

@jitendraRai
Copy link

@rfurmaniak
Can you reproduce this issue on the HoloLens 2 emulator? If so, what steps do you take to verify the issue using the emulator?

I managed to deploy the solution I've sent previously to emulator. The issue can be reproduced there, all there is to do is to deploy the app and run it in the emulator. After the startup, error appears in log and message about not being able to login is displayed on the screen.
Expected behavior is for the device code to appear in front of the user.

Thanks and can you please share the steps as well as screen to reproduce for emulator. Please share in the google drive.
Please try to use Microsoft.Identity.client.dll for version 4.16.0 and net standard 1.3

@therealjohn
Copy link

therealjohn commented Dec 21, 2020

I think I am reproducing the issue on the HoloLens 2 emulator FWIW. It might be an issue with the code running in Unity's IL2CPP runtime.

I've attached the full output log from VS when running the app on the emulator in case it helps.

This doesn't appear to be working on the emulator/device.

_authResult = await _identityClient.AcquireTokenWithDeviceCode(_authScopes,
                    deviceCodeResultCallback).ExecuteAsync(cancellationToken);

Snippet from the log (note, the message is user defined):
debug-log.txt

Could not acquire Identity with device flow
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
API.Authentication.<LoginInternal>d__19:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean)
System.Threading.Tasks.Task:FinishContinuations()
System.Threading.Tasks.Task:FinishStageThree()
System.Threading.Tasks.Task`1:TrySetResult(TResult)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Create()
API.Authentication.<NewUserLogin>d__20:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean)
System.Threading.Tasks.Task:FinishContinuations()
System.Threading.Tasks.Task:FinishStageThree()
System.Threading.Tasks.Task`1:TrySetResult(TResult)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Create()
API.Authentication.<SignInWithDeviceFlow>d__16:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.<>c:<.cctor>b__7_0(Object)
System.Threading.SendOrPostCallback:Invoke(Object)
UnityEngine.WSA.WindowSizeChanged:.ctor(Object, IntPtr)
UnityEngine.UnitySynchronizationContext:Exec()
UnityEngine.UnitySynchronizationContext:ExecuteTasks()

@jitendraRai
Copy link

I think I am reproducing the issue on the HoloLens 2 emulator FWIW. It might be an issue with the code running in Unity's IL2CPP runtime.

I've attached the full output log from VS when running the app on the emulator in case it helps.

This doesn't appear to be working on the emulator/device.

_authResult = await _identityClient.AcquireTokenWithDeviceCode(_authScopes,
                    deviceCodeResultCallback).ExecuteAsync(cancellationToken);

Snippet from the log (note, the message is user defined):
debug-log.txt

Could not acquire Identity with device flow
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
API.Authentication.<LoginInternal>d__19:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean)
System.Threading.Tasks.Task:FinishContinuations()
System.Threading.Tasks.Task:FinishStageThree()
System.Threading.Tasks.Task`1:TrySetResult(TResult)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Create()
API.Authentication.<NewUserLogin>d__20:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.AwaitTaskContinuation:InvokeAction(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.Tasks.AwaitTaskContinuation:RunCallback(ContextCallback, Object, Task&)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation:Run(Task, Boolean)
System.Threading.Tasks.Task:FinishContinuations()
System.Threading.Tasks.Task:FinishStageThree()
System.Threading.Tasks.Task`1:TrySetResult(TResult)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1:Create()
API.Authentication.<SignInWithDeviceFlow>d__16:MoveNext()
System.Runtime.CompilerServices.MoveNextRunner:InvokeMoveNext(Object)
System.Threading.ContextCallback:Invoke(Object)
System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
System.Runtime.CompilerServices.MoveNextRunner:Run()
System.Action:Invoke()
System.Threading.Tasks.<>c:<.cctor>b__7_0(Object)
System.Threading.SendOrPostCallback:Invoke(Object)
UnityEngine.WSA.WindowSizeChanged:.ctor(Object, IntPtr)
UnityEngine.UnitySynchronizationContext:Exec()
UnityEngine.UnitySynchronizationContext:ExecuteTasks()

Thanks John and its also verified working with Unity editor of Microsoft.Identity.client.dll (latest netstandard 1.3 dll). However issue of AcquireTokenWithDeviceCode method when deployed to Hololens emulator using Visual Studio.

@jmprieur
Copy link
Contributor

jmprieur commented Jan 5, 2021

Update after synching with Jitendra from CPX.

  1. The scenario is working in Visual Studio in the (Unity) editor,
  2. but not when deployed in the emulator.
Error: (True) MSAL 4.xx.1.0 MSAL.CoreCLR N/A [01/04/2021 18:22:28 - 349f907d-7652-4502-b85c-8fc9db5e43a1] Microsoft.Identity.Json.JsonSerializationException: Error setting value to 'TenantDiscoveryEndpoint' on 'Microsoft.Identity.Client.Instance.Discovery.InstanceDiscoveryResponse'. ---> System.NullReferenceException: Object reference not set to an instance of an object.

this answers some question. The platform is CoreCLR

@jitendraRai
Copy link

Update after synching with Jitendra from CPX.

  1. The scenario is working in Visual Studio in the (Unity) editor,
  2. but not when deployed in the emulator.
Error: (True) MSAL 4.xx.1.0 MSAL.CoreCLR N/A [01/04/2021 18:22:28 - 349f907d-7652-4502-b85c-8fc9db5e43a1] Microsoft.Identity.Json.JsonSerializationException: Error setting value to 'TenantDiscoveryEndpoint' on 'Microsoft.Identity.Client.Instance.Discovery.InstanceDiscoveryResponse'. ---> System.NullReferenceException: Object reference not set to an instance of an object.

this answers some question. The platform is CoreCLR

MSAL code has this TenantDiscoveryEndPoint property and it is crashing while working on Hololens emulator or Device.

image

@rfurmaniak
Copy link
Author

@jitendraRai
Sorry for the late response - we were out of office due to the christmas break.
You are correct, this issue only happens when using HoloLens 2 or Emulator, as I wrote in the bug description.
I guess you were able to reproduce the issue in the emulator, do you still need steps and screens or any other information?

@najadojo
Copy link
Contributor

I've found a work around for this issue. The clue was in Unity Issue Tracker - [IL2CPP] NotSupportedExceptions exception is thrown in build with Newtonsoft.Json plugin (unity3d.com).

I needed to add to link.xml:

<assembly fullname="System" preserve="all" />
<assembly fullname="System.Core" preserve="all" />

Additionally and what the Unity docs are inaccurate about is that this must be in the root Assets/link.xml. Lower or package link.xml inclusions don't work.

@rfurmaniak
Copy link
Author

Thank you @najadojo! The solution you posted is working :)

@jmprieur
Copy link
Contributor

Thanks @najadojo for the workaround, and @rfurmaniak for confirming.

Added an entry in the wiki: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/hololens2

@najadojo
Copy link
Contributor

@jmprieur This isn't a HL2 specific issue it is a general Unity IL2CPP issue. I'd suggest that you title the wiki entry to include that fact so more folks can find the solution.

@jmprieur
Copy link
Contributor

Thanks for the update, @najadojo
I've updated the wiki page.

@pmaytak
Copy link
Contributor

pmaytak commented Feb 12, 2021

As already been mentioned, the issue comes from Unity IL2CPP. When optimizing code (using code stripping), it removes needed dependencies for reflection to work (because it can't properly detect that usage). We investigated removing reflection related code from MSAL but it proved to be very impractical. Unity themselves have this documented in their docs Managed code stripping and recommend to use Link XML method as one of the solutions to this issue. This is our recommendation as well.

@rfurmaniak @therealjohn @jitendraRai @najadojo

@jmprieur
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants