Identityserver4: [3.0] TypeLoadException: Could not load type 'IdentityModel.EpochTimeExtensions'

Created on 24 Jul 2019  路  7Comments  路  Source: IdentityServer/IdentityServer4

Issue / Steps to reproduce the problem

I'm upgrading my project from asp.net Core 2.2.6 and IdentityServer4 2.5.0 to .net Core 3.0.0-preview7.19365.7 and IdentityServer4 3.0.0-preview3.4 (The latest one available on NuGet).

When user trying to sign in, it throws the following error:
TypeLoadException: Could not load type 'IdentityModel.EpochTimeExtensions' from assembly 'IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e7877f4675df049f'.
IdentityServer4.Hosting.IdentityServerAuthenticationService.AugmentMissingClaims(ClaimsPrincipal principal, DateTime authTime)

Everything works as expected with stable versions of .net Core and IdentityServer4.

Did I missed some important breaking change?
Sorry if it's a known issue.

dotnet --version: 3.0.100-preview7-012821

Exception details

System.TypeLoadException: Could not load type 'IdentityModel.EpochTimeExtensions' from assembly 'IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e7877f4675df049f'.

   at IdentityServer4.Hosting.IdentityServerAuthenticationService.AugmentMissingClaims(ClaimsPrincipal principal, DateTime authTime)

   at IdentityServer4.Hosting.IdentityServerAuthenticationService.AugmentPrincipal(ClaimsPrincipal principal)

   at IdentityServer4.Hosting.IdentityServerAuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)

   at Microsoft.AspNetCore.Identity.SignInManager`1.SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable`1 additionalClaims)

   at Microsoft.AspNetCore.Identity.SignInManager`1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor)

   at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(TUser user, String password, Boolean isPersistent, Boolean lockoutOnFailure)

   at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(String userName, String password, Boolean isPersistent, Boolean lockoutOnFailure)

   at FixMate.Server.Areas.Account.Controllers.IdentityController.Login(LoginViewModel model, String returnUrl) in C:\src\fixmate\Source\fixmate.server\Areas\Account\Controllers\IdentityController.cs:line 76

   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)

   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)

   at IdentityServer4.Hosting.MutualTlsTokenEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)

   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)

   at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.SpaServices.Webpack.ConditionalProxyMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Log

[18:20:35 INF] Starting IdentityServer4 version 3.0.0.0
[18:20:35 INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation.
[18:20:35 INF] Using the default authentication scheme Identity.Application for IdentityServer
[18:20:35 DBG] Using Identity.Application as default ASP.NET Core scheme for authentication
[18:20:35 DBG] Using Identity.External as default ASP.NET Core scheme for sign-in
[18:20:35 DBG] Using Identity.External as default ASP.NET Core scheme for sign-out
[18:20:35 DBG] Using Identity.Application as default ASP.NET Core scheme for challenge
[18:20:35 DBG] Using Identity.Application as default ASP.NET Core scheme for forbid

Related packages in my project file

    <PackageReference Include="IdentityServer4" Version="3.0.0-preview3.4" />
    <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.0-preview.2" />
    <PackageReference Include="IdentityServer4.AspNetIdentity" Version="3.0.0-preview3.4" />
    <PackageReference Include="IdentityServer4.EntityFramework" Version="3.0.0-preview3.4" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0-preview7.19365.7" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0-preview7.19365.7" />

question

Most helpful comment

So, if anyone other than me has an actual use case where you are handling multiple apps in a suite using multiple web hosts and reverse proxies in a single core application... and have an api and need to use open ID and jwt bearer tokens, this version difference is a SILLY frustration. To get around it, I grabbed a copy of identity model 4.0.0.0 and embedded it as a resource, then overrode the AssemblyResolve event to create the version of the assembly that the jwt decided to need an EXACT version of.. this might save you the 6 to 8 hours of fun I had with this.

In initialization somewhere, I put this in my ConfigureServices:

  AppDomain.CurrentDomain.AssemblyResolve += this.CurrentDomain_AssemblyResolve;

and then the handler:

private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender,
  ResolveEventArgs args)
{
  System.Reflection.Assembly ret;

  if (args.Name.StartsWith("IdentityModel,") &&
    args.Name.Contains("=4.0.0.0,"))
  {
    byte[] bytes;
    Stream stream;

    stream = GetType().Assembly.GetManifestResourceStream("$YOUR_NAME_SPACE_.IdentityModelDll._4_0_0.IdentityModel.dll");
    bytes = new byte[stream.Length];
    stream.Read(bytes, 0, bytes.Length);
    ret = System.Reflection.Assembly.Load(bytes);
  }
  else
  {
    // I did this in a class library with just the one reference -- if you don't do the same be sure and call the default assembly loader or things won't work!
    ret = null;
  }
  return ret;
}

If you are new to using embedded resources, by chance, after getting the Identity Model 4.0.0.0 dll which is 130,488 bytes on disk into a folder in your project or library, right click it, change build to 'embedded resource'. Then, in debug mode you can stop in that function above and then in a debug inspection, insepect GetType().Assembly.GetManifestResourcesNames() and find the exact name for the GetManifestResourceStream call above. Even getting the 4.0.0.0 dll file was an issue because nuget restore wasn't bothering to download the package. To get around that, I made a dummy app and added IdentityModel 4.0.0.0 to it (be sure to turn on prerelease versions in nuget) and then grabbed the dll from disk to copy into the sub-folder.

All 7 comments

IdentityServer4 3.0.0-preview3.4 references IdentityModel (>= 3.10.6) . not 4.x. Then it works.

Oook, I see now. IdentityServer4 3.0.0-preview3.4 references IdentityModel (>= 3.10.6), yes. But I also have reference to IdentityServer4.AccessTokenValidation 3.0.0-preview.2, which depends on IdentityModel.AspNetCore.OAuth2Introspection (>= 4.0.0-preview.6) which in turn depends on IdentityModel (>= 4.0.0-preview.2.13).

There is no use case for referencing both IdentityServer and AccessTokenValidation in a single application. Why are you doing that?

Well, I need to authenticate SignalR web-socket request and using the workaround from #2349. I needed to access TokenRetrieval class and found it in the IdentityModel.AspNetCore.OAuth2Introspection which was available in my project because AccessTokenValidation package (I don't remember why it was there). Stupid me, I didn't realise that I can reference IdentityModel.AspNetCore.OAuth2Introspection directly.

Now I referencing IdentityModel.AspNetCore.OAuth2Introspection 3.5.0 and it works now. So, @leastprivilege thank you for pointing me into right direction.

OK. great.

So, if anyone other than me has an actual use case where you are handling multiple apps in a suite using multiple web hosts and reverse proxies in a single core application... and have an api and need to use open ID and jwt bearer tokens, this version difference is a SILLY frustration. To get around it, I grabbed a copy of identity model 4.0.0.0 and embedded it as a resource, then overrode the AssemblyResolve event to create the version of the assembly that the jwt decided to need an EXACT version of.. this might save you the 6 to 8 hours of fun I had with this.

In initialization somewhere, I put this in my ConfigureServices:

  AppDomain.CurrentDomain.AssemblyResolve += this.CurrentDomain_AssemblyResolve;

and then the handler:

private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender,
  ResolveEventArgs args)
{
  System.Reflection.Assembly ret;

  if (args.Name.StartsWith("IdentityModel,") &&
    args.Name.Contains("=4.0.0.0,"))
  {
    byte[] bytes;
    Stream stream;

    stream = GetType().Assembly.GetManifestResourceStream("$YOUR_NAME_SPACE_.IdentityModelDll._4_0_0.IdentityModel.dll");
    bytes = new byte[stream.Length];
    stream.Read(bytes, 0, bytes.Length);
    ret = System.Reflection.Assembly.Load(bytes);
  }
  else
  {
    // I did this in a class library with just the one reference -- if you don't do the same be sure and call the default assembly loader or things won't work!
    ret = null;
  }
  return ret;
}

If you are new to using embedded resources, by chance, after getting the Identity Model 4.0.0.0 dll which is 130,488 bytes on disk into a folder in your project or library, right click it, change build to 'embedded resource'. Then, in debug mode you can stop in that function above and then in a debug inspection, insepect GetType().Assembly.GetManifestResourcesNames() and find the exact name for the GetManifestResourceStream call above. Even getting the 4.0.0.0 dll file was an issue because nuget restore wasn't bothering to download the package. To get around that, I made a dummy app and added IdentityModel 4.0.0.0 to it (be sure to turn on prerelease versions in nuget) and then grabbed the dll from disk to copy into the sub-folder.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

garymacpherson picture garymacpherson  路  3Comments

chrisrestall picture chrisrestall  路  3Comments

user1336 picture user1336  路  3Comments

krgm03 picture krgm03  路  3Comments

wangkanai picture wangkanai  路  3Comments