Pub sub pattern with centralized subscription storage using Azure Service Bus - rebus

I am new to Rebus, and try to run the app below with Azure Service Bus, but I got the error below:
https://github.com/rebus-org/RebusSamples/tree/master/PubSubCentralized
System.InvalidOperationException
HResult=0x80131509
Message=Attempted to register primary -> Rebus.Subscriptions.ISubscriptionStorage, but a primary registration already exists: primary -> Rebus.Subscriptions.ISubscriptionStorage (The Azure Service Bus transport was inserted as the subscriptions storage because it has native support for pub/sub messaging)
Source=Rebus
StackTrace:
at Rebus.Injection.Injectionist.Register[TService](Func`2 resolverMethod, Boolean isDecorator, String description)
at Rebus.Injection.Injectionist.Register[TService](Func`2 resolverMethod, String description)
at Publisher.Program.<>c.<Main>b__2_2(StandardConfigurer`1 s) in C:\ReBus\PubSub\Publisher\Program.cs:line 26
at Rebus.Config.RebusConfigurer.Subscriptions(Action`1 configurer)
at Publisher.Program.Main() in C:\ReBus\PubSub\Publisher\Program.cs:line 22
My question is that How can I make it work because Subscriptions is not supported?
class Subscriber
{
static void Main()
{
using (var activator = new BuiltinHandlerActivator())
{
activator.Register(() => new Handler());
Configure.With(activator)
.Logging(l => l.ColoredConsole(minLevel: LogLevel.Warn))
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Subscriber))
// .Subscriptions(s => s.StoreInSqlServer("server=.; database=RebusPubSubCentralized; trusted_connection=true", "Subscriptions", isCentralized: true))
.Start();
activator.Bus.Subscribe<StringMessage>().Wait();
activator.Bus.Subscribe<DateTimeMessage>().Wait();
activator.Bus.Subscribe<TimeSpanMessage>().Wait();
Console.WriteLine("This is Subscriber 1");
Console.WriteLine("Press ENTER to quit");
Console.ReadLine();
Console.WriteLine("Quitting...");
}
}
}
class Publisher
{
static void Main()
{
using (var activator = new BuiltinHandlerActivator())
{
Configure.With(activator)
.Logging(l => l.ColoredConsole(minLevel: LogLevel.Warn))
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Publisher))
//.Subscriptions(s => s.StoreInSqlServer("server=.; database=RebusPubSubCentralized; trusted_connection=true", "Subscriptions", isCentralized: true))
.Start();
var startupTime = DateTime.Now;
var keyChar = char.ToLower(Console.ReadKey(true).KeyChar);
var bus = activator.Bus.Advanced.SyncBus;
bus.Publish(new TimeSpanMessage(DateTime.Now - startupTime));
Console.WriteLine("Quitting!");
}
}
}

With Azure Service Bus there's no need for registering a subscription storage, because it natively supports publish/subscribe.
This means that you can safely comment out the
.Subscriptions(s => ...)
thing, and then everything will work.

Related

Keep getting 401 on authorization with SignalR

I have been trying to create a sample Test app with SignalR but I have been extremely unsuccessful with authentication.
I am on .NET 6, and my Program.cs code looks like this.
Program.cs
using HubTestApp.Hubs;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSignalR();
builder.Services
.AddAuthentication(options =>
{
// Identity made Cookie authentication the default.
// However, we want JWT Bearer Auth to be the default.
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = "https://login.microsoftonline.com/{TenantId}/";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = false,
};
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required due to
// a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://docs.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/Test")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<SimpleHub>("/Test");
app.Run();
My hub code is pretty simple:
namespace HubTestApp.Hubs
{
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
internal class SimpleHub : Hub<ISimpleClient>
{
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task EchoMessage(string message) => await Clients.All.ReceiveMessage(message);
}
}
And this is my client code:
namespace HubTestClient
{
using Microsoft.AspNetCore.SignalR.Client;
public class MockClient
{
private const string Token = "Bearer <JWT from AAD>";
private readonly HubConnection hubConnection;
public MockClient()
{
// Notice here I have tried to pass the token in various ways, all to no avail.
this.hubConnection = new HubConnectionBuilder()
.WithUrl($"http://localhost:5110/Test?access_token={MockClient.Token}", options =>
{
options.AccessTokenProvider = () => Task.FromResult(MockClient.Token);
options.Headers.Add("Authorization", MockClient.Token);
})
.Build();
this.hubConnection.On<string>("ReceiveMessage", (message) =>
{
Console.WriteLine($"Received message: '{message}'");
});
}
public async Task StartClient()
{
await hubConnection.StartAsync();
Random rng = new Random();
while (true)
{
string messageToEcho = $"Sending random number '{rng.Next()}'";
Console.WriteLine(messageToEcho);
try
{
await hubConnection.InvokeAsync("EchoMessage", messageToEcho);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Delay 5 seconds between hitting the hub.
await Task.Delay(5000);
}
}
}
}
I continuously get the message "Failed to invoke 'X' because user is unauthorized." I have made sure the token I got is valid. So, I'm bashing my head over this, completely confused where I am going wrong. Thank you in advance!

Authentication method not supported (Received: 10) after initializing AppDbContext in EF Core

I have received the following exception while trying to Save a migration at startup...
Host.Startup.InitializeDatabase(Microsoft.AspNetCore.Builder.IApplicationBuilder) en Startup.cs
Host.Startup.Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder, Microsoft.AspNetCore.Hosting.IHostingEnvironment, Microsoft.Extensions.Logging.ILoggerFactory, System.IServiceProvider) en Startup.cs
Here is my db configuration
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString, sql =>
sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString, sql =>
sql.MigrationsAssembly(migrationsAssembly));
})
.AddAspNetIdentity<ApplicationUser>();
initialization
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();//crashes here
}
called before the line where crashes
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.Configure<FormOptions>(options => options.ValueCountLimit = 100000000);
services.AddDbContext<ApplicationDbContext>(options =>
{ options.UseNpgsql(connectionString);});
then ctor and then OnModelCreating
Here is my string connection which is passed to the AppDbContext:
User ID=postgres;Password=xxxxxxxxx;Host=localhost;Port=5432;Database=Dbname;Pooling=true;SSL Mode=Disable;TrustServerCertificate=true;

