Authorize signalR Hub not connected in ASP Core - c#

Authorize signalR Hub not getting connect to hub with front end angular, unable to invoke jwt access token to hub connection. How to connect Authurized signalr hub in asp core with angular project
i have the following code in my project,
Here my Code
public class Startup
{
readonly string CorsPolicy = "CorsPolicy";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCors(options =>
{
options.AddPolicy(name: CorsPolicy,
builder =>
{
builder
.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("http://localhost:3000", "http://localhost:4200", "http://localhost:1234")
.AllowCredentials();
});
});
services.AddControllers();
services.AddSignalR()
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
//jwt token
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])),
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = "Bearer " + context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/connecthub")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDatabaseContext context)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(CorsPolicy);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<signalR_Hub>("/connecthub");
});
}
}
}
This my Hub connection
[Authorize]
public class sample_Hub :Hub
{
}
This my controller
[Authorize]
[HttpPut("{id}")]
public async Task<IActionResult> Putfilecreations(string id, filecreations filecreations)
{
var entity = _context.file_creations.Where(x => x.file_creation_id == id).FirstOrDefault();
entity.client_type_id = filecreations.client_type_id;
_context.file_creations.Update(entity);
await _context.SaveChangesAsync();
await _hubContext.Clients.All.SendAsync("FileUpdated", entity);
return Ok(entity);
}
This my Angular code for connect Hub
this.hubConnection = new HubConnectionBuilder()
.withUrl(environment.baseUrls.server + 'connecthub')
.configureLogging(LogLevel.Information)
.build();

Try to add CORS this way, the order is important:
services.AddCors(options =>
{
options.AddPolicy(CorsPolicy, builder => builder.WithOrigins("http://localhost:3000", "http://localhost:4200", "http://localhost:1234"")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((host) => true));
});

Related

IdentityServer4 sometimes returns 500 for google and microsoft authentication

Sometimes google and microsoft authentication returns 500 on IdentityServer4.
Specifically, /signin-google and /signin-microsoft are returning 500 errors, and the situation is as follows
・About 3,000 requests per minute.
・About 80 of them return 500 errors.
・The rest return 301.
Do you know the cause?
Is there a problem with the number of accesses or the external authentication side?
Also, will upgrading to IdenityServer 6 and registering a license solve the problem?
Here is the code
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
AppSettings.Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer(options =>
{
options.Caching.ClientStoreExpiration = TimeSpan.FromMinutes(60);
options.Caching.ResourceStoreExpiration = TimeSpan.FromMinutes(60);
options.Caching.CorsExpiration = TimeSpan.FromMinutes(60);
})
.AddRedirectUriValidator<RedirectUriValidator>()
.AddInMemoryCaching()
.AddInMemoryIdentityResources(new IdentityResource[]
{
new IdentityResources.OpenId(), // OIDC認証を使用
new IdentityResources.Profile(),
})
.AddInMemoryApiScopes(new ApiScope[]
{
new ApiScope(IdentityServerConstants.LocalApi.ScopeName),
})
.AddInMemoryPersistedGrants()
.AddInMemoryCaching()
.AddInMemoryClients(Configuration.GetSection("Clients"))
.AddDeveloperSigningCredential()
.AddAspNetIdentity<User>();
services.ConfigureApplicationCookie(config =>
{
config.LoginPath = "/Web/User/Login";
config.LogoutPath = "/Web/User/Logout";
config.ExpireTimeSpan = TimeSpan.FromSeconds(Common.LoginTimeoutSeconds);
});
services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Common.TokenCreateKey)),
ClockSkew = TimeSpan.Zero,
};
}).AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = Configuration.GetValue<string>("GoogleClient:ClientId");
options.ClientSecret = Configuration.GetValue<string>("GoogleClient:ClientSecret");
}).AddMicrosoftAccount(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = Configuration.GetValue<string>("MicrosoftClient:ClientId");
options.ClientSecret = Configuration.GetValue<string>("MicrosoftClient:ClientSecret");
});
services.AddLocalApiAuthentication();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
One problem that I see is that you have these in the wrong order:
app.UseAuthorization();
app.UseAuthentication();
app.UseIdentityServer();
Authorization should always be last:
app.UseIdentityServer();
app.UseAuthorization();
If I am not wrong, you can also remove UseAuthentication() because UseIdentityServer adds that for you.

