- 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)
-
0
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"); }
-
0
It 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. -
0
Will it work if identity server and api are run on one instance?
-
0
Locally they run on the same instance but are started on separate processes and it still does not work
-
0
hi
Can i check it remotely? https://zoom.us/j/92883778530?pwd=SHRCbEd0UndjYzc5R2szVlJiLzJzQT09
-
0
hi
I checked, the problem should DataProtectorTokenProvider.
You can inject the
UserManager
in your identity server project, then callGenerateEmailConfirmationTokenAsync
andConfirmEmailAsync
to 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)
-
0
I 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
GenerateEmailConfirmationTokenAsync
is being called on the api whileConfirmEmailAsync
is on the idenentity server so having the same UserManager is not possible accross both. -
0
OK, I will investigate this.
-
0
hi
If two instances use the same
key
and 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
-
0
Is 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"); }
-
0
Is that not what we did when you said to do this?
Yes, Can you try on another computer? Let us rule out environment.
-
0
I have tried it on two different computers with the same result
-
0
hi
I will check again.
-
0
-
0
The 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.
-
0
Yes, 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
-
0
Looking 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
SendingEmailConfirmationTokenAsync
from the API andConfirmEmailAsync
from 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
-
0
Since 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
-
0
Yes, and as stated in previous messages and seen in our zoom call we are using Redis to save the keys
-
0
hi
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.
-
0
I checked and Redis is not the problem. What is happening is that in
ConfirmEmailAsync
the line that callsUserManager.ConfirmEmailAsync
is not using Redis for the key. When I change it to callUserManager.VerifyUserTokenAsync
directly it does. Can you check as to why this is happening? Since this is theIdentityUserManager
for 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
AccountAppService
class fromVolo.Abp.Account.Pro.Public.Application
project using theVolo.Abp.Account
namespace -
0
hi
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
-
0
We are using the
IdentityUserManager
class from theVolo.Abp.Identity
identity 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. -
0
hi
Can you share a demo project that can simple reproduce the problem?
liming.ma@volosoft.com