MassTransit SignalR Bindings

I'm having some issues setting up my MassTransit SignalR solution. I have posted a question before regarding bindings which Chris answered and helped me out but after updating the packages (i was still using 6.x.x which had other issues like restarting RabbitMQ service would origin in an error when sending messages) my bindings don't seem to work properly anymore.
This was the first question i asked: Exchange binding not working in MassTransit with RabbitMQ and SignalR
Now i have updated to the 7.1.7 and also updated the methods that were deprecated. This is how my Startup looks like:
public void ConfigureServices(IServiceCollection services)
{
Utilities utilities = new Utilities(Configuration);
RabbitMQIdentity rabbitMQIdentity = utilities.GetRabbitMQIdentity();
var username = rabbitMQIdentity.UserName;
var password = rabbitMQIdentity.Password;
var hostName = rabbitMQIdentity.HostName;
var portNumber = rabbitMQIdentity.Port;
services.AddHttpClient();
services.AddControllers();
services.AddSignalR(e => {
e.EnableDetailedErrors = true;
e.MaximumReceiveMessageSize = 102400000;
});
services.AddMassTransit(x =>
{
x.AddSignalRHub<NotificationHub>();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host($"amqp://{username}:{password}#{hostName}:{portNumber}");
cfg.AutoDelete = true;
cfg.Durable = false;
cfg.QueueExpiration = TimeSpan.FromMinutes(10);
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService();
services.AddSingleton<IHostEnvironment>(hostEnvironment);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<LogConfigurationUtility, WebLogConfigurationUtility>();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.SetIsOriginAllowed((x) => Configuration["CorsWhiteList"].Split(';').Any(x.Contains))
.WithMethods("GET", "POST")
.AllowAnyHeader()
.AllowCredentials();
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseMiddleware<RequestMiddleware>();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/notificationhub");
});
}
And here is my publish:
logger.LogInformation($"MassTransit publishing group message. GroupName:{paymentCallback.OrderId} ; Message:{paymentCallback.Event}");
IReadOnlyList <IHubProtocol> protocols = new IHubProtocol[] { new JsonHubProtocol() };
publishEndpoint.Publish<Group<NotificationHub>>(
new GroupHub<NotificationHub>()
{
GroupName = paymentCallback.OrderId,
Messages = protocols.ToProtocolDictionary("Notify", new object[] { paymentCallback.Event })
},
context => context.TimeToLive = TimeSpan.FromMinutes(10)
);
And the client:
useEffect(() => {
$(function() {
const connection = new HubConnectionBuilder()
.withUrl(hubUrl)
.configureLogging(LogLevel.Trace)
.build();
// Create a function that the hub can call to broadcast messages.
connection.on("Notify", (status) => {
console.log("entrouuuuuu");
setNotification(status);
});
// Start the connection.
async function start() {
try {
await connection.start();
connection.invoke("InitializeClient", orderId);
console.log("SignalR Connected.");
} catch (err) {
setTimeout(start, 5000);
}
}
start();
});
So now, in RabbitMQ management tools i can see the exchange being created but again it has no bindings and it has also no queues. Does the same issue still persist Chris? Or am i doing something wrong?
Thanks for the help!

How to get current logged in user with open id connect identity jwt bearer net core

Hello I am trying to get current user but nothing seems to work every resource I find brings null.
this is the code in my startup
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
};
});
services.ConfigureApplicationCookie(o => {
o.ExpireTimeSpan = TimeSpan.FromDays(5);
o.SlidingExpiration = true;
});
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(3));

Using Signalr on Angular 5 and Asp.net WebApi

I am actually creating a chat with angular 5 and signalR on an ASP.NET Framework API. I followed the documentation but it's still not work. Here is my hub:
public class ChatHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
}
Here is my startup class:
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
and here is my angular part which create the hubconnection:
ngOnInit() {
this._hubConnection = new HubConnection('http://localhost:58525/signalr/hubs');
this._hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log('Error while establishing connection :( : ' + err));
this._hubConnection.on('send', data => {
console.log(data);
});
}
I get this error:
If your ASP.NET page runs on another server, then your URL looks not correct.
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#crossdomain
You have to connect to:
this._hubConnection = new HubConnection('http://localhost:58525/signalr');

Resources