Invalid_request, unauthorized_client using OpenId, OAuth2, Cognito, .NET Core 3.1 and Swagger

I am trying to add an authentication layer in my API with OpenId and OAuth2 but when I make the call, passing the token in the header, I keep receiving
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: 'invalid_request', error_description: 'unauthorized_client', error_uri: 'error_uri is null'.
I have configured AWS Cognito, the startup.cs, and I can successfully get a JWT token from Swagger or Postman.
Do you see something configured badly, or missing, in my startup.cs?
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication(c =>
{
c.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
c.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
c.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(c =>
{
c.ResponseType = Configuration["Authentication:Cognito:ResponseType"];
c.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
c.ClientId = Configuration["Authentication:Cognito:ClientId"];
c.Authority = "https://auth.myauthserver.com";
c.Scope.Add("myscope");
c.GetClaimsFromUserInfoEndpoint = true;
});
// Configure named auth policies that map directly to OAuth2.0 scopes
services.AddAuthorization(c =>
{
c.AddPolicy("myscope", p => p.RequireClaim("scope", "myscope"));
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "MyAPI", Version = "v1"
});
c.OperationFilter<AddAuthHeaderOperationFilter>();
c.AddSecurityDefinition("bearer", //Name the security scheme
new OpenApiSecurityScheme{
Flows = new OpenApiOAuthFlows()
{
ClientCredentials = new OpenApiOAuthFlow()
{
TokenUrl = new Uri("https://auth.myauthserver.com/oauth2/token"),
Scopes = new Dictionary<string, string>(){ {"myscope", "Access API"}},
AuthorizationUrl = new Uri("https://auth.myautherver.com/oauth2/authorize")
}
},
Type = SecuritySchemeType.OAuth2,
OpenIdConnectUrl = new Uri("https://cognito-idp-url.../.well-known/openid-configuration"),
BearerFormat = "JWT",
In = ParameterLocation.Header,
Scheme = "bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
},
OpenIdConnectUrl = new Uri("https://cognito-idp-url.../.well-known/openid-configuration")
},new List<string>(){"myscope"}
}
});
});
services.AddOptions();
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.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();
else
app.UseHsts();
app.UseCors(policy =>
policy
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyOrigin()
.SetPreflightMaxAge(TimeSpan.FromDays(1))
);
app.UseCors("CorsPolicy");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
app.UseAuthentication();
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint($"v1/swagger.json", "MyAPI v1");
c.OAuth2RedirectUrl("https://auth.myauthserver.com/signin-oidc");
});
}
The controller has the [Authorize] and [Produces("application/json")] attributes on it.
This is the AddAuthHeaderOperationFilter:
public class AddAuthHeaderOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var isAuthorized = (context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
|| context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any())
&& !context.MethodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any(); // this excludes methods with AllowAnonymous attribute
if (!isAuthorized) return;
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
var jwtbearerScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" }
};
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement { [jwtbearerScheme] = new string []{} }
};
}
}
This is part of the appsettings.json:
"Authentication": {
"Cognito": {
"ClientId": "47...",
"IncludeErrorDetails": true,
"MetadataAddress": "https://cognito-idp-url.../.well-known/openid-configuration",
"RequireHttpsMetadata": false,
"ResponseType": "code",
"SaveToken": true,
"TokenValidationParameters": {
"ValidateIssuer": true
}
}
},
Cognito is configured to accept Client Credentials OAuth flow and the Allowed Auth Scope myscope selected.
And this is a curl example:
curl -X GET "https://localhost:5001/v1/MyController/2" -H "accept: application/json" -H "Authorization: Bearer eyJraWQiOiI2dGFPTW..."
Thank you
I have finally found a solution to my question. Not everything was configured well, I'll leave here the startup.cs that works with the Client Credentials flow and allows the authentication from Swagger and OpenAPI.
Authentication:Cognito:Authority is configured in the appsettings.json as "Authority": "https://cognito-idp.eu-west-1.amazonaws.com/your_region-id"
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services.AddControllers();
Initializer.RegisterServices(services);
services.AddAuthentication(o =>
{
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(
parameters.ValidIssuer + "/.well-known/jwks.json");
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
return (IEnumerable<SecurityKey>) keys;
},
ValidIssuer = Configuration["Authentication:Cognito:Authority"],
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
ValidateLifetime = true,
ValidateAudience = false,
};
options.IncludeErrorDetails = true;
options.SaveToken = true;
options.Authority = Configuration["Authentication:Cognito:Authority"];
options.RequireHttpsMetadata = true;
})
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["Authentication:Cognito:Authority"];
options.RequireHttpsMetadata = false;
options.ClientId = Configuration["Authentication:Cognito:ClientId"];
options.Scope.Add("myscope");
});;
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(ApiVersion, new OpenApiInfo
{
Title = "MyAPI", Version = "v1"
});
c.OperationFilter<AddAuthHeaderOperationFilter>();
c.AddSecurityDefinition("bearer", //Name the security scheme
new OpenApiSecurityScheme
{
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri("https://auth.myauthserver.com/oauth2/token"),
Scopes = new Dictionary<string, string> {{"myscope", "Access API"}},
AuthorizationUrl = new Uri("https://auth.myauthserver.com/oauth2/authorize")
}
},
Type = SecuritySchemeType.OAuth2,
OpenIdConnectUrl =
new Uri(
"https://cognito-idp.eu-west-1.amazonaws.com/your_region-id/.well-known/openid-configuration"),
BearerFormat = "JWT",
In = ParameterLocation.Header,
Scheme = "bearer"
});
});
services.AddOptions();
}
// 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();
else
app.UseHsts();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
app.UseAuthentication();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ApiVersion}/swagger.json", "MyAPI v1");
c.OAuth2RedirectUrl("https://auth.myauthserver.com/signin-oidc");
c.OAuthConfigObject = new OAuthConfigObject
{
ClientId = Configuration["Authentication:Cognito:ClientId"],
UsePkceWithAuthorizationCodeGrant = true
};
});
}
I hope it helps.

