> ## Content Index
> Fetch the complete content index at: https://www.azurebrasil.cloud/llms.txt
> Use this file to discover other available public pages before exploring further.

# Como proteger um gateway Ocelot com o Azure AD
- URL: https://www.azurebrasil.cloud/blog/como-proteger-um-gateway-ocelot-com-o-azure-ad/
- Published: 2023-09-21T20:57:52.000Z
- Updated: 2023-09-21T20:57:52.000Z
- Author: Talles Valiatti

Nesses últimos dias, me deparei com um desafio e por isso resolvi escrever esse post. O desafio foi: como podemos utilizar o Azure AD para proteger um *gateway* feito com o Ocelot?

Antes de começarmos, sugiro um pequeno estudo sobre o que são *[gateways](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern?ref=azurebrasil.cloud)* e a lib [Ocelot](https://ocelot.readthedocs.io/en/latest/index.html?ref=azurebrasil.cloud) para .NET!

Vamos inicialmente criar um *web api* para servir como fonte de dados. Em uma aplicação de produção, essa aplicação não seria exposta na internet, e poderia ser consumida (ou não, dependendo do caso) via *gateway.*

```
dotnet new webapi -n App.Api
```

Nesse projeto, essa *web api* será protegida somente pelo *gateway,* não precisando validar *access tokens* para cada requisição*.*

Para o gateway, vamos criar um projeto com o *template* *web* do .NET:

```
dotnet new web -n App.Gateway
```

E adicionaremos os seguintes pacotes NuGet:

```
dotnet add package Microsoft.Identity.Web
dotnet add package Ocelot
```

Eu já escrevi uma [série de posts](https://tallesvaliatti.com/como-proteger-suas-aplica%C3%A7%C3%B5es-com-o-microsoft-identity-platform-parte-1-6375adb44995?ref=azurebrasil.cloud) sobre como podemos proteger nossas aplicações com o [Microsoft identity platform](https://learn.microsoft.com/en-us/azure/active-directory/develop/?ref=azurebrasil.cloud). Vamos usá-la como base para essa solução!

Como mostrado na série de posts de referência, vamos criar dois registros de aplicativos no Azure AD:

- *App.Gateway* **;**
- *App.Client* **;**

O *App.Gateway* será o aplicativo web protegido, e o *App.Client* será o aplicativo que irá fazer requisições para o *gateway.* Na nossa solução, a *web api* não será exposta na internet, por tanto não iremos protege-lá com o Azure AD.

Para o *App.Gateway,* vamos criar um App Role chamado *Admin:*

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.23.18.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.24.38.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.24.40.png)

Vamos permitir que essa role seja atribuída a grupos ou usuários. A ideia é atribuirmos essa role ao nosso usuário para podermos ser autorizados a chamar os endpoints do *gateway.*

Para o *client*, vamos seguir como no post de referência: criar um *app registration* para ser usado pelo postman.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.23.22.png)

O código da nossa *web api* vai ser mantido como está, já para a aplicação do *gateway,* modificar o *Program.cs*:

```
using App.Gateway;
using Microsoft.Identity.Web;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);

builder.Services.AddOcelot(builder.Configuration);

builder.Services.DecorateClaimAuthoriser();

var app = builder.Build();

app.UseAuthentication();

app.UseOcelot().Wait();

app.Run();

```

- Adicionamos o arquivo *ocelot.json* ao provedor de configurações;
- Protegemos a aplicação com o *Microsoft identity platform;*
- Configuramos o Ocelot;

Além disso, observe que adicionamos o ***builder.Services.DecorateClaimAuthoriser().*** Esse método de extensão fará com que o arquivo *ocelot.json* seja lido de forma correta para as chaves do tipo "*http://schemas.microsoft.com/ws/2008/06/identity/claims/role"* (mais detalhes [aqui](https://github.com/ThreeMammals/Ocelot/issues/679?ref=azurebrasil.cloud#issuecomment-662905004)!).

```
using Ocelot.Authorization;

namespace App.Gateway;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection DecorateClaimAuthoriser(this IServiceCollection services)
    {
        var serviceDescriptor = services.First(x => x.ServiceType == typeof(IClaimsAuthorizer));
        services.Remove(serviceDescriptor);

        var newServiceDescriptor = new ServiceDescriptor(serviceDescriptor.ImplementationType, serviceDescriptor.ImplementationType, serviceDescriptor.Lifetime);
        services.Add(newServiceDescriptor);

        services.AddTransient<IClaimsAuthorizer, ClaimAuthorizerDecorator>();

        return services;
    }
}
```

```
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using System.Security.Claims;
using Ocelot.Authorization;
using Ocelot.Responses;

namespace App.Gateway;

public class ClaimAuthorizerDecorator : IClaimsAuthorizer
{
    private readonly ClaimsAuthorizer _authoriser;

    public ClaimAuthorizerDecorator(ClaimsAuthorizer authoriser)
    {
        _authoriser = authoriser;
    }

    public Response<bool> Authorize(ClaimsPrincipal claimsPrincipal,
                                    Dictionary<string, string> routeClaimsRequirement,
                                    List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
    {
        var newRouteClaimsRequirement = new Dictionary<string, string>();
        foreach (var kvp in routeClaimsRequirement)
        {
            if (kvp.Key.StartsWith("http///"))
            {
                var key = kvp.Key.Replace("http///", "http://");
                newRouteClaimsRequirement.Add(key, kvp.Value);
            }
            else
            {
                newRouteClaimsRequirement.Add(kvp.Key, kvp.Value);
            }
        }

        return _authoriser.Authorize(claimsPrincipal, newRouteClaimsRequirement, urlPathPlaceholderNameAndValues);
    }
}
```

Finalmente temos o arquivo *ocelot.json:*

```
{
    "Routes": [
      {
        "DownstreamPathTemplate": "/WeatherForecast",
        "DownstreamScheme": "http",
        "DownstreamHostAndPorts": [
          {
            "Host": "localhost",
            "Port": 5014
          }
        ],
        "UpstreamPathTemplate": "/api/weather-forecast",
        "UpstreamHttpMethod": [ "Get" ],
        "AuthenticationOptions": {
          "AuthenticationProviderKey": "Bearer"
        },
        "RouteClaimsRequirement": {
          "http///schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin"
        }
      }      
    ],
    "GlobalConfiguration": {
      "BaseUrl": "https://localhost:7096"
    }
  }
```

- Ajuste o valor da chave *Port* com a porta correta que você vai rodar a web api;
- O *gateway* retornará 403 se a requisição para o caminho */api/weather-forecast* não possuir a *role* *Admin;*

Para finalizar, precisamos adicionar ao *appsettings.json* os dados do registro de aplicativo *App.Gateway*:

```
{ 
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<your-domain>",
    "ClientId": "<your-client-id>",
    "TenantId": "<your-tenant-id>"
},
```

Dito isso, vamos executar o *gateway* e a *web api:*

```
dotnet run

```

Como mostrado no post de referência, vamos adquirir um novo *access token* via postman e fazer a requisição ao gateway.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.25.59.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.22.05.png)

Observe que estamos autenticados, porém, não possuímos a role *Admin* no *access token.* Vamos ao portal do Azure, na aba "*Enterprise Application"*, e adicionar a *app role* *Admin* do *App.Gateway* ao usuário que estamos usando.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.26.43.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.27.46.png)

De volta ao postman, podemos fazer uma aquisição de um novo *access token*. Utilizando o site [jwt.io](https://jwt.io/?ref=azurebrasil.cloud), podemos ver que o *access token* recebido possui a *role Admin.*

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.32.05.png)

Vamos refazer a requisição ao gateway utilizando esse novo *access token*:

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2023/09/Screen-Shot-2023-09-05-at-16.32.32.png)

Tudo ocorreu bem! Dessa forma podemos acessar web apis através de um gateway feito em Ocelot e protegido com o Azure AD.

Você já pode baixar o projeto por esse [link](https://github.com/TallesValiatti/ProtectOcelotWithAzureAd?ref=azurebrasil.cloud), e não esquece de me seguir no [LinkedIn](https://www.linkedin.com/in/tallesvaliatti/?ref=azurebrasil.cloud)!

Até a próxima, abraços!