-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
I've created with Visual Studio a BlazorWebAssembly App with Authentication, ASP.NET Core hosted, PWA, selections.
In the Startup I have cookie policy for SignalR and the client authenticates with SignalR Hub and Context information can be obtained. Messages can be sent via Clients.Client(connectionId) or Clients.All but not to Client.User. Since user Identiy is authenticated and identity known I could create database or dictionary mapping email to connectionIds and send that way. No errors are thrown and authorization to hub is successful. I'm wondering what I am missing or with Blazor do you send only by connectionId and not via Client.User?
Below are Startup.cs, ChatHubTest.cs, ChatTest.Razor, Program.cs.
Thanks
Patrick
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options =>
options.SignIn.RequireConfirmedAccount = false)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap
.Remove("role");
services.Configure<IdentityOptions>(options =>
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);
services.AddAuthorization(options =>
{
options.AddPolicy("TeamMember", policy => policy.RequireRole("TeamMember"));
options.AddPolicy("IsMember", policy => policy.RequireClaim("IsMember", "true"));
});
services.AddAuthentication().AddIdentityServerJwt();
.
services.AddAuthorization(options => options.DefaultPolicy =
new AuthorizationPolicyBuilder(IdentityConstants.ApplicationScheme,
IdentityServerJwtConstants.IdentityServerJwtBearerScheme)
.RequireAuthenticatedUser()
.Build());
services.AddSignalR();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapHub<ChatHubTest>("/chatHubTest");
endpoints.MapFallbackToFile("index.html");
});
}
}
ChatHubTest.cs
public class ChatHubTest : Hub
{
public async Task SendPrivateMessage(string receiver, string message)
{
string dateStamp = DateTime.Now.ToString();
string sender = Context.User.Identity.Name;
string senderConnectId = Context.ConnectionId;
await Clients.User(receiver).SendAsync("ReceiveMessage", sender, receiver, "Send User: " + message, dateStamp);
await Clients.User(sender).SendAsync("ReceiveEchoMessage", sender, receiver, "Send User: " + message, dateStamp);
await Clients.All.SendAsync("ReceiveMessage", sender, receiver, "Send All: "+ message, dateStamp);
await Clients.Client(senderConnectId).SendAsync("ReceiveMessage", sender, receiver, "Send ConnectId: " + message, dateStamp);
}
}
ChatTest.razor
@page "/chat/chatTest"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@inject IJSRuntime JSRuntime
@implements IDisposable
@using Microsoft.AspNetCore.Authorization
@inject HttpClient http
@attribute [Authorize]
<h3 class="text-warning">Chat Test</h3>
<hr />
@foreach ((string Sender, string Receiver, string Message, string DateStamp) message in messages)
{
<div> @message.Sender "to " @message.Receiver</div>
<div>@message.Message</div>
<div>@message.DateStamp</div>
<p></p>
}
<div class="footer">
<div class="row">
<div class="col-8 form-group ml-3">
<div>
<input @bind="receiverEmail" class="w-100" placeholder="Recipient Email" />
</div>
<div>
<textarea @bind="messageInput" placeholder="Message" rows="2" class="w-100 mt-1"></textarea>
</div>
</div>
<div class="col-1">
<button @onclick="Send" disabled="@(!IsConnected)" class="btn btn-success">
Send
</button>
</div>
</div>
</div>
@code{
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
[Parameter] public string ChatKey { get; set; }
private HubConnection hubConnection;
private List<(string, string, string, string)> messages = new List<(string Sender, string Receiver, string Message, string DateStamp)>();
private string receiverEmail;
private string messageInput;
public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
private string errorMessage;
private string authMessage;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chatHubTest"))
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string, string, string>("ReceiveMessage", async (sender, receiver, message, datestamp) =>
{
messages.Add((sender, receiver, message, datestamp));
StateHasChanged();
await ScrollToBottom();
});
hubConnection.On<string, string, string, string>("ReceiveEchoMessage", async (sender, receiver, message, datestamp) =>
{
messages.Add((sender, receiver, message, datestamp));
StateHasChanged();
await ScrollToBottom();
});
hubConnection.On<string, string, string, string>("RecieveAllMessage", async (sender, receiver, message, datestamp) =>
{
messages.Add((sender, receiver, message, datestamp));
StateHasChanged();
await ScrollToBottom();
});
await hubConnection.StartAsync();
}
public async Task Send()
{
if (String.IsNullOrEmpty(receiverEmail) || String.IsNullOrEmpty(messageInput))
{
errorMessage = "Please use a valid recipient email and message";
}
else
{
errorMessage = "";
await hubConnection.SendAsync("SendPrivateMessage", receiverEmail, messageInput);
}
}
public void Dispose()
{
_ = hubConnection.DisposeAsync();
}
public async Task ScrollToBottom()
{
await JSRuntime.InvokeVoidAsync("scrollToStandardBottom");
}
}
Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddHttpClient("BlazorHospital.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorHospital.ServerAPI"));
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<CustomUserFactory>();
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
await builder.Build().RunAsync();
}
}