How to work with SignalR in ASP.NET Core 3.0

We're working on an asp.net core project that depends on SignalR. Lastly we updated our project from ASP.NET Core 2.2 to 3.0 and SignalR stopped working. In the documentation we configured everything (I think correctly) but is still doesn't work. What did we miss?
ASP.NET Core 3.0 API
Startup:
public class Startup
{
private readonly string corsPolicy = "CorsPolicy";
private static string[] AllowdOrigins() => return new string[] {"localhost:4200","example.com"};
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
ConfigureAuthentication(services);
///MICROSOFT SQL DATABASE
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")
));
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.KnownProxies.Add(IPAddress.Parse("XX.XX.XX.XX"));
});
services.AddSignalR();
services.AddControllers();
//services dependencies
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(corsPolicy);
//app.UseForwardedHeaders(new ForwardedHeadersOptions
//{
// ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
//});
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chat");
});
DummyData.Initialize(app);
}
private void ConfigureAuthentication(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(corsPolicy,
builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowAnyOrigin()
.AllowCredentials()
.WithOrigins(AllowdOrigins());
});
});
services.AddHttpContextAccessor();
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
}
Chathub:
[EnableCors("CorsPolicy")]
public class ChatHub : Hub
{
private static Dictionary<string, int> onlineClientCounts = new Dictionary<string, int>();
private static readonly string FrontPrefix = "_FRONT";
public ChatHub()
{
}
[HubMethodName("ConnectFrontend")]
public async Task ConnectFrontend(int sessionId)
{
//////
}
[HubMethodName("ConnectDevice")]
public async Task ConnectDevice(int sessionId)
{
//// This method should be called but it isn't.
}
public void DisConnect(int sessionId, int userId)
{
//////////
}
[HubMethodName("SendJsonToFrontends")]
public async Task SendJsonToFrontends(int sessionId, string jsonString)
{
///
}
[HubMethodName("SendJsonToAll")]
public async Task SendJsonToAll(int sessionId, string jsonString)
{
////
}
}
Angular project
SignalRService:
export class SignalRService {
private connection: signalR.HubConnection;
public newMessage = new Subject<SocketMessage>();
constructor() {
}
public connectFront() {
this.connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:2525/chat")//(environment.baseSignalR)
.configureLogging(signalR.LogLevel.Trace)
.build();
this.connection.on("ReceiveJson", data => { this.handleJsonMessage(data) });
// handles the first connection message
this.connection.start().then(() => this.sendConnectionMessage());
// handles the incoming messages
this.connection.on("ReceiveJson", data => this.handleJsonMessage(data));
this.connection.on("ReceiveJsonFrontend", data => this.handleJsonMessage(data));
}
private sendConnectionMessage() {
var sessionId = sessionStorage.getItem("SessionId");
if (sessionId != null) {
this.connection.invoke("ConnectFrontend", sessionId).catch((error) => { debugger; console.log(error); });
}
}
public sendWebsocketMessageToAll(msg: SocketMessage) {
var sessionId = sessionStorage.getItem("SessionId");
if (sessionId != null) {
this.connection.invoke("SendJsonToAll", sessionId, JSON.stringify(msg)).catch((error) => console.log(error));
}
}
public sendWebsocketMessageToFrontend(msg: SocketMessage) {
var sessionId = sessionStorage.getItem("SessionId");
if (sessionId != null) {
this.connection.invoke("SendJsonToFrontends", sessionId, JSON.stringify(msg)).catch((error) => console.log(error));
}
}
private handleJsonMessage(data: string) {
this.newMessage.next(this.getSocketMessage(data));
}
public getSocketMessage(data: string): SocketMessage {
var msg: SocketMessage = JSON.parse(data);
return msg;
}
public disconnect() {
this.connection.stop();
}
}
angular output:
Api output:
Just create an web app API with angular template you can view my code for your ref
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddSignalR().AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.WriteIndented = false;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapHub<ChatHub>("/chatHub");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
In FE side. Note: use new package #microsoft/signalr
import * as signalR from "#microsoft/signalr";
#Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
title = 'app';
ngOnInit() {
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.build();
connection.on("receiveMessage", (username: string, message: string) => {
console.log(username);
console.log(message);
});
connection.start().then(() => {
connection.send("sendMessage", "aaa", "aaaa")
.then(() => console.log("done"));
}).catch(err => document.write(err));
}
}
Well I finaly figured it out with the help of Tony,
Apperently It went wrong in the methods of the SignalR chat hub. The methods allowed only integers as parameters but it needed to be strings. I don't know it it has to do with some other settings but my guess now is that signalr cant convert the request parameters to something other then strings.
When I changed it to strings it worked.
It seems, that you have configured JWT based authentication on the server side and do not provide a token for SignalR connection. Try to provide a token using the accessTokenFactory:
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${environment.urlAddress}/chathub`, {
accessTokenFactory: () => this.token
})
.build()

.Net Core Firebase Google Email Authentication With JWT - 401

I'm trying to use firebase email authentication on my .net core web-backend via JWT. I couldn't find detailed and clear example.
I get successful login on my android app then i get the IdToken.
I'm sending IdToken with prefix "Bearer" on PostMan(or app) to my Controller. But it gives 401
Whatever i tried i couldn't get the 200. Only 401.
My service Configuration:
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://securetoken.google.com/myapp-c1e32";
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/myapp-c1e32",
ValidateAudience = true,
ValidAudience = "myapp-c1e32",
ValidateLifetime = true
};
});
My Controller:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public ActionResult GetAllRoomsWithOtherInfos(int id)
{
var rooms = _roomService.GetAllRoomsWithOtherInfos(id);
return Ok(rooms);
}
My Request:
http://localhost:5000/Room/GetAllRoomsWithOtherInfos/1
Exception Message On Log:
Bearer was not authenticated. Failure message: IDX10501: Signature
validation failed. Unable to match keys:
Also my app and service configurations
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<TaraftarServerContext>(options => options.UseMySql(Configuration.GetConnectionString(GlobalVariables.DbConnectionName)));
services.AddMvc(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver())
.AddFluentValidation();
services.AddKendo();
services.AddSignalR(hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
})
.AddJsonProtocol(jsonOptions =>
{
jsonOptions.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
jsonOptions.PayloadSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
services.AddWebClientServices();
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// this method is above configuration where the jwt is ( My service Configuration:)
services.AddFirebaseAuthentication(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
var supportedCultures = new[]
{
new CultureInfo("tr")
};
app.UseRequestLocalization(
new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("tr"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
FallBackToParentCultures = true,
FallBackToParentUICultures = true,
RequestCultureProviders = null
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Room}/{action=Index}/{id?}");
});
}
.Net Core Version : 2.2

ASP.NET Core 2.1 API JWT token Session.id changes on every request

I have a asp.net core 2.1 API that I connect to using and Angular 4 app and authenticate thru a JWT token. I also have 2 SignalR hubs there as well.
The authentication works nice and I am sure it works because after login I have access to the methods and classes I have set [Authorize] on.
The problem is the injected _accessor.HttpContext.Session.Id changes every time with every request. So the real issues is I cannot use session variables.
I am at a loss and it seems to me I am missing something here.
Can someone please help me with some ideas? Any help is much appreciated.
This is my startup.cs so far:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
if (env.IsDevelopment())
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath+"/logs/", "log-{Date}.txt"))
//, outputTemplate: "{MachineName} {EnvironmentUserName}: {Message:lj}{NewLine}{Exception}"
.WriteTo.Seq("http://192.168.1.164:5341")
.Enrich.WithMachineName()
.Enrich.WithEnvironmentUserName()
.CreateLogger();
}
else
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath + "/logs/", "log-{Date}.txt"))
.Enrich.WithMachineName()
.Enrich.WithEnvironmentUserName()
.CreateLogger();
}
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
services.AddDbContext<PaymentServicesContext>(options => options.UseSqlServer(Configuration.GetConnectionString("PaymentDatabase")));
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(10);
options.Cookie.HttpOnly = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
LifetimeValidator = (before, expires, token, param) =>
{
return expires > DateTime.UtcNow;
},
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
ValidateActor = false,
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(key)
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAutoMapper();
services.AddCors();
services.AddSignalR(options => options.EnableDetailedErrors = true);
///services
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IBrainzService, BrainzService>();
services.AddTransient<ISecurityService, SecurityService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddSerilog();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials())
.UseStaticFiles()
.UseWebSockets();
app.UseAuthentication();
if (!env.IsDevelopment())
{
app.UseHttpsRedirection();
}
app.UseWebSockets();
app.UseSignalR(
routes =>
{
routes.MapHub<MessagingHub>("/messagingHub");
routes.MapHub<UpdatesHub>("/updatesHub");
});
app.UseSession();
app.UseMvc();
}
}
The configuration works.
The problem was that Angular's HttpClient does not store/get/send cookies by default as I have found out the hard way.
All I had to do was add {withCredentials: true } option to the login request and the session cookie popped into the browser!
To get the session back you have to add this option to all requests and the API will know the session!
One solution would be to create an Interceptor to intercept all requests before it leaves the client application. This tutorial explains it well. You can also do this on a per-request level by setting the withCredentials to true.
const requestOptions = {
headers: new HttpHeaders({
'Authorization': "my-request-token"
}),
withCredentials: true
};

Categories