I'm working on a chat project by asp.net core 2.2 signalR project. I implemented asp.net core identity a project I can authenticate persons without any problem this is a scenario of my project. A user enters username and password, and my login controller checks it if everything is ok after that I generate a JwtToken and return Ok result to Login Razor page the login page redirects me to chat section and so far everything works correctly but when I want to get a current user in my chat hub it failed and I got NullRefrenceException.
public class ChatHub : Hub
{
public HubConnectionContext hubConnection;
public async Task SendMessage(string name, string text)
{
var personPricture = "https://devilsworkshop.org/files/2013/01/enlarged-facebook-profile-picture.jpg";
var message = new ChatMessage
{
SenderName = name,
Text = text,
SendAt = DateTimeOffset.Now
};
await Clients.All.SendAsync("ReciveMessage",
message.SenderName,
message.SendAt, message.Text, personPricture);
}
public Task SendMessageToUser(string connectionId, string message)
{
return Clients.Client(connectionId).SendAsync("ReceiveMethod", message);
}
private readonly HashSet<string> onlineUsers = new HashSet<string>();
public override async Task OnConnectedAsync()
{
**if (Context.User.Identity.IsAuthenticated)**
{
var name = Context.User.Identity.Name;
}
onlineUsers.Add(Context.ConnectionId);
await Clients.Caller.SendAsync("UserConnected", Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception ex)
{
onlineUsers.Remove(Context.ConnectionId);
await Clients.All.SendAsync("UserDisconnected", Context.ConnectionId);
await base.OnDisconnectedAsync(ex);
}
}
[HttpPost, Route("Login")]
public async Task<IActionResult> Login([FromBody]LoginViewModel user)
{
object tokenString;
if (user == null)
{
return BadRequest("Invalid client request");
}
var findUser = await _userManager.FindByNameAsync(user.UserName);
if (findUser == null)
{
return NotFound();
}
else if (findUser != null)
{
var result = await _signManager.
PasswordSignInAsync(user.UserName,
user.Password,
false, lockoutOnFailure: false);
if (!result.Succeeded)
return BadRequest();
else
{
tokenString = await GenerateJwtToken(findUser.Email, findUser);
return Ok(new {token = tokenString});
//return Redirect("/");
}
}
else
{
return Unauthorized();
}
}
public async Task<string> GenerateJwtToken(string email, AppUser user)
{
var claims = new List<Claim>{
new Claim(JwtRegisteredClaimNames.Sub,email),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier,user.Id)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ThisIsMyPasswrod3"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble(30));
var token = new JwtSecurityToken(
issuer: "http://localhost:8916",
audience: "http://localhost:8916",
claims: claims,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
$(document).ready(function () {
$('#login').click(function () {
let UserName = $('#username').val();
let Password = $('#password').val();
let user = { UserName: UserName, Password: Password };
console.log($('#password').val());
$.ajax({
url: '/api/auth/Login',
type: 'post',
contentType: 'application/json',
data: JSON.stringify(user),
success: function (data) {
let token = data.token;
console.log(token);
url = "/?jwt=" + token;
window.location.href = url;
},
error: function (data) {
console.log(data);
}
})
});
});
```
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSignalR(config => { config.EnableDetailedErrors = true; });
services.AddDbContext<ApplicationDbContext>
(options => options.UseSqlServer("Data Source=(local);Integrated Security=True;Initial Catalog=ChatDB;"));
services.AddIdentity<AppUser, AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateLifetime = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "http://localhost:8916",
ValidAudience = "http://localhost:8916",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["SecurityKey"]))
});
services.Configure<IdentityOptions>(op =>
{
op.Password.RequireDigit = false;
op.Password.RequiredLength = 3;
op.Password.RequireLowercase = false;
op.Password.RequireNonAlphanumeric = false;
op.Password.RequireUppercase = false;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSignalR(op => { op.MapHub<ChatHub>("/chatter"); });
app.UseAuthentication();
app.UseMvc();
app.UseStaticFiles();
}
```
What does your SignalR client code look like? Are you passing the jwt token through via the accessTokenFactory? See https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-2.2#bearer-token-authentication for some info on using JWT with SignalR.
Also, you should consider putting the [Authorize] attribute on your Hub so every invocation runs the auth check.
Also, you should consider putting the [Authorize] attribute on your Hub so every invocation runs the auth check.
https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-2.2#authorize-users-to-access-hubs-and-hub-methods
Thanks a lot. But, I want to say my problem was two things. One of them was [Authorize] and another was about order of middlewares: I changed order of middlewares
From
app.UseSignalR(op => { op.MapHub<ChatHub>("/chatter"); });
app.UseAuthentication();
to
app.UseAuthentication();
app.UseSignalR(op => { op.MapHub<ChatHub>("/chatter"); });
Most helpful comment
https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-2.2#authorize-users-to-access-hubs-and-hub-methods