I'm trying to make an simple chat website for a school project. The frontend is vanilla JS and backend is ASP DOTNET 8. It is now that I have a lot of trouble with authorization. Login and register work fine but when I try to make an API call to search for a user to an controller with [authorize] I keep getting a 401. I know why I get a 401 because with the code below I check for a value in the token whoever there is nothing there.
My question is that I don't know how to fix it. I tried different things but nothing seems to work on why the JWT token isn't being send back so it can be authorize by the back-end.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Log all cookies
Console.WriteLine("=== OnMessageReceived ===");
Console.WriteLine($"Cookies received: {string.Join(", ", context.Request.Cookies.Keys)}");
// 1) SignalR websocket transports send access_token in query string
var accessToken = context.Request.Query["access_token"].FirstOrDefault();
if (!string.IsNullOrEmpty(accessToken))
{
Console.WriteLine(" Token found in query string");
context.Token = accessToken;
return Task.CompletedTask;
}
// 2) fallback to the AuthToken cookie
if (context.Request.Cookies.TryGetValue("AuthToken", out var cookieToken) && !string.IsNullOrEmpty(cookieToken))
{
Console.WriteLine($" Token found in AuthToken cookie: {cookieToken.Substring(0, 20)}...");
context.Token = cookieToken;
}
else
{
Console.WriteLine(" No AuthToken cookie found!");
}
return Task.CompletedTask;
}
};
So the code below shows what the code in the front-end looks like and there also some code for SignalR but that works just fine for now it is primarily the search function for a user here is the code.
render() {
const root = document.createElement('div');
root.innerHTML = `
<div style="display:flex; gap:12px;">
<div style="width:260px;">
<h3>Users</h3>
<div><input id="search" placeholder="search"></div>
<button id="searchBtn">Search</button>
<div id="users" class="user-list"></div>
</div>
<div style="flex:1;">
<h3>Chat <span id="chatWith">no one</span></h3>
<div id="chatWindow" class="chat-window"></div>
<textarea id="msg" rows="3" style="width:100%"></textarea>
<div><button id="sendBtn">Send</button> <button id="refreshBtn">Refresh</button></div>
</div>
</div>
`;
root.querySelector('#searchBtn').addEventListener('click', async ()=> {
const q = root.querySelector('#search').value;
const res = await fetch(API + '/api/Users/search?q=' + encodeURIComponent(q), { withCredentials: true });
const users = await res.json();
const usersEl = root.querySelector('#users');
usersEl.innerHTML = '';
users.forEach(u=> {
I know it is not the nicest code to look at but after getting stuck here for a week that is the least of my priority so Ii know. Also here is the back-end code to where I make the call.
using ChatBackendFinal.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace ChatBackendFinal.Controllers
{
[ApiController]
[Route("api/Users")]
[Authorize(Policy = "AuthenticatedUsersOnly")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet("search")]
public async Task<IActionResult> SearchUsers([FromQuery] string query)
{
var users = await _userService.SearchAsync(query);
var result = users.Select(u => new { u.Id, u.Username, u.Email });
return Ok(result);
}
}
}
withCredentials. But then the cookie must not be set withSameSite: lax, because that explicitly excludes it from being send withfetchrequests, among other things, developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/…strictwould mean that no cookie will be send with cross-origin requests at all, no matter what type of request.