Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Identity as a Service (IDaaS) :  ASP.NET Core API and AWS Cognito

DZone's Guide to

Identity as a Service (IDaaS) :  ASP.NET Core API and AWS Cognito

In this post, we learn how to add authentication to a web application by using the ASP.NET Core API and the AWS Cognito service.

· Security Zone ·
Free Resource

Discover how to provide active runtime protection for your web applications from known and unknown vulnerabilities including Remote Code Execution Attacks.

This is another article in a series on Identity as a Service. In this article, we are going to see how to configure an ASP.NET Core API to validate the identities of the users using AWS Cognito.

For this example, we need the app that we developed in the previous article — Identity as a service (IDaaS): AWS Cognito and ASP.NET Core. In addition, we will continue using Visual Studio 2017 Community Edition.

All code for this example is available on  GitHub.

Protecting API Access

The objective of these examples is to add the necessary validations in the API developed in ASP.NET Core to allow only invocations from apps that have a valid user connected.

This example uses web apps but it is very simple to do the same for single page apps using JavaScript or for a mobile app. The important thing is the propagation of the token from the client to the API, which will validate it before returning the result of the invocation.

This is one of the best aspects of the  OAuth2.0 and  OIDC model. Each layer of our apps do not have to trust whoever contacts them, there is only a relationship of trust with the identity provider. The key element of the model are the  JWTs  ( JSON Web Tokens).

This relationship of trust is achieved through the publication of configuration parameters that are public for any service that wants to use them. New services and layers can be added to our ecosystem without modifying existing services.

Something to keep in mind when we create or modify an API Controller in ASP.NET Core is that the [Authorize] attribute can be applied to a particular method or to the whole class. When applied to the entire class, then all methods will require you to meet the access criteria before responding.

Web API Using AWS Cognito

Let’s start by creating and configuring an ASP.NET Core API to use AWS Cognito as an identity provider. Again, most of the changes have to be made in startup.cs, although, in this example, we will also create a new controller to return one of the JWT token values.

Startup.cs Changes

The first changes necessary are the using

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

Most of the changes are in the ConfigureServices method. Some comments before reviewing the code.

First, instead of harcoding the properties of the JWT validation in the code, we use appsettings.jsonor more flexibility.

Then you have to useAddAuthentication to indicate the Authentication and AddJwtBearer schema to indicate the validation parameters of the JWT, by far the most important section of the code. The MetaDataAddress value depends on our AWS Cognito account, the same value that we use in our Web App.

It is mandatory that our web app and API use the same MetaDataAddress to be able to propagate the identities. TheMetaDataAddress value is the same regardless of the web app registered in AWS Cognito.

Something distinctive about AWS Cognito is that the tokens do not have the Audience claim, so it is necessary to indicate to the ASP.NET validation routines that they must ignore it. If ValidateAudience = false is not included, each validation will give an error.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<JwtBearerOptions>(Configuration.GetSection("Authentication:Cognito"));

    var serviceProvider = services.BuildServiceProvider();
    var authOptions = serviceProvider.GetService<IOptions<JwtBearerOptions>>();

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(options =>
    {
        options.MetadataAddress = authOptions.Value.MetadataAddress;
        options.SaveToken = authOptions.Value.SaveToken;

        options.IncludeErrorDetails = authOptions.Value.IncludeErrorDetails;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = authOptions.Value.TokenValidationParameters.ValidateAudience
        };
    });

    services.AddMvc();
}

And, finally, modify the Configure method to use the Authentication model that we configured in the previous step.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();

    // The rest of the Configure Method

}

Note: Since I was refactoring the API example using Okta, I forgot to include this line... I spent 15 minutes reviewing the rest of the code before I found the error. In each test, the answer was Invalid Token without further explanation in the error message or in the debug log.

New API Controller

Each new API project in VS 2017 creates a default controller API ValuesController. Instead of modifying it, we are going to create a new controller. This will allow us to see that the ValuesController invocations will not require the JWT but it will be necessary for the new controller.

We will call the new API Controller, TokenControllerand we will only implement the action GET that simply returns the date when the token expires. The [Authorize] attribute indicates that a valid user is required to invoke any method of this class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Authorization;

namespace APICognito.Controllers
{
    [Produces("application/json")]
    [Route("api/Token")]
    [Authorize]
    public class TokenController : Controller
    {
        // GET: api/Token
        [HttpGet]
        public string Get()
        {
            string exp = User.Claims.Where(c => c.Type == "exp").First().Value;

            return "Your token is valid until " + (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(Double.Parse(exp)).ToString();
        }
    }
}

After the invocation, we can take the answer and show it on the screen.

Thanks for reading!

All views expressed are my own and do not represent opinions of any entity whatsoever with which I have been, am now, or will be affiliated.

Find out how Waratek’s award-winning application security platform can improve the security of your new and legacy applications and platforms with no false positives, code changes or slowing your application.

Topics:
identity as a service ,security ,authentication ,web application security ,appsec

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}