# ASP.NET Core

> Add Centinel Analytica to your ASP.NET Core application.

Source: https://docs.centinelanalytica.com/platforms/application/aspnet-core

## Overview

The Centinel middleware runs before your endpoints, sending request metadata to the validator API. The validator decides if the request should be allowed, blocked, redirected, or didn't match any protected path. Blocked and challenged responses are rendered from HTML the validator returns, so that traffic never reaches your endpoints. Two cases do fall through to your app: a challenge that arrives without HTML, and a misconfigured `CentinelConfiguration` section.

## Prerequisites

* .NET 9.0 SDK or later (the package targets `net9.0`)
* NuGet package access
* Centinel secret key (from the [Dashboard](https://docs.centinelanalytica.com/install/dashboard.md))

## Install

In your ASP.NET Core project:

```bash
dotnet add package Centinel.AspNetCore
```

## Configure

1. **Configuration**

   Add your Centinel settings to `appsettings.json` (or any other configuration provider):

   ```json
   {
     "CentinelConfiguration": {
       "SecretKey": "YOUR_SECRET_KEY",
       "ValidatorApiUrl": "https://validator.centinelanalytica.com/validate",
       "ProtectedPaths": ["/api", "/dashboard"],
       "ExcludedPaths": ["/health", "/static"],
       "FailOpen": true
     }
   }
   ```

   | Property          | Type       | Required | Default                                            | Description                                                                                         |
   | ----------------- | ---------- | -------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
   | `SecretKey`       | `string`   | Yes      | –                                                  | Secret key for the validator API.                                                                   |
   | `ValidatorApiUrl` | `string`   | No       | `https://validator.centinelanalytica.com/validate` | Validator endpoint URL.                                                                             |
   | `ProtectedPaths`  | `string[]` | No       | –                                                  | Routes to validate. If empty, all routes are protected.                                             |
   | `ExcludedPaths`   | `string[]` | No       | –                                                  | Routes to skip validation on.                                                                       |
   | `FailOpen`        | `boolean`  | No       | `true`                                             | If `false`, return 503 with `Service temporarily unavailable` when the Centinel API is unreachable. |
   | `RequestTimeout`  | `TimeSpan` | No       | `00:00:05`                                         | Validator request timeout.                                                                          |
   | `EnableLogging`   | `boolean`  | No       | `true`                                             | Emit validator request and response logs.                                                           |
   | `CookieName`      | `string`   | No       | `_centinel`                                        | Session cookie the middleware reads.                                                                |

   `ProtectedPaths` and `ExcludedPaths` are case-insensitive prefix matches with no wildcards, and
   exclusions always win. A prefix of `/api` therefore also matches `/apiary`, so add a trailing
   slash (`/api/`) when you mean a path segment.

2. **Register services**

   Register Centinel in `Program.cs`:

   ```csharp
   using Centinel.AspNetCore.Extensions;

   var builder = WebApplication.CreateBuilder(args);

   builder.Services.AddCentinel(builder.Configuration);
   // Or configure manually:
   // builder.Services.AddCentinel(options =>
   // {
   //     options.SecretKey = Environment.GetEnvironmentVariable("CENTINEL_SECRET_KEY")!;
   //     options.ProtectedPaths = new() { "/api", "/admin" };
   // });

   var app = builder.Build();
   ```

3. **Add middleware**

   Put the Centinel middleware in your request pipeline before endpoint execution:

   ```csharp
   using Centinel.AspNetCore.Extensions;

   app.UseCentinel();
   app.UseRouting();
   app.UseAuthentication();
   app.UseAuthorization();

   app.MapControllers();
   ```

   Put `UseCentinel()` first. The middleware reads `context.Request.Path` directly and needs no routing
   data, so placing it ahead of routing and auth keeps unwanted traffic away from that work entirely.

   > **Confirm the config actually bound:** If the `CentinelConfiguration` section is missing or misspelled, the middleware allows every request and logs `Centinel configuration is invalid. Requests will be allowed through.` The check runs per request, not at startup, so send a request first and then read the logs. Nothing appears in your startup output either way, and under production traffic this line repeats on every request.

   The NuGet package does not inject the browser script. Add the [script tag](https://docs.centinelanalytica.com/install/scripts.md)
   to `_Layout.cshtml` yourself if you want browser-side signals.

### Middleware

With `app.UseCentinel()`, every matching request is validated automatically. Use `ProtectedPaths` and `ExcludedPaths` in your configuration to control which routes get checked.

Inject `ICentinelValidator` to apply Centinel only where you need it (e.g. sensitive APIs) without middleware-wide protection.

### Custom validator endpoint

Override the validator URL or request timeout if you're using a self-hosted validator:

```csharp
builder.Services.AddCentinel(options =>
{
    options.SecretKey = Environment.GetEnvironmentVariable("CENTINEL_SECRET_KEY")!;
    options.ValidatorApiUrl = "https://validator.yourdomain.com/validate";
    options.RequestTimeout = TimeSpan.FromSeconds(10);
});
```

### Deployment notes

Make sure your production configuration or environment variables include the `CentinelConfiguration` values (especially `SecretKey` and any custom validator URL). Restart your app after updating keys so the middleware picks up the new settings.

## Verify

With `EnableLogging` on and the log level at `Debug`, curl an excluded path and then a protected one.
A protected request logs `Sending validation request to Centinel API for URL: {Url}` followed by
`Received validation response: {Decision}`.

```bash
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:5000/health
curl -s -o /dev/null -w '%{http_code}\n' -A 'python-requests/2.31' http://localhost:5000/api/items
```

Four log lines tell you what went wrong when it doesn't work:

| Log line                                             | Meaning                                                 |
| ---------------------------------------------------- | ------------------------------------------------------- |
| `Centinel configuration is invalid`                  | The config section did not bind. Everything is allowed. |
| `Centinel API returned status {StatusCode}`          | Usually a wrong secret key.                             |
| `Centinel API request timed out for URL`             | Raise `RequestTimeout`, or check egress.                |
| `Centinel API unavailable, allowing request through` | Fail-open engaged.                                      |

The repo ships a runnable demo under `demo/` with public and protected routes if you want to see all
of this without touching your own app.

## Changelog

* **v1.0.1** — Response header passthrough
