microsoftgraph / msgraph-training-dotnet

Microsoft Graph Training Module - Build .NET apps with Microsoft Graph
MIT License
118 stars 86 forks source link

"Code: generalException Message: Unable to deserialize time of day" #26

Closed NMarkGraves closed 3 years ago

NMarkGraves commented 3 years ago

Where did you get the code?

Describe the bug GetMeAsync() catches a ServiceException, "Code: generalException Message: Unable to deserialize time of day"

To Reproduce Steps to reproduce the behavior:

  1. Follow tutorial to the end of page 4
  2. Fix bug in DeviceCodeAuthProvider constructor (below)
  3. Press F5

OK, u.MailboxSettings is trying to parse something goofy as a date here. No idea what.

return await graphClient.Me
    .Request()
    .Select(u => new {
        u.DisplayName
        , u.MailboxSettings
    })
    .GetAsync();

The first time I tried it, GetMeAsync() worked, then I got an exception trying to access the calendar. I didn't take note of what, I just put in a breakpoint and hit F5 again. But every time since, it's been failing in GetMeAsync()

We can't use the API due to the requirement for the user to log in via a web page every time the application runs. But that's a separate issue.

Also, DeviceCodeAuthProvider doesn't work either. I get "No tenant-identifying information found in either the request or implied by any provided credentials." unless I change this call in the constructor

.WithAuthority(AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount, true) 

to this: .WithTenantId(OurTenantIDGUID)

Expected behavior I expected it to fail when I listed the calendar, the way it did the first time. But it's failing earlier now.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Dependency versions

Additional context Stack trace:

at Microsoft.Graph.TimeOfDayConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.Serialization.JsonConverter1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize[TValue](ReadOnlySpan1 json, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerOptions options) at Microsoft.Graph.DerivedTypeConverter1.PopulateObject(Object target, JsonElement json, JsonSerializerOptions options) at Microsoft.Graph.DerivedTypeConverter1.Read(Utf8JsonReader& reader, Type objectType, JsonSerializerOptions options) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.Serialization.JsonConverter1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize[TValue](ReadOnlySpan1 json, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerOptions options) at Microsoft.Graph.DerivedTypeConverter1.PopulateObject(Object target, JsonElement json, JsonSerializerOptions options) at Microsoft.Graph.DerivedTypeConverter1.Read(Utf8JsonReader& reader, Type objectType, JsonSerializerOptions options) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.JsonPropertyInfo1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan1 buffer, JsonSerializerOptions options, ReadStack& state, JsonConverter converterBase) at System.Text.Json.JsonSerializer.d__191.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Threading.Tasks.ValueTask1.get_Result() at System.Runtime.CompilerServices.ValueTaskAwaiter1.GetResult() at Microsoft.Graph.Serializer.DeserializeObject[T](Stream stream) at Microsoft.Graph.ResponseHandler.<HandleResponse>d__21.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.Graph.BaseRequest.<SendAsync>d__341.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Graph.UserRequest.<GetAsync>d__5.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at GraphTutorial.GraphHelper.d__2.MoveNext() in C:\non-dev\WPFExampleCode\Microsoft.Graph.Console\GraphHelper.cs:line 23

jasonjoh commented 3 years ago

Regarding the auth provider issue - did you register the application as multi-tenant, or for only users in your tenant?

On the Graph error - that seems to indicate something odd about the response from the Graph service, and the SDK can't deserialize it properly. Can you sign in to Graph Explorer with that same user and do a GET on https://graph.microsoft.com/v1.0/me/mailboxsettings and post the response?

NMarkGraves commented 3 years ago

Hi Jason, here's what I got.

(UPDATE: I initially posted the sample account output; this is me after I logged in properly)

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('1fe568c3-4c30-4806-9c84-09567a59320e')/mailboxSettings",
    "archiveFolder": "AAMkAGQ0MzYxZTljLTE5MDQtNGMzMy1iYTEzLWJiMTNlZTFiYTk1ZgAuAAAAAACF-uzi5wmiRoN9Dv6sncxXAQAsvP_RMkhlQodJsIU4WNToAAAAAAE6AAA=",
    "timeZone": "Eastern Standard Time",
    "delegateMeetingMessageDeliveryOptions": "sendToDelegateOnly",
    "dateFormat": "",
    "timeFormat": "",
    "automaticRepliesSetting": {
        "status": "disabled",
        "externalAudience": "all",
        "internalReplyMessage": "<html xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"><head><meta http-equiv=Content-Type content=\"text/html; charset=unicode\"><meta name=Generator content=\"Microsoft Word 15 (filtered medium)\"><style><!--/* Font Definitions */@font-face{font-family:\"Cambria Math\";panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face{font-family:Calibri;panose-1:2 15 5 2 2 2 4 3 2 4;}@font-face{font-family:\"Segoe UI\";panose-1:2 11 5 2 4 2 4 2 2 3;}/* Style Definitions */p.MsoNormal, li.MsoNormal, div.MsoNormal{margin:0in;margin-bottom:.0001pt;font-size:11.0pt;font-family:\"Calibri\",sans-serif;}a:link, span.MsoHyperlink{mso-style-priority:99;color:#0563C1;text-decoration:underline;}a:visited, span.MsoHyperlinkFollowed{mso-style-priority:99;color:#954F72;text-decoration:underline;}span.EmailStyle17{mso-style-type:personal-compose;font-family:\"Segoe UI\",sans-serif;}.MsoChpDefault{mso-style-type:export-only;font-family:\"Calibri\",sans-serif;}@page WordSection1{size:8.5in 11.0in;margin:1.0in 1.0in 1.0in 1.0in;}div.WordSection1{page:WordSection1;}--></style></head><body lang=EN-US link=\"#0563C1\" vlink=\"#954F72\"><div class=WordSection1><p class=MsoNormal style='text-autospace:none'><span style='font-size:8.0pt;font-family:\"Segoe UI\",sans-serif'>I'm currently out of the office. I'll be back on Monday, April 3, 2021.</span><span style='font-size:10.0pt;font-family:\"Segoe UI\",sans-serif'><o:p></o:p></span></p></div></body></html>",
        "externalReplyMessage": "<html xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"><head><meta http-equiv=Content-Type content=\"text/html; charset=unicode\"><meta name=Generator content=\"Microsoft Word 15 (filtered medium)\"><style><!--/* Font Definitions */@font-face{font-family:\"Cambria Math\";panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face{font-family:Calibri;panose-1:2 15 5 2 2 2 4 3 2 4;}@font-face{font-family:\"Segoe UI\";panose-1:2 11 5 2 4 2 4 2 2 3;}/* Style Definitions */p.MsoNormal, li.MsoNormal, div.MsoNormal{margin:0in;margin-bottom:.0001pt;font-size:11.0pt;font-family:\"Calibri\",sans-serif;}a:link, span.MsoHyperlink{mso-style-priority:99;color:#0563C1;text-decoration:underline;}a:visited, span.MsoHyperlinkFollowed{mso-style-priority:99;color:#954F72;text-decoration:underline;}span.EmailStyle17{mso-style-type:personal-compose;font-family:\"Segoe UI\",sans-serif;}.MsoChpDefault{mso-style-type:export-only;font-family:\"Calibri\",sans-serif;}@page WordSection1{size:8.5in 11.0in;margin:1.0in 1.0in 1.0in 1.0in;}div.WordSection1{page:WordSection1;}--></style></head><body lang=EN-US link=\"#0563C1\" vlink=\"#954F72\"><div class=WordSection1><p class=MsoNormal style='text-autospace:none'><span style='font-size:8.0pt;font-family:\"Segoe UI\",sans-serif'>I'm currently out of the office. I'll be back on Monday, April 3, 2021.</span><span style='font-size:10.0pt;font-family:\"Segoe UI\",sans-serif'><o:p></o:p></span></p><p class=MsoNormal style='text-autospace:none'><span style='font-size:8.5pt;font-family:\"Segoe UI\",sans-serif'><o:p>&nbsp;</o:p></span></p></div></body></html>",
        "scheduledStartDateTime": {
            "dateTime": "2021-04-12T15:00:00.0000000",
            "timeZone": "UTC"
        },
        "scheduledEndDateTime": {
            "dateTime": "2021-04-13T15:00:00.0000000",
            "timeZone": "UTC"
        }
    },
    "language": {
        "locale": "en-US",
        "displayName": "English (United States)"
    },
    "workingHours": {
        "daysOfWeek": [
            "monday",
            "tuesday",
            "wednesday",
            "thursday",
            "friday"
        ],
        "startTime": "08:00:00.0000000",
        "endTime": "17:00:00.0000000",
        "timeZone": {
            "name": "Eastern Standard Time"
        }
    }
}
NMarkGraves commented 3 years ago

I think it's only users in our tenant. It's an in-house application. All users will be in our AD.

jasonjoh commented 3 years ago

Ok. The only oddity I note is that your dateFormat and timeFormat properties are empty, which isn't a valid value for those properties. Those don't map to TimeOfDay types though, so I'm not convinced that's the problem. Just to rule it out though, could you login to OWA (outlook.office.com) with that user, go to your Language and time settings, and choose formats for both date and time, then see if you still get the exception?

On the auth provider: yeah that's expected if you register as single-tenant. Microsoft Identity platform requires you to use your tenant's endpoints for auth, so instead of hitting https://login.microsoftonline.com/common like the sample is configured, you have to hit https://login.microsoftonline.com/YOUR_TENANT_ID. The code change you made does exactly that :D.

NMarkGraves commented 3 years ago

Here's my Language and Time settings from OWA

image

jasonjoh commented 3 years ago

Cool can you try again (both in Graph Explorer and the app)? Also, have you ever logged into OWA with that user before?

NMarkGraves commented 3 years ago

I've used OWA before, on windows and on iOS.

jasonjoh commented 3 years ago

Interesting. So are those values still blank in Graph Explorer?

NMarkGraves commented 3 years ago

Same result with the tutorial app. Here's the actual inner exception message that I should have included three days ago: "Invalid leading zero before '8'. LineNumber: 0 | BytePositionInLine: 1."

NMarkGraves commented 3 years ago

Still blank in graph explorer

"dateFormat": "",
"timeFormat": "",

And this must be the time with the invalid leading zero before '8' "startTime": "08:00:00.0000000",

jasonjoh commented 3 years ago

Doh! I just realized you're using the 4.0 preview of the Graph SDK. Seems they have a bug there. I just updated to that version and reproduce the error. Go back to the latest 3.x version and you should be good to go.

NMarkGraves commented 3 years ago

Bingo. That worked. Much appreciated!

jasonjoh commented 3 years ago

Filed this to track the bug on the SDK side: https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/issues/244

NMarkGraves commented 3 years ago

One more thing, if you've got a minute: At some point, we have to make our users do the interactive web page thing, so they can consent to enumerating their own emails or whatever. Thereafter, it's not needed unless we add another scope.

Is there any way to determine what scopes they've already consented to for the application, other than catching MsalUiRequiredException?

I'd like to spare them the VerificationUrl web page, but I suppose that would be a security hole.

jasonjoh commented 3 years ago

Yes and no. The scopes are in the token (it's a JSON Web Token, which is parseable). You can see yourself at https://jwt.ms. However! It's not recommended that you take a dependency on this format, as the token format could change. The recommended pattern from Microsoft identity is to attempt a silent token acquisition first, and if that generates MsalUiRequiredException, then do the interactive request.

If you implement token cache serialization and persist your cache between runs of the app, the user may not ever have to login again. If you go that route, you want to take steps to secure wherever you store your cache - that's not something you'd want folks to get access to.