Skip to main content

In this example, we will demonstrate how to implement CSRF protection in a .NET Core 8 API and a Vue.js 3 client using the composition API pattern.

---
title: Securing Your .NET Core API with CSRF Tokens and Vue.js 3
subtitle: In this example, we will demonstrate how to implement CSRF protection in a .NET Core 8 API and a Vue.js 3 client using the composition API pattern.
author: Jon LaBelle
date: September 29, 2024
source: https://jonlabelle.com/snippets/view/markdown/implementing-csrf-protection-in-a-net-core-8-api-with-a-vuejs-3-client
notoc: false
---

Cross-Site Request Forgery (CSRF) is a type of attack that tricks the victim
into submitting a malicious request. It can compromise the integrity of your
application by allowing unauthorized actions.

Using CSRF tokens is a robust way to mitigate this risk by ensuring that every
state-changing request is intentional and comes from a trusted source.

### .NET Core API Setup

#### 1. Create a new .NET Core API project

```bash
dotnet new webapi -n SecureApi
cd SecureApi
```

#### 2. Install necessary packages

```bash
dotnet add package Microsoft.AspNetCore.Antiforgery
dotnet add package Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
```

#### 3. Setup Data Protection for a Web Farm

Create a new class `DataProtectionContext.cs` in `Models` folder:

```csharp
// Models/DataProtectionContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;

public class DataProtectionContext : DbContext, IDataProtectionKeyContext
{
    public DataProtectionContext(DbContextOptions<DataProtectionContext> options) : base(options) { }

    public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
```

Update `Program.cs`:

```csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<DataProtectionContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddDataProtection()
    .PersistKeysToDbContext<DataProtectionContext>();
builder.Services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
```

#### 4. Create Middleware for CSRF Protection

Create a new class `CsrfMiddleware.cs` in `Middleware` folder:

```csharp
// Middleware/CsrfMiddleware.cs
using Microsoft.AspNetCore.Antiforgery;

public class CsrfMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IAntiforgery _antiforgery;

    public CsrfMiddleware(RequestDelegate next, IAntiforgery antiforgery)
    {
        _next = next;
        _antiforgery = antiforgery;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (HttpMethods.IsPost(context.Request.Method) ||
            HttpMethods.IsPut(context.Request.Method) ||
            HttpMethods.IsDelete(context.Request.Method))
        {
            await _antiforgery.ValidateRequestAsync(context);
        }

        await _next(context);
    }
}
```

Update `Program.cs` to use the middleware:

```csharp
// Program.cs
app.UseMiddleware<CsrfMiddleware>();
```

#### 5. Create a Controller

Create a new controller `SampleController.cs` in the `Controllers` folder:

```csharp
// Controllers/SampleController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Antiforgery;

[ApiController]
[Route("api/[controller]")]
public class SampleController : ControllerBase
{
    private readonly IAntiforgery _antiforgery;

    public SampleController(IAntiforgery antiforgery)
    {
        _antiforgery = antiforgery;
    }

    [HttpGet("token")]
    public IActionResult GetCsrfToken()
    {
        var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
        return Ok(new { token = tokens.RequestToken });
    }

    [HttpPost("data")]
    public async Task<IActionResult> PostData([FromBody] object data)
    {
        // Handle data
        return Ok();
    }
}
```

### Vue.js 3 Client Setup

#### 1. Create a new Vue.js 3 project using Vite

```bash
npm init vite@latest vue-client --template vue
cd vue-client
npm install
npm install axios
```

#### 2. Create a Composition API Hook for CSRF

You do not need to use a different CSRF token for every request. Typically, a
CSRF token is generated once per user session and reused for subsequent requests
within that session. This approach balances security and performance.

However, it's crucial to ensure that the token is securely stored and
transmitted with each _state-changing_ request (e.g., `POST`, `PUT`, `DELETE`).

Create a new file `useCsrf.js` in `src/composables` folder:

```javascript
// src/composables/useCsrf.js
import { ref } from 'vue';
import axios from 'axios';

const csrfToken = ref('');

export function useCsrf() {
  const fetchCsrfToken = async () => {
    if (!csrfToken.value) {
      const response = await axios.get('/api/sample/token');
      csrfToken.value = response.data.token;
    }
  };

  axios.interceptors.request.use((config) => {
    if (csrfToken.value) {
      config.headers['X-CSRF-TOKEN'] = csrfToken.value;
    }
    return config;
  });

  return {
    csrfToken,
    fetchCsrfToken
  };
}
```

#### 3. Use the Hook in a Component

Create a new component `App.vue` in `src` folder:

```vue
<!-- src/App.vue -->
<template>
  <div>
    <button @click="sendData">Send Data</button>
  </div>
</template>

<script setup>
import { useCsrf } from './composables/useCsrf';
import axios from 'axios';

const { fetchCsrfToken } = useCsrf();

const sendData = async () => {
  await fetchCsrfToken();
  await axios.post('/api/sample/data', { key: 'value' });
};
</script>
```

### Conclusion

By implementing CSRF tokens in your .NET Core API and Vue.js 3 client, you can
significantly enhance the security of your application. Protecting
state-changing HTTP methods like `POST`, `PUT`, and `DELETE` ensures that your
application is safeguarded against CSRF attacks.