Tuesday 24 January 2023

User Authentication with AWS Cognito with Web API Backend and Frontend Web
Part 1 - Setting up AWS Cognito and backend Web API user functions

We will set up a simple Angular frontend web app with ASP.NET Core Web API backend (.NET 5.0) using AWS Cognito as authentication provider. Users will be able to sign up, confirm sign up/verify and sign in. The backend WebAPI will be the point of contact and interaction with AWS Cognito. The frontend Angular web app will simply pass user information and then keep the Cognito Access Token passed by the backend WebAPI.

This writing will be divided into three parts:
  1. Set up AWS Cognito and backend Web API user functions - this post
  2. Configure CORS in Web API backend - next post
  3. Authentication between frontend and Web API backend - next post

On this first series, we will set up AWS Cognito and ASP.NET Core Web API backend (.NET 5.0) with some user sign up an sign in functions.

1. First, create a new user pool in AWS Cognito then create a new App Client. Make sure 'Enable username password auth for admin APIs for authentication (ALLOW_ADMIN_USER_PASSWORD_AUTH)' is selected:


2. Create an ASP.NET Core Web API project. On Startup.cs file inside ConfigureServices method set options.Audience and options.Authority
public void ConfigureServices(IServiceCollection services)
{
	// AWS Cognito
	services.AddAuthentication("Bearer")
	.AddJwtBearer(options =>
	{
		options.Audience = "[APP_CLIENT_ID]";
		options.Authority = "https://cognito-idp.[REGION_NAME].amazonaws.com/[USER_POOL_ID]";
	});

	. . .
}
App Client Id is shown on:

Then for the Authority field, we can get Region and User Pool Id from


3. In Configure(IApplicationBuilder app, IWebHostEnvironment env) method, add
app.UseAuthentication();
after app.UseRouting() is called.

4. Make sure your project has this AWSSDK.CognitoIdentityProvider package installed

5. Then we can add sign up feature in a controller
[HttpPost]
[Route("api/signup")]
public async Task<ActionResult<string>> SignUp(User user)
{
	var cognito = new AmazonCognitoIdentityProviderClient(_region);

	var request = new SignUpRequest
	{
		ClientId = _clientId,
		Password = user.Password,
		Username = user.Username
	};
    
	// Cognito email attribute
	var emailAttribute = new AttributeType
	{
		Name = "email",
		Value = user.Email
	};
	request.UserAttributes.Add(emailAttribute);

	var response = await cognito.SignUpAsync(request);

	return Ok(response);
}

6. When a user sign up with the method above, its Account Status is "Unconfirmed" and Email Verified is "false". A confirmation email will be sent with a code. We need to add another function to handle this.
[HttpPost]
[Route("api/confirmSignUp")]
public async Task<ActionResult<string>> ConfirmSignUp(string username, string confirmationCode)
{
	var cognito = new AmazonCognitoIdentityProviderClient(_region);

	var request = new ConfirmSignUpRequest
	{
		ClientId = _clientId,
		Username = username,
		ConfirmationCode = confirmationCode
	};

	var response = await cognito.ConfirmSignUpAsync(request); // after calling this method, user's Account Status will become 'Confirmed' and Email Verified become 'true'

	return Ok(response);
}

7. Then the sign in function. This will return an Access Token if successful.
[HttpPost]
[Route("api/signin")]
public async Task<ActionResult<string>> SignIn([FromBody] User user)
{
	var cognito = new AmazonCognitoIdentityProviderClient(_region);

	var request = new AdminInitiateAuthRequest
	{
		UserPoolId = _userPoolId, // User Pool Id
		ClientId = _clientId, // App Client Id
		AuthFlow = AuthFlowType.ADMIN_USER_PASSWORD_AUTH
	};

	request.AuthParameters.Add("USERNAME", user.Username);
	request.AuthParameters.Add("PASSWORD", user.Password);

	var response = await cognito.AdminInitiateAuthAsync(request);

	return Json(response.AuthenticationResult.AccessToken);
}

Additional resend confirmation code and find user functions:
[HttpPost]
[Route("api/resendConfirmationCode")]
public async Task<ActionResult<string>> ResendConfirmationCode(string username)
{
	var cognito = new AmazonCognitoIdentityProviderClient(_region);

	var request = new ResendConfirmationCodeRequest
	{
		ClientId = _clientId,
		Username = username
	};

	var response = await cognito.ResendConfirmationCodeAsync(request);

	return Ok(response);
}


[HttpPost]
[Route("api/findUser")]
public async Task<ActionResult<string>> FindUser(string username)
{
	var cognito = new AmazonCognitoIdentityProviderClient(_region);

	var request = new AdminGetUserRequest
	{
		UserPoolId = _userPoolId,
		Username = username
	};
	var response = await cognito.AdminGetUserAsync(request);

	return Ok(response);    
}

On the next post, we will set up CORS (Cross-Origin Resource Sharing) in the project.

No comments: