CRamsey
CRamsey

Reputation: 96

Ocelot Integration with Azure Active Directory Authentication .Net Core 3.1

Context: My company is moving to an API Gateway to manage all our or services in our network. Each service is currently authenticated using azure AD. Everything is single tenant and just allows company users. Question: Am I understanding this correctly? Problem: When I call http:localhost:5000/authResource/get I get a 401 response back from Ocelot. After tracing through the error, I see that I get null back when the Ocelot Authentication middle ware attempts to authenticate using the AzureADJwtBearer scheme.

I followed the advice from questions related to Ocelot and azure ad but even following that I am unable to get anything to work. (Mostly from this answer: How set up Ocelot Api Gateway with Azure Active Directory) I think I may be misunderstanding how the authentication is supposed to work. My current understanding is that I tell Ocelot to authenticate against one of my Apis using the AzureADJwtBearer scheme. In my configuration I have the information for that specific api set up (ClientId, TenantId, etc).

Once I call the route, I am expecting Ocelot to make a call to Microsoft to start authentication. At this point, I expect the same flow as the Apis provide, i.e. I make the call and get the Microsoft login page which then redirects me back to the application once I enter my username and password.

After redirecting me back to Ocelot I guess (this is the part I am fuzzy on), I expect ocelot to store the access token Microsoft sends back FOR THE SPECIFIC RESOURCE I JUST REQUESTED. Then I expect ocelot to attach the token to an Auth header and then send the request for the resource I initially asked for.

To clarify this, I will include the code I have for my startup files, and my ocelot.json file. From Startup.cs

public void ConfigureServices(IServiceCollection services)
        {

            services.AddProtectedWebApi(Configuration)
                .AddProtectedApiCallsWebApis(Configuration)
                .AddInMemoryTokenCaches();
            services.AddOcelot(Configuration);
            services.AddControllers();
        }

Also from Startup.cs

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseOcelot().Wait();


            app.UseAuthentication();
        }

My ocelot.json file for the authenticated resource is as follows (names changed for security reasons):

{
      "DownstreamPathTemplate": "/api/Controller/Get",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5003
        }
      ],
      "UpstreamPathTemplate": "/authResource/get",
      "UpstreamHttpMethod": [ "GET" ],

      "AuthenticationOptions": {
        "AuthenticationProviderKey": "AzureADJwtBearer",
        "AllowedScopes": []
      }
    }

To solidify how I understand this, I will go through an example using the sample api shown in the ocelot configuration.

I wish to access resource http://localhost:5003/api/Controller/Get which is a protected API meaning I can only get a response from this if I provide an authorization header with my get request. I make a request through my ocelot gateway for the url http://localhost:5000/authResource/get (I am hosting Ocelot on localhost:5000). Ocelot sees it needs to authenticate to access this resource, so it makes a request using the AzureADJwtBearer scheme. I am redirected to microsoft to login. Once done, I am sent back to the Ocelot application with an access token in tow. Ocelot takes this taken, creates the Auth header, and finally calls http://localhost:5003/api/Controller/Get and returns the result.

Upvotes: 3

Views: 4224

Answers (1)

CRamsey
CRamsey

Reputation: 96

Working Example for .Net Core 3.1

I ended up getting this working using the Microsoft.Identity.Web library (current version located at https://github.com/AzureAD/microsoft-identity-web/tree/master/src/Microsoft.Identity.Web)

First up, my ocelot configuration file (ocelot.json):

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/myapp/api/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "apphost.com",
          "Port": 443
        }
      ],
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "GET", "POST" ],

      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": []
      }
    }
  ]
}

Notice the AuthenticationProviderKey has the value Bearer.

My appsettings.json file contains my azure configurations:

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "mydomain.com",
    "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }

Then finally, my Startup.cs file contains something like this (reduced for brevity)

public void ConfigureServices(IServiceCollection services)
        {
            services.AddProtectedWebApi(Configuration)
                .AddProtectedApiCallsWebApis(Configuration)
                .AddInMemoryTokenCaches();

            services.AddOcelot(Configuration);
            services.AddControllers();
        }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                IdentityModelEventSource.ShowPII = true;
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseOcelot().Wait();

            app.UseAuthentication();
        }

With this all set up, I can pass in a bearer token generated using my azure credentials, and the ocelot gateway will correctly validate it.

Upvotes: 5

Related Questions