Security in microservices and API's Keycloak / Auth0

Implementing security in Microservices using OpenID Connect / OAuth 2.0 / JWT in the protection of web pages and API’s.

During this scenario we will use the DevPrime Stack security adapter to enable security settings and the services adapter to query a protected internal api in microservices.

This context requires you to create a service in the Keycloak or Auth0 with the initial settings and get the credentials to use in the tests.

Creating “ms-order” microservices

Use DevPrime CLI to create a new microservices

    dp new ms-order --state mongodb --stream rabbitmq --marketplace order --init

By accessing a web screen through OpenID Connect (OIDC)

We will use the OpenID Connect (OIDC) approach. To speed up the process we will use a DevPrime CLI accelerator that will include a private controller.

    dp add web login

Upon completion it is possible to run the microservices and track the result

    .\run.ps1 ou ./run.sh (Linux, macOS)        

To move forward it is necessary to stop the application. Edit the “Startup.cs” file in the ms-order/src/Adapters/Web folder and modify the “Configure” method according to the template below.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
    {
        //this should always be the first line in this method
        UseDevPrime(app, env, applicationLifetime);
        //Uncomment these lines to enable Authentication
        app.UseCookiePolicy();
        app.UseHsts();
        app.UseHttpsRedirection();
        app.UseRouting();

        //Uncomment this line to enable Authentication
        app.UseAuthentication();

        UseDevPrimeSwagger(app);

        //Uncomment this line to enable UseAuthorization
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Securing access to the web portal

For portal access protection we will configure the security adapter with identity provider data (Keycloak/Auth0). Open the appsettings.json file in the ms-order/src/App folder and modify the data as the example below.

  "DevPrime_Security": {
    "Enable": "true",
    "Type": "keycloak",
    "Domain": "Put your url from identity",
    "ClientId": "Your client id",
    "ClientSecret": "Your client secrets",
    "EnableOIDC": "true",
    "AuthenticationScheme": "OpenIdConnect",
    "LogoutUri": "https://yourkeycloak.com/auth/realms/demo/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Flocalhost%3A5001",
    "Scopes": "openid;email"
  },

After this step run the application again and enter the “Private” link that will attempt to access a protected web screen as demonstrated below and direct access to a login screen.

    [Authorize]
        public IActionResult Private()
        {
            return View();
        }

Under the default security mechanism the identity provider will request the user and password.

After identification, access to the protected resource is released.

Securing API Access

To advance in our scenario we will use DevPrime CLI to create a new microservices called “ms-delivery”. at the first moment we will protect the API of this new microservices and later simulate an access using HTTP from “ms-order” to “ms-delivery”.

    dp new ms-delivery --state mongodb --stream rabbitmq --marketplace delivery --init

We will now edit the appsettings.json file available in the ms-delivery/src/App folder to configure the web adapter and then the adapter security. Add the value of the url parameter to avoid port conflict.

  "DevPrime_Web": {
    "url": "https://localhost:5003;http://localhost:5002",
    "enable": "true",
    "enableswagger": "true",
    "PostSuccess": "201",
    "PostFailure": "500",
    "GetSuccess": "200",
    "GetFailure": "500",
    "PatchSuccess": "200",
    "PatchFailure": "500",
    "PutSuccess": "200",
    "PutFailure": "500",
    "DeleteSuccess": "200",
    "DeleteFailure": "500"
  },

Add the security setting for API protection. For API’s scenario we will leave the parameter “EnableOIDC” as false.

 "DevPrime_Security": {
    "Enable": "true",
    "Type": "keycloak",
    "Domain": "https://your-keycloak.com/auth/realms/demo",
    "Audience": "myclient",
    "EnableOIDC": "false"
  },

Then change the Startup.cs file in the ms-order/src/Adapters/Web folder by modifying the “Configure” method below with the objtivo of complementing the API security settings.

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
        {
            //this should always be the first line in this method
            UseDevPrime(app, env, applicationLifetime);
            //Uncomment these lines to enable Authentication
            app.UseCookiePolicy();
            app.UseHsts();
            app.UseHttpsRedirection();
            app.UseRouting();

            //Uncomment this line to enable Authentication
            app.UseAuthentication();

            UseDevPrimeSwagger(app);

            //Uncomment this line to enable UseAuthorization
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

After completing this stage the API access security control has already been configured. Find the DeliveryController .cs in the ms-delivery/src/Adapters/Web/Controllers folder to configure the access restriction on a controller method in the API. In the example below we will apply in the item “add”.

[Authorize]
[HttpPost]
public async Task<Result> Add([FromBody] DevPrime.Web.ViewModels.Delivery.Delivery DeliveryAdd)
    {
         return await Dp.PipelineAsync(Execute: () =>
         {
             var command = DeliveryAdd.ToApplication();
             Service.Add(command);
         });
    }

Run the “ms-delivery” application and try to post directly to the API and find the example below.

After confirming the “Post” we will have an error “401 Unauthorized”

Accessing a protected API

At this point return to the microservice “ms-order” and modify the configuration in the adapter services. Open the appsettings.json file in ms-order/src/App and include the configuration with the credentials that we will use in communication between microservices.

 "DevPrime_Services": {
    "Timeout": "10",
    "CircuitBreak": "45",
    "Retry": "3",
    "connections": [
      {
        "Timeout": "10",
        "name": "myprivate",
        "TokenUri": "Put your",
        "ClientId": "myclient",
        "ClientSecret": "Put your secret",
        "GrantType": "client_credentials"
      }
    ]
  },

To demonstrate our scenario, open the OrderCreatedEventHandler file.cs in the ms-order/src/Core/Application/EventHandlers/Order folder, and modify handle by including an http call to demonstrate access to a private API in “ms-delivery” microservices.

public override dynamic Handle(OrderCreated domainEvent)
{
            var order = domainEvent.Get<Domain.Aggregates.Order.Order>();
            Dp.State.Order.Add(order);

            // Try post to private api
            var url = "https://localhost:5003/v1/delivery";
            var data = new { started = DateTime.Now, finished = DateTime.Now, orderID = order.ID, paymentID = new Guid() };
            var httpParams = new HTTPParameter(url)
            {
                Content = JsonSerializer.Serialize(data),
                Connection = "myprivate" // Security Token
            };

            // Post
            var result = Dp.Services.HTTP.Post(httpParams);

            Dp.Observability.Log($"Status:{result.Status}");

            return true;
}

Now it’s time to validate the whole scenario. Start the microservices “ms-order” and “ms-delivery”. Enter the “ms-order” microservices and post the API. Upon arriving at Handle the services adapter will automatically get a tocken using the settings and access the private API automatically.

Well, congratulations🚀. You have protected a web application and accessed a private API.

Last modified March 10, 2022 (615ec2b)