3

I'm new to AWS and need to secure access to a .NET Core Web API application using Cognito. I'm largely following the excellent video on YouTube by Les Jackson (https://www.youtube.com/watch?v=3PyUjOmuFic) but he uses Azure rather than AWS.

So far, I have a very simple API controller action that just pulls some records from SQL Server and sends a JSON response.

        [HttpGet]
        public async Task<IEnumerable<TodoItem>> Get()
        {
            var items = await context.TodoItem.ToListAsync();
            return items;
        }

This works fine locally in Visual Studio and also when deployed to an AWS instance using Elastic Beanstalk.

To secure the application I added to the ConfigureServices method in Startup.cs

    services.AddAuthentication("Bearer")
        .AddJwtBearer(options =>
        {
            options.Audience = "App client id";
            options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/Cognito User Pool id";
        });

And in the Configure method

            app.UseAuthentication();

I added a controller to the application just to authenticate with Cognito and get back an Access token.

        private const string _clientId = "App client id";
        private readonly RegionEndpoint _region = RegionEndpoint.EUWest2;

        [HttpPost]
        [Route("/api/signin")]
        public async Task<ActionResult<string>> SignIn(User user)
        {
            var cognito = new AmazonCognitoIdentityProviderClient(_region);

            var request = new AdminInitiateAuthRequest
            {
                UserPoolId = "Cognito User Pool id",
                ClientId = _clientId,
                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 Ok(response.AuthenticationResult);
        }

After a bit of messing around I've got this working locally and on AWS, returning tokens when a Cognito user name and password are POSTed using Postman, e.g.

{
    "accessToken": ".....",
    "expiresIn": 3600,
    "idToken": "...",
    "newDeviceMetadata": null,
    "refreshToken": "...",
    "tokenType": "Bearer"
}

So, I added the [Authorize] attribute to my API method, got an Access Token using the SignIn method and constructed a request in Postman that includes the Authorization HTTP header with the value "Bearer " + accessToken.

Sadly, this is not working and I can't think of anything else to try.

Using Postman requests to the application running locally in IIS Express via Visual Studio, I get the response

System.InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
 ---> System.IO.IOException: IDX20807: Unable to retrieve document from: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. HttpResponseMessage: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]', HttpResponseMessage.Content: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.

When the application is deployed using Elastic Beanstalk I just get a 500 Internal Server Error with no additional messages.

What am I doing wrong? I would be grateful for any help / pointers.

Doug

Doug
  • 457
  • 5
  • 17
  • Does this answer your question? [JWT token authentication fails with message "PII is hidden"](https://stackoverflow.com/questions/55300231/jwt-token-authentication-fails-with-message-pii-is-hidden) – Roar S. Aug 24 '20 at 18:29

1 Answers1

4

I'm not too sure why, but it seems to be working OK now!

The final code I ended up with is:

In ConfigureServices, Startup.cs

            services.AddAuthentication("Bearer")
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false };
                    options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_xxxxxxxxx";
                    options.RequireHttpsMetadata = false;
                });

In Configure, Startup.cs

            app.UseAuthentication();

In appsettings.json

  "AWSCognito": {
    "Region": "{Region}",
    "PoolId": "{PoolId}",
    "AppClientId": "{AppClientId}"
  }

When an access token is supplied in the Authorization HTTP header, calls to API methods decorated with the [Authorize] attribute succeed and User.Identity.Claims.Where(c => c.Type == "username").FirstOrDefault() gives the user Id of the authenticated user.

Thanks for the suggestions.

Doug
  • 457
  • 5
  • 17