- ABP Framework version: v4.2.2
- UI type: Angular / MVC / Blazor
- DB provider: EF Core
- Tiered (MVC) or Identity Server Separated (Angular): yes
- Exception message and stack trace: AbpIdentityResultException: Invalid token.
An unhandled exception has occurred while executing the request.
Volo.Abp.Identity.AbpIdentityResultException: Invalid token.
   at Microsoft.AspNetCore.Identity.AbpIdentityResultExtensions.CheckErrors(IdentityResult identityResult)
   at Volo.Abp.Account.AccountAppService.ConfirmEmailAsync(ConfirmEmailInput input) in /home/ruben/FrogSlayer/Projects/LaborLoop/laborloop-api/modules/Volo.Account.Pro/src/Volo.Abp.Account.Pro.Public.Application/Volo/Abp/Account/AccountAppService.cs:line 173
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
   at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
   at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
   at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
   at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at Volo.Abp.Account.Public.Web.Pages.Account.EmailConfirmationModel.OnGetAsync() in /home/ruben/FrogSlayer/Projects/LaborLoop/laborloop-api/modules/Volo.Account.Pro/src/Volo.Abp.Account.Pro.Public.Web/Pages/Account/EmailConfirmation.cshtml.cs:line 41
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.NonGenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(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__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Volo.Abp.AspNetCore.Serilog.AbpSerilogMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)
   at IdentityServer4.Hosting.MutualTlsEndpointMiddleware.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 Volo.Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Volo.Abp.AspNetCore.Tracing.AbpCorrelationIdMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
- Steps to reproduce the issue: - Go to Landing page of Angular application
- Click on register new user
- Fill out information
- Click register
- Recieve email with confirmation link
- Click on the link
 
- Additional Info: - We are using a postgres DB and redis cache
- I verified that the token being generated is the same that is being recieved by the identity server
- The identity server and api are running on different instances
- Looking at the redis cache I see what should be the shared key between the two applicatons
 
For some reason the confirmation token in the email is always invalid. I double checked to make sure that the token being generated when the user is being created is the same as what is in the email and then checked again that the string matches before it tries to validate it on the identity server. Any help/insight would be appreciated.
32 Answer(s)
- 
    0I think everything is ok locally, right? Can you add below code to identity server and api? public void ConfigureServices(IServiceCollection services) { var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services.AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "Your-DataProtection-Keys") .SetApplicationName("YourAppName"); }
- 
    0It is not working locally either. I have added that and it still gives me the same error. I have also tried using that along with DisableAutomaticKeyGeneration()with no luck also.
- 
    0Will it work if identity server and api are run on one instance? 
- 
    0Locally they run on the same instance but are started on separate processes and it still does not work 
- 
    0hi Can i check it remotely? https://zoom.us/j/92883778530?pwd=SHRCbEd0UndjYzc5R2szVlJiLzJzQT09 
- 
    0hi I checked, the problem should DataProtectorTokenProvider. You can inject the UserManagerin your identity server project, then callGenerateEmailConfirmationTokenAsyncandConfirmEmailAsyncto check if the function is works.public IdentityUserManager UserManager { get; set; } public virtual Task<string> GenerateEmailConfirmationTokenAsync(TUser user); public virtual async Task<IdentityResult> ConfirmEmailAsync(TUser user, string token)
- 
    0I have tried that on both the api and identity server and they work when they are called together on each individual application. The issue with that is that the GenerateEmailConfirmationTokenAsyncis being called on the api whileConfirmEmailAsyncis on the idenentity server so having the same UserManager is not possible accross both.
- 
    0OK, I will investigate this. 
- 
    0hi If two instances use the same keyand sameApplicationName, it is possible to call each other.https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0 
- 
    0Is that not what we did when you said to do this? I think everything is ok locally, right? Can you add below code to identity server and api? public void ConfigureServices(IServiceCollection services) { var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services.AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "Your-DataProtection-Keys") .SetApplicationName("YourAppName"); }
- 
    0Is that not what we did when you said to do this? Yes, Can you try on another computer? Let us rule out environment. 
- 
    0I have tried it on two different computers with the same result 
- 
    0hi I will check again. 
- 
    0
- 
    0The issue with that is that the GenerateEmailConfirmationTokenAsync is being called on the api while ConfirmEmailAsync is on the idenentity server so having the same UserManager is not possible accross both. I'm curious how this happened. 
- 
    0Yes, we implemented a custom registration process and put the registration API in the API project. We are still using the the built in Registration method that is in the Applications project 
- 
    0
- 
    0Looking at your example, we are doing almost the same thing but with two differences - We are using the built in AccountAppService class from ABP and calling SendingEmailConfirmationTokenAsyncfrom the API andConfirmEmailAsyncfrom the Identity Server which callsUserManager.ConfirmEmailAsync
- Since both instances are separate in our production environment we cannot persist the keys on the local file system
 Is the built in functionality in the AccountAppService class not suitable for this? Do we need to just use our own implementation instead? 
- We are using the built in AccountAppService class from ABP and calling 
- 
    0Since both instances are separate in our production environment we cannot persist the keys on the local file system You can use other provider to save your keys. https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0 
- 
    0Yes, and as stated in previous messages and seen in our zoom call we are using Redis to save the keys 
- 
    0hi Can you check your redis to see if the keys are created? I think your Redis may have problem, Because we've confirmed the Data Protection can works between two application. 
- 
    0I checked and Redis is not the problem. What is happening is that in ConfirmEmailAsyncthe line that callsUserManager.ConfirmEmailAsyncis not using Redis for the key. When I change it to callUserManager.VerifyUserTokenAsyncdirectly it does. Can you check as to why this is happening? Since this is theIdentityUserManagerfor the ABP library and the code in question is also from the ABP library I would have expected the using the code given to us would work out of the box.This is all in the AccountAppServiceclass fromVolo.Abp.Account.Pro.Public.Applicationproject using theVolo.Abp.Accountnamespace
- 
    0hi Can you check the tokenProvider and purpose?https://github.com/dotnet/aspnetcore/blob/52eff90fbcfca39b7eb58baad597df6a99a542b0/src/Security/samples/Identity.ExternalClaims/Pages/Account/ConfirmEmail.cshtml.cs#L34 https://github.com/dotnet/aspnetcore/blob/af9bb41d623b17eec946029815db5554b73be156/src/Identity/Extensions.Core/src/UserManager.cs#L1495 
- 
    0We are using the IdentityUserManagerclass from theVolo.Abp.Identityidentity package so im not sure if I am following you. We have not modified or changed that class so im not sure how to check those unless it is in a setting somewhere.
- 
    0hi Can you share a demo project that can simple reproduce the problem? liming.ma@volosoft.com 


 
                                