I want to use anti-forgery-token for my login method in my C# and Vue 3 demo app but I always get error 400.I tried making a request to get the token,store it and put it in the headers but I am getting error 400.
Here is my Program.cs file here,I am not sure if I am correctly adding the token
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers();
builder.Services.AddAntiforgery(option => option.HeaderName = "X-CSRF-TOKEN");
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<AutoValidateAntiforgeryTokenAttribute>();
});
var app = builder.Build();
app.UseCors(o => o.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
IApplicationBuilder applicationBuilder = app.UseRouting().UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Urls.Add("http://localhost:5000");
app.Run();
This is my User Controller,here I made a new method to get the anti forgery token so I can get it with a request and use it in the headers
using Microsoft.AspNetCore.Mvc;
using System.Data.SqlClient;
using Dapper;
using WebApplication1.Models;
using Microsoft.AspNetCore.Antiforgery;
using NuGet.Common;
namespace userController.Controllers
{
[ApiController]
[Route("api")]
public class UsersController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IAntiforgery _antiforgery;
public UsersController(IConfiguration configuration, IAntiforgery antiforgery)
{
_configuration = configuration;
_antiforgery = antiforgery;
}
[HttpGet("get-anti-forgery-token")]
public IActionResult GetAntiForgeryToken()
{
try
{
// Generate and store the anti-forgery token
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
// Return the anti-forgery token in the response
return Ok(new { token = tokens.RequestToken });
}
catch (Exception ex)
{
return StatusCode(500, $"Internal Server Error: {ex.Message}");
}
}
[HttpGet("users")]
public IEnumerable<FormModel> GetUsers()
{
string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string selectQuery = "SELECT * FROM Users";
return connection.Query<FormModel>(selectQuery);
}
}
[HttpPost("register")]
public async Task<IActionResult> PostUser([FromBody] FormModel model)
{
try
{
string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
// Check if the user with the same username already exists
string checkUserQuery = "SELECT COUNT(*) FROM Users WHERE Username = @Username";
int existingUser = await connection.QueryFirstOrDefaultAsync<int>(checkUserQuery, new { Username = model.Username });
if (existingUser > 0)
{
// User with the same username already exists, handle accordingly
return Conflict("Username already exists");
}
string insertQuery = "INSERT INTO Users (Username, Password) OUTPUT INSERTED.Id VALUES (@Username, @Password)";
int userId = await connection.QueryFirstOrDefaultAsync<int>(insertQuery, new { Username = model.Username, Password = model.Password });
model.Id = userId;
return Ok(model);
}
}
catch (Exception ex)
{
return StatusCode(500, $"Internal Server Error: {ex.Message}");
}
}
[HttpGet("testredirect")]
public IActionResult GetLoginInfo([FromQuery] FormModel model)
{
return Redirect("/api/login?username=" + model.Username + "&password=" + model.Password);
}
[ValidateAntiForgeryToken]
[HttpPost("login")]
public async Task<ActionResult<FormModel>> LoginUser([FromBody] FormModel model)
{
try
{
string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
string selectQuery = "SELECT * FROM Users WHERE Username = @Username AND Password = @Password";
var user = await connection.QueryFirstOrDefaultAsync<FormModel>(selectQuery, new { Username = model.Username, Password = model.Password});
if (user != null)
{
return Ok(user);
}
else
{
// Return 401 Unauthorized
return Unauthorized();
}
}
}
catch (Exception ex)
{
return StatusCode(500, $"Internal Server Error: {ex.Message}");
}
}
[HttpPut("update/{id}")]
public async Task<IActionResult> UpdateUser([FromBody] FormModel model, int id)
{
try
{
string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
string checkUserQuery = "SELECT COUNT(*) FROM Users WHERE Id = @Id";
var userCount = await connection.QueryFirstOrDefaultAsync<int>(checkUserQuery, new { Id = id });
if (userCount == 0)
{
// User with the given ID does not exist
return NotFound();
}
string updateQuery = "UPDATE Users SET Username = @Username WHERE Id = @Id";
await connection.ExecuteAsync(updateQuery, new { Id = id, Username = model.Username });
return Ok();
}
}
catch (Exception ex)
{
return StatusCode(500, $"Internal Server Error: {ex.Message}");
}
}
[HttpDelete("delete/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
try
{
string connectionString = _configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
string deleteQuery = "DELETE FROM Users WHERE Id = @Id;";
await connection.ExecuteAsync(deleteQuery, new { Id = id });
return Ok($"User with ID {id} has been deleted");
}
}
catch (Exception ex)
{
return StatusCode(500, $"Internal Server Error: {ex.Message}");
}
}
}
}
Vue Template
<template>
<div>
<form v-if="!loggedIn" @submit.prevent="submitForm">
<h1>Register</h1>
<label for="username">Username:</label>
<input v-model="username" type="text" id="username" required />
<label for="password">Password:</label>
<input v-model="password" type="password" id="password" required />
<div v-if="usernameError !== ''">
<p>{{ usernameError }}</p>
</div>
<button type="submit">Submit</button>
</form>
<form v-if="!loggedIn" @submit.prevent="login">
<h1>login</h1>
<label for="username">Username:</label>
<input v-model="loginUsername" __RequestVerificationToken type="text" id="username" required />
<label for="password">Password:</label>
<input v-model="loginPassword" __RequestVerificationToken type="password" id="password" required />
<button type="submit">Submit</button>
</form>
<div v-if="loggedIn">
<p>Logged In as {{ loggedInUser.username }}</p>
change username
<input type="text" v-model="newUsername" />
<button @click="editUser()" :disabled="newUsername.trim() === ''" >Edit</button>
<button @click="deleteUser()">Delete</button>
<div v-if="updatedusername">
<p>Successfully updated!</p>
</div>
<div v-if="sameUsername">
<p>You cannot change your username to the same username</p>
</div>
<p>Add a book</p>
<form @submit.prevent="addBook">
<label for="bookTitle">Title:</label>
<input type="text" id="bookTitle" v-model="bookTitle" />
<label for="bookAuthor">Author:</label>
<input type="text" id="bookAuthor" v-model="bookAuthor" />
<button type="submit">Submit</button>
</form>
<div v-for="book in mybookCollection" :key="book.id">
{{ book.title }} - {{ book.author }}
<button @click="deleteBook(book.bookId)">Delete</button>
</div>
</div>
<div v-if="loginError">
<p>{{ loginError }}</p>
</div>
<div v-for="user in users" :key="user.id">{{ user.username }}
<input type="checkbox" >
</div>
</div>
<div v-if="loggedIn">
<button @click="logout">Logout</button>
</div>
</template>
Vue Script
<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";
const username = ref("");
const password = ref("");
const loginUsername = ref("");
const loginPassword = ref("");
const usernameError = ref("");
const users = ref([]);
const loggedIn = ref(false);
const loggedInUser = ref(null);
const loginError = ref("");
const bookTitle = ref("");
const bookAuthor = ref("");
const mybookCollection = ref([]);
const fileInput = ref(null);
const sameUsername = ref(false);
const newUsername = ref("");
const updatedusername = ref(false);
const userIds = ref([]);
const url = "http://localhost:5000/";
const saveUserToLocalStorage = (user) => {
localStorage.setItem("loggedInUser", JSON.stringify(user));
};
const loadUserFromLocalStorage = () => {
const user = localStorage.getItem("loggedInUser");
return user ? JSON.parse(user) : null;
};
const clearUserFromLocalStorage = () => {
localStorage.removeItem("loggedInUser");
};
const submitForm = () => {
axios
.post(`${url}api/register`, {
username: username.value,
password: password.value,
})
.then((response) => {
console.log(`${response.data} has been created`);
saveUserToLocalStorage(response.data);
loggedInUser.value = response.data;
loggedIn.value = true;
username.value = "";
password.value = "";
})
.catch((error) => {
if (
error.response &&
error.response.status === 409 &&
error.response.data.includes("Username already exists")
) {
console.log("Username already exists");
usernameError.value = "Username already exists";
} else {
console.log(`Unexpected error: ${error}`);
}
});
};
const editUser = () => {
const id = loggedInUser.value.id;
axios
.put(`${url}api/update/${id}`, { username: newUsername.value, password: loggedInUser.value.password,id:id })
.then((response) => {
if(newUsername.value === loggedInUser.value.username){
sameUsername.value = true;
return;
}
if (!newUsername.value) {
return;
}
console.log(response.data);
updatedusername.value = true;
setTimeout(() => {
updatedusername.value = false;
}, 2000);
loggedInUser.value.username = newUsername.value;
saveUserToLocalStorage(loggedInUser.value);
newUsername.value = "";
})
.catch((error) => {
console.log(`Unexpected error: ${error}`);
});
};
const getAntiForgeryToken = async () => {
try {
const response = await axios.get(`${url}api/get-anti-forgery-token`);
return response.data.token;
} catch (error) {
console.log(`Unexpected error: ${error}`);
throw error; // Re-throw the error to be caught by the calling function
}
};
const login = async () => {
const token = await getAntiForgeryToken();
console.log(`Anti-forgery token: ${token}`);
axios.post(`${url}api/login`, {
username: loginUsername.value,
password: loginPassword.value,
}, {
headers: {
"X-CSRF-TOKEN": token,
}
})
.then((response) => {
loggedInUser.value = response.data;
loggedIn.value = true;
saveUserToLocalStorage(loggedInUser.value);
console.log(`${loggedInUser.value} has logged in`);
// Additional logic, if needed
})
.catch((error) => {
console.error('Login error:', error);
});
};
const logout = () => {
loggedIn.value = false;
loggedInUser.value = null;
clearUserFromLocalStorage();
};
const deleteUser = () => {
const id = loggedInUser.value.id;
axios
.delete(`${url}api/delete/${id}`)
.then((response) => {
console.log(response.data);
// Additional logic if needed after successful deletion
loggedIn.value = false;
loggedInUser.value = null;
clearUserFromLocalStorage();
})
.catch((error) => {
console.log(`Unexpected error: ${error}`);
});
};
const addBook = () => {
const id = loggedInUser.value.id;
axios
.post(`${url}api/insertbook/${id}`, {
title: bookTitle.value,
author: bookAuthor.value,
userId: id,
})
.then((response) => {
console.log(response.data);
myBooks();
bookAuthor.value = "";
bookTitle.value = "";
})
.catch((error) => {
console.log(`Unexpected error: ${error}`);
});
};
const myBooks = () => {
const id = loggedInUser.value.id;
axios
.get(`${url}api/getbooks/${id}`)
.then((response) => {
console.log(response.data);
mybookCollection.value = response.data;
})
.catch((error) => {
console.log(`Unexpected error: ${error}`);
});
};
const deleteBook = (id) => {
axios
.delete(`${url}api/deletebook/${id}`)
.then((response) => {
console.log(response.data);
mybookCollection.value = mybookCollection.value.filter(book => book.bookId !== id);
})
.catch((error) => {
console.log(`Unexpected error: ${error}`);
});
};
onMounted(() => {
const user = loadUserFromLocalStorage();
if (user) {
loggedInUser.value = user;
loggedIn.value = true;
myBooks();
}
});
</script>