aws / aws-aspnet-cognito-identity-provider

ASP.NET Core Identity Provider for Amazon Cognito
https://aws.amazon.com/developer/language/net/
Apache License 2.0
215 stars 89 forks source link

Issues with SignUpAsync when Cognito is configured for username as the email address #40

Closed jlwhitfill closed 5 years ago

jlwhitfill commented 5 years ago

So after some extensive use of this package for the last few days, I have noticed that when you setup Cognito to use the email address as the username as shown below:

image

I started to have a few issues, one I was able to get around, the other I cannot and am stuck. In your sample, in the Register.cshtml.cs file, first thing, you have to change is the Cognito user creation to use the Email address instead of the username (which is what I would have expected):

CognitoUser user = _pool.GetUser(Input.Email);

However, when the next piece of code runs:

IdentityResult result = await _userManager.CreateAsync(user, Input.Password).ConfigureAwait(false);
if (result.Succeeded)
{
    _logger.LogInformation("User created a new account with password."); 
    await _signInManager.SignInAsync(user, false).ConfigureAwait(false);
    return RedirectToPage("./ConfirmAccount");
}

This piece of code fails with a USER NOT FOUND error:

await _signInManager.SignInAsync(user, false).ConfigureAwait(false);

It fails because the "user" variable that was created at the beginning has a userid = the email address, which is NOT how cognito stores it. When the user is created in Cognito uising this configuration, the userid is replaced with a randomly generated GUID/UUID as shown below:

image

The current Cognito "user" variable is not updated or passed back after the CreateAsync is called. Once I figured this out I inserted an additional line in the above code to refresh the user by looking them up by their email address

user = await _userManager.FindByEmailAsync(Input.Email).ConfigureAwait(false);

Once I did that, THEN I could see that the username/userid in the newly retrieved "user" variable contained the GUID/UUID. Successively, the SignInAsync call worked and after that the Confirm also worked. So I was able to work around that issue, however, I have to make and additional call to get proper user with the proper ID in the Cognito user object.

Now I go to logout and in the samples LogOut.cshtml.cs page code, it's a simple call:

await _signInManager.SignOutAsync().ConfigureAwait(false);

When that call is made it throws an error trying to find the current user to log them out and of course the "username" field is incorrect as its not getting what it needs:

InvalidParameterException: 1 validation error detected: Value at 'username' failed to satisfy constraint: Member must satisfy regular expression pattern: [\p{L}\p{M}\p{S}\p{N}\p{P}]+

CognitoServiceException: Failed to find the Cognito User by Id

Somehow this needs to be adjusted to account for this email as the username/userid structure or, it has to let me pass a Cognito user with the proper id as an overload. Right now Im dead in the water because I cant log my user out. I tried to get the Cognito user and then perform a GlobalSignOut with the following code:

string userId = User.Claims.FirstOrDefault().Value;
CognitoUser user = await _userManager.FindByIdAsync(userId).ConfigureAwait(false);
await user.GlobalSignOutAsync().ConfigureAwait(false);

It retrieves the correct user with the correct GUID/UUID, but this also failed with an error that the user is not authenticated?!?! Not sure how that can be when the SignInAsyc from the Login page is successful.

NotAuthorizedException: User is not authenticated.
Amazon.Extensions.CognitoAuthentication.CognitoUser.EnsureUserAuthenticated()
Amazon.Extensions.CognitoAuthentication.CognitoUser.GlobalSignOutAsync()
Samples.Areas.Identity.Pages.Account.LogoutModel.OnPost(string returnUrl) in Logout.cshtml.cs
+
await user.GlobalSignOutAsync().ConfigureAwait(false);
Microsoft.AspNetCore.Mvc.RazorPages.Internal.ExecutorFactory+GenericTaskHandlerMethod.Convert<T>(object taskAsObject)
Microsoft.AspNetCore.Mvc.RazorPages.Internal.ExecutorFactory+GenericTaskHandlerMethod.Execute(object receiver, object[] arguments)
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker.InvokeHandlerMethodAsync()
assyadh commented 5 years ago

Thank you for reporting the issue, I'll try to reproduce it.

jlwhitfill commented 5 years ago

This new update 0.9.0.2 with the removal of the SignoutAsync overload solved this issue. I am no longer having issues with the SignOutAsyc it is now properly logging me out and I am not seeing any Stack Overflows as of yet, but they take some time to appear when they do after that command is issued. Thanks for your help!