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

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

DZone's Guide to

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

In the third part of this series on IDaaS, we take a look at how to use these two tools to validate the identity of users.

· 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 the third article in a series on Identity as a Service (you can see Part 1 here and Part 2 here). In this article, we are going to see how to configure an ASP.NET Core API to validate the identities of users using Okta.

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

All the 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 app 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 app does 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 Okta

Let’s start by creating and configuring an ASP.NET Core API to use Okta 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 those to using

using Microsoft.AspNetCore.Authentication.JwtBearer;

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

First, instead of hardcoding the properties of the JWT validation in the code, we use appsettings.json for more flexibility.

Then you have to useAddAuthentication to indicate the Authentication and an AddJwtBearer schema to indicate the validation parameters of the JWT, by far the most important section of the code. The authority value depends on our Okta account, the same value that we use in our web app.

It is mandatory that our web app and API use the same authority to be able to propagate the identities. The authority value is the same regardless of the web app registered in Okta
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<JwtBearerOptions>(Configuration.GetSection("Authentication:Okta"));

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

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.Authority = authOptions.Value.Authority;
        options.Audience = authOptions.Value.Audience;
    });

    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

}

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 TokenController and 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 APIOkta.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();
        }
    }
}

Invoking From the Web App

With the API ready, we are going to modify the web app to invoke the new API Controller. We are going to modify the page 'About' — About.cshtml.cs —which, by default, means creating a web app in Visual Studio 2017.

Using HttpClient we invoke the API. The important thing is to include the access token that Okta provides to the web app so that the API gives us the correct answer and not an error.

A critical configuration for this example is the SaveTokens property in the OIDC settings in the web app.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;

using Microsoft.AspNetCore.Authentication;
using System.Net.Http;
using System.Net;

namespace WebAppOkta.Pages
{
    public class AboutModel : PageModel
    {
        public string Message { get; set; }

        private static HttpClient client = new HttpClient();

        public async Task OnGetAsync()
        {
            if (User.Identity.IsAuthenticated)
            {
                string accessToken = await HttpContext.GetTokenAsync("access_token");

                client.BaseAddress = new Uri("http://localhost:59569/");

                var request = new HttpRequestMessage()
                {
                    RequestUri = new Uri(baseURL + "/api/token"),
                    Method = HttpMethod.Get
                };
                request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + accessToken);

                var result = client.SendAsync(request).Result;

                Message = result.StatusCode == HttpStatusCode.OK ? result.Content.ReadAsStringAsync().Result : "Error HTTP Code: " + result.StatusCode;
            }
        }
    }
}

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 ,web application security ,api security ,user identity

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}