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

ReflectionTypeLoadException: Unable to Load IBaseRequest and IAuthenticationProvider from assembly 'Microsoft.Graph.Core, Version=3.0.2.0 #629

Closed
VorTechS opened this issue Mar 16, 2023 · 21 comments
Labels
Needs: Attention 👋 Question: SDK Not actionable. Question related to the SDK.

Comments

@VorTechS
Copy link

Having encountered a problem with v4.x of the Microsoft.Graph NuGet packages, I decided to grab the latest version of the NuGet package for use in our .NET Core 6 Blazor project, written using Visual Studio 2022 on a Windows 10 machine.

Having followed the instructions for the Upgrade from v4 to the new v5 (starting with 5.01 and now 5.02),
I have the errors:

System.Reflection.ReflectionTypeLoadException: 'Unable to load one or more of the requested types.
Could not load type 'Microsoft.Graph.IBaseRequest' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Microsoft.Graph.IAuthenticationProvider' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Microsoft.Graph.IAuthenticationProviderOption' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Microsoft.Graph.IBaseRequest' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Microsoft.Graph.IBaseRequest' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Microsoft.Graph.IBaseRequest' from assembly 'Microsoft.Graph.Core, Version=3.0.2.0, Culture=neutral, PublicKeyToken=null'.'

...when calling app.MapControllers() from my Program.cs during the application startup.

So I decided to download the source code which leaves you with a broken set of source code mostly related to: 'DoesUserHaveAccessuserIdUserIdTenantIdTenantIdUserPrincipalNameUserPrincipalNameRequestBuilder' and other similarly named classes which I can see live in separate folders but just don't make it down to a machine from either of the above.
(Possibly a file path character limit being hit?!)

Having battled through those issues, adding the missing classes and finally getting both Microsoft.Graph and Microsoft.Graph.Core building against .NET Core 6, I'm able to confirm I get the same errors from the source code as I do the NuGet package.

I can see mentions of IBaseRequest in previous commits living under Microsoft.Graph.Core.Requests, e.g:

073613f

.... but the current iteration of the project does not have this, and I can see from the documentation IBaseRequest is meant to have been dropped, yet somehow Reflection still seems to want to load it (presumably something from Kiota?)

The loaded source code project(s) have the following packages referenced:

image

image

...which are inline with the available NuGet packages.

Is can only surmise that there is a reference missing, something that I need to be including.

Can anyone give me any pointers please?

@ghost ghost added the ToTriage label Mar 16, 2023
@ghost ghost added this to Issues to triage in Graph SDK - DotNet Mar 16, 2023
@andrueastman
Copy link
Member

Thanks for raising this @VorTechS

Any chance you could share the contents of your .csproj to help us understand if there's another depenendecy trying to load an older version of the client? Alternatively, sharing a sample repro project would be great as well.

@andrueastman andrueastman added Question: SDK Not actionable. Question related to the SDK. Needs: Author Feedback and removed ToTriage labels Mar 17, 2023
@VorTechS
Copy link
Author

VorTechS commented Mar 17, 2023

Thanks for the reply. Here's the .csproj file contents:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup Label="Globals">
    <SccProjectName>SAK</SccProjectName>
    <SccProvider>SAK</SccProvider>
    <SccAuxPath>SAK</SccAuxPath>
    <SccLocalPath>SAK</SccLocalPath>
  </PropertyGroup>

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
	<RootNamespace>Polestar</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <_ContentIncludedByDefault Remove="wwwroot\css\site.css.txt" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.2" />
    <PackageReference Include="Azure.Identity" Version="1.8.2" />
    <PackageReference Include="Azure.Storage.Blobs" Version="12.15.0" />
    <PackageReference Include="Azure.Storage.Files.Shares" Version="12.13.0" />
    <PackageReference Include="Azure.Storage.Queues" Version="12.13.0" />
    <PackageReference Include="BlazorPro.BlazorSize" Version="6.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.14" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.14" />
    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.14" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.14" />
    <PackageReference Include="Microsoft.Extensions.Azure" Version="1.6.3" />
    <PackageReference Include="Microsoft.Identity.Web" Version="1.26.0" />
    <PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.26.0" />
    <PackageReference Include="Microsoft.Identity.Web.UI" Version="1.26.0" />
  </ItemGroup>

  <ItemGroup>
    <Folder Include="Pages\CreateAndEdit\Interactive\" />
    <Folder Include="Pages\CreateAndEdit\NewFolder\" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\InteractiveFormsCommon\InteractiveFormsCommon.csproj" />
    <ProjectReference Include="..\InteractiveFormsRazor\InteractiveForms.csproj" />
    <ProjectReference Include="..\Microsoft.Graph\Microsoft.Graph.csproj" />
    <ProjectReference Include="..\PdfSharpCore-master\PdfSharpCore\PdfSharpCore.csproj" />
    <ProjectReference Include="..\Polestar.Business\Polestar.Business.csproj" />
    <ProjectReference Include="..\Polestar.DataLayer\Polestar.DataLayer.csproj" />
    <ProjectReference Include="..\Polestar.ViewModel\Polestar.ViewModels.csproj" />
    <ProjectReference Include="..\Polestart.DataEngine\Polestar.DataEngine.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Content Update="wwwroot\css\site.css">
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </Content>
  </ItemGroup>

</Project>

Clearly, that's not everything as it's quite a large project there are other projects involved, I suspect the culprit may be one of the Azure packages (as we're using KeyVault and Blob storage)

    <PackageReference Include="Azure.Core" Version="1.30.0" />
    <PackageReference Include="Azure.Identity" Version="1.8.2" />
    <PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.4.0" />
    <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.4.0" />
    <PackageReference Include="Azure.Storage.Blobs" Version="12.15.0" />
    <PackageReference Include="Azure.Storage.Files.Shares" Version="12.13.0" />
    <PackageReference Include="BlazorPro.BlazorSize" Version="6.2.0" />
    <PackageReference Include="libphonenumber-csharp" Version="8.13.7" />
    <PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
    <PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />

... most of which have been updated to the latest versions to within the last few days.

I haven't been brave enough to be updating any of the ASPNetCore binaries, yet!

If necessary, I'm more than willing to put together a sample project with every package used throughout the entire project to see if that suffers the same fate.

@timayabi2020
Copy link

@andrueastman Is there any update on this issue? I also have a similar problem

@andrueastman
Copy link
Member

@VorTechS

This is most likely caused by which still depends on Graph core 2.x.x. There is an update upcoming to resolve this from that side to resolve this. In the meantime you will need to use Graph v4 if you need the dependency.

<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.26.0" />

Any chance you are able to drop the dependency to verify?

@VorTechS
Copy link
Author

Sure.

Removing that reference (and any associated code) did indeed resolve that particular issue.

But then, I'm unable to do TokenAcquisition to push down to Graph in order to retrieve an Ad User List of members that have permissions to the application (the overall problem I am trying to solve).

Graph v4 worked fine in development but when the application was pushed to Azure it behaved differently, returning an error related to JSON Deserialization (something along the lines of Expected Object, Received Array). Hence trying down this path of going to the latest binaries.

So I'm stuck in between a rock and hard place here :)

@andrueastman
Copy link
Member

There will be an upcoming update to support the new version from Microsoft.Identity.Web.MicrosoftGraph. AzureAD/microsoft-identity-web#2097

In the meantime, we may be able to resolve json error from V4. Are you able to share details on what situation the error is occurring and on calling what endpoint?

@VorTechS
Copy link
Author

I'll put the source code back together as it was, re-deploy to the server and attach a debugger again to get the specific error. Like I said it only happens once deployed to the server, it doesn't happen on the local development machine(s).

@VorTechS
Copy link
Author

Well after re-enabling the previous v4.x code, it would appear we have a NEW error 😄

'Message: CompactToken parsing failed with error code: 80049217'

            var microsoftUser = (await _authProvider.GetAuthenticationStateAsync()).User;

                var userTenant = microsoftUser.GetTenantId();

                // Acquiring token for graph in the signed-in users tenant, so it can be used to retrieve all the users from their tenant
                var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "user.read" }, tenantId: userTenant);

                var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
                {
                    requestMessage
                        .Headers
                        .Authorization = new AuthenticationHeaderValue("Bearer", graphAccessToken);

                    return Task.CompletedTask;
                }));

                // Whatever you do, it's easier to get the full user list
                var userListPage = await graphClient.Users.Request().GetAsync();

There's a chance it worked ONCE whilst I was stepping through and then subsequent calls (after a StateHasChanged() forces an update) fail with this error. But I've yet to go through more methodically and successfully prove that.

@andrueastman
Copy link
Member

Hey @VorTechS

As you are trying to list all the users in the tenant, I believe you would need to pass one of the permissions listed at the link below. User.ReadBasic.All, User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All
https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#permissions

Any chance the error goes away if you change the API call made from

var userListPage = await graphClient.Users.Request().GetAsync();

to

var user = await graphClient.Me.Request().GetAsync();

There is the possibility that the user in the dev environment already has one of the permissions above to list all users in the tenant but the once deployed the user doesn't have this.

@ghost
Copy link

ghost commented Mar 28, 2023

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.

@VorTechS
Copy link
Author

VorTechS commented Mar 28, 2023

Sorry ended up fighting other fires that took me away from this.

But at this point, this error is occurring attached to the Azure App Instance process, not locally.

I'll try out your suggestion, and poke around a bit more, but it does work fine in dev. just not once deployed to the server. And my dev account doesn't have permissions (there seems to be a Visual Studio bug where it's ignoring the Azure user it's setup to use), so when I say it works in dev. I mean by launching a separate browser and signing in as an Azure User that has the correct permissions.

@VorTechS
Copy link
Author

VorTechS commented Mar 30, 2023

So. I modified our 'Profile Menu' component to include the code you suggested, which does as follows:

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var microsoftUser = (await _authProvider.GetAuthenticationStateAsync()).User;

            var userTenant = microsoftUser.GetTenantId();

            // Acquiring token for graph in the signed-in users tenant, so it can be used to retrieve all the users from their tenant
            var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "user.read" }, tenantId: userTenant);

            var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
            {
                requestMessage
                    .Headers
                    .Authorization = new AuthenticationHeaderValue("Bearer", graphAccessToken);

                return Task.CompletedTask;
            }));

            var graphUser = await graphClient.Me.Request().GetAsync();

            if (!string.IsNullOrEmpty(microsoftUser?.Identity.Name))
            {
                _signedInUser = $"{graphUser.DisplayName} ({graphUser.Mail})";
                //_signedInUser = microsoftUser?.Identity.Name;
            }
        }
        catch (Exception ex)
        {
            try
            {
                _consentHandler.HandleException(ex);
            }
            catch (Exception exConsent)
            {
                ConsoleHelper.WriteLine(ConsoleHelper.Priority.Error, $"Failed to obtain authentication details, or escalate consent due to initial error: \r\n\r\n{ex}, \r\n\r\nFollowed by consent error of:\r\n\r\n{exConsent}");
            }
        }
    }

On first render, the identity is correctly displayed:

image

...then a second call happens, running through exactly the same code and fails:

Code: InvalidAuthenticationToken\r\nMessage: CompactToken parsing failed with error code: 80049217

...and on that second call the graphAccessToken is blank.

@VorTechS
Copy link
Author

I updated the call to Token Acquisition to include the correct scopes as you suggested ("user.read.all ", "directory.read.all"), and now get a consent dialog for 'Need Admin Approval'.

I've reached out to the tenant owner to grant the scopes in question and will see how that affects things.

I'm guessing, from what I read here:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.identity.web.itokenacquisition.getaccesstokenforuserasync?view=msal-model-dotnet-latest

...that the previous calls to Token Acquisition was using an OpenId default on the first call - but then somehow gets lost on subsequent calls?

@dlgombert
Copy link

dlgombert commented Apr 24, 2023

I'm seeing this error: System.TypeLoadException: Could not load type 'Microsoft.Graph.IAuthenticationProviderOption' from assembly 'Microsoft.Graph.Core, Version=3.0.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. which seems very similar. I've tried many different configuration modifications and various tape and glue, but I can't get this to work

Opened separate issue: microsoftgraph/msgraph-sdk-dotnet#1854

@andrueastman
Copy link
Member

I've reached out to the tenant owner to grant the scopes in question and will see how that affects things.

Any update on this @VorTechS?

@andrueastman
Copy link
Member

I'm seeing this error: System.TypeLoadException: Could not load type 'Microsoft.Graph.IAuthenticationProviderOption' from assembly 'Microsoft.Graph.Core, Version=3.0.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. which seems very similar. I've tried many different configuration modifications and various tape and glue, but I can't get this to work

Followed up at microsoftgraph/msgraph-sdk-dotnet#1854

@VorTechS
Copy link
Author

VorTechS commented Apr 25, 2023

I've reached out to the tenant owner to grant the scopes in question and will see how that affects things.

Any update on this @VorTechS?

Sorry. Yes. The tenant owner modified the scopes and the issue remains the same.
This is not an issue that we're not getting the token, or permissions to read AD.

It seems our Blazor Component Initialize event is firing twice (which I believe is normal), and the first time we ask for a Token, we get a token back, which can then be used to query graph as needed.

On the second firing the token is coming back as null.

@inject AuthenticationStateProvider _authProvider
@inject ITokenAcquisition _tokenAcquisition

var microsoftUser = (await _authProvider.GetAuthenticationStateAsync()).User;
var userTenant = microsoftUser.GetTenantId();
var graphAccessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "user.read" }, tenantId: userTenant);

For now, caching that token on the first firing of the event and then re-using it on the subsequent firing seems to get round the issue although that's not ideal.

Refreshing the page with the component on, consistently behaves the same.

@dlgombert
Copy link

dlgombert commented Apr 25, 2023

@VorTechS I first encountered this issue when working with Blazor. I'm now dealing with something similar in my api. In Blazor I found out the solution was to add builder.AddMicrosoftIdentityConsentHandler() in Program.cs. That hasn't worked for me in the api, but perhaps it will serve you.

@VorTechS
Copy link
Author

@VorTechS I first encountered this issue when working with Blazor. I'm now dealing with something similar in my api. In Blazor I found out the solution was to add builder.AddMicrosoftIdentityConsentHandler() in Program.cs. That hasn't worked for me in the api, but perhaps it will serve you.

I've had the ConsentHandling in place from the beginning:

        }
        catch (Exception ex)
        {
            try
            {
                _consentHandler.HandleException(ex);
            }
            catch (Exception permissionDenied)
            {
                // This will happen if the user doesn't have permissions within Azure to do the relevant stuff.
                _azureFailed = true;
            }
        }

@VorTechS
Copy link
Author

So recently I updated all the binaries in our project, and we're now using:

Azure.Core - 1.34
Azure.Identity - 1.10
Microsoft.Identity.Web - 2.13.3
Microsoft.Graph.Core - 2.0.15

(and all the bits these come with) ... and I'm pleased to say this issue no longer happens. Multiple calls to fetch the current token appear to be working (whereas two calls one after the other used to fail), so I'm hoping I can remove the reliance on Token caching now!

There's a potential this issue can be closed, but will confirm on Monday.

@VorTechS
Copy link
Author

VorTechS commented Oct 3, 2023

Right! So I FINALLY have this working perfectly in production, after diagnosing a constantly looping authentication issue on a new deployment slot on Azure. The binary combination above is working perfectly well with an injected GraphServiceClient to achieve what we were struggling to achieve.

The production vs development issue was down to the Client Secret in the App Service Deployment slot not being valid.

Somewhere along the lines there appears to be a valid token when the component first renders (RenderMode = ServerPrerendered) and you can visibly see a user list before it renders a second time.

On that second render, and subsequent call to the Graph API, the 'Client Secret' setting in the App Service Deployment Slot settings is used. In our deployment slot, this wasn't the same as the secret in the Visual Studio appsettings.json.

Once the valid Client Secret was supplied in the deployment slot settings, the production server behaved as would be expected.

I have no idea if this gives any useful information to anyone, or if anything can be learned from this subsequent analysis.

Graph SDK - DotNet automation moved this from Issues to triage to Completed Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Attention 👋 Question: SDK Not actionable. Question related to the SDK.
Projects
No open projects
Graph SDK - DotNet
  
Completed
Development

No branches or pull requests

4 participants