I am trying to implement SSO Authentication in ASP.Net Core 3.1 and deploy in Pivot Cloud Foundry(PCF).
In local it's working fine but after deployment getting below error
An unhandled exception occurred while processing the request.
Exception: The oauth state was missing or invalid.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
HomeController.cs
#if !LOCALTEST
[Authorize]
#endif
public IActionResult Index()
{
string user = "";
if (User.Identity.IsAuthenticated)
{
user = User.Identity.Name;
}
else
{
// WindowsIdentity.GetCurrent
user = WindowsIdentity.GetCurrent().Name.Substring(WindowsIdentity.GetCurrent().Name.LastIndexOf(#"\") + 1);
}
TempData["user"] = user;
return View();
}
Manifest.yml
---
applications:
- name: ApplicationName
memory: 1G
stack: cflinuxfs3
buildpacks:
- dicf_dotnet_core_buildpack_2339_cflinuxfs3
instances: 2
disk_quota: 1G
env:
ASPNETCORE_ENVIRONMENT: Development
GRANT_TYPE: authorization_code
SSO_IDENTITY_PROVIDERS : XXX-sso
SSO_SCOPES : openid,roles,user_attributes
SSO_AUTO_APPROVED_SCOPES : openid,roles,user_attributes
SSO_USERINFO_URL : https://appsso.login.sr3.pcf.xxx.com/userinfo
services :
- serviceName
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvc();
services.Configure<MyOptions>(myOptions =>
{
myOptions.ConnString = Configuration.GetConnectionString("DefaultConnection");
});
services
.AddMvc()
.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
services.AddScoped<IRepository, RepositoryConcrete>();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddCloudFoundryContainerIdentity(Configuration);
services.AddAuthentication((options) =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CloudFoundryDefaults.AuthenticationScheme;
})
.AddCookie((options) =>
{
options.AccessDeniedPath = new PathString("/Home/AccessDenied");
})
.AddCloudFoundryOAuth(Configuration);
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddControllersWithViews();
var serviceInfos = CloudFoundryServiceInfoCreator.Instance(Configuration);
var ssoInfo = serviceInfos.GetServiceInfos<SsoServiceInfo>().FirstOrDefault()
?? throw new NullReferenceException("Service info for an SSO Provider was not found!");
userInfoEndPoint = ssoInfo.AuthDomain + "/userinfo";
}
// 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.UseExceptionHandler("/Home/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.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto
});
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Please help me to know what i am missing or required modification.
Thanking in Advance!
Related
Trying to store a string in a session while waiting for confirmation from the front end, but on the next request (confirmation request) the session is completely empty.
Config
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDistributedMemoryCache();
services.AddSession(options => {
options.Cookie.Name = ".SingleTouch.API.Session";
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.IsEssential = true;
options.Cookie.HttpOnly = true;
});
string allowedHosts = Configuration.GetValue<string>("AllowedHosts");
services.AddCors(options =>
{
options.AddPolicy(name: CorsAllowedUrls,
builder =>
{
builder.WithOrigins(allowedHosts)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllersWithViews(options =>
options.Filters.Add(new ApiExceptionFilter()));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(CorsAllowedUrls);
app.UseAuthentication();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
}
And in one endpoint I have
_httpContextAccessor.HttpContext.Session.SetString("payEvent", "bro you good?");
and on the the following endpoint it retrieves it again
_httpContextAccessor.HttpContext.Session.GetString("payEvent");
but on retrieval, the session is empty. It's definitely being added initially because if you inspect or run GetString in the first request the value exists. Asp core 6 is in use.
The Session has scoped lifetime, i.e. for any request you always get a new session. This is nesessary as each request can be issued by a differing user. If you want to store information for specific users, you need to do this in a singleton service.
I have a dotnet 5 web API with an Angular2+ front end, which I'm building from a previous 3.1 MVC version of the app. I'm having an issue with CORS while looking to authenticate with Yahoo that I did not have with the MVC version. The error I am getting is:
"Access to XMLHttpRequest at 'https://api.login.yahoo.com...' (redirected from 'https://localhost:5003/api/draft/yahooauth/') from origin 'https://localhost:5003' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."
My API Controller:
[EnableCors("MyPolicy")]
[HttpPost("yahooauth")]
public void YahooAuth()
{
string leftUrl = string.Format("{0}://{1}", HttpContext.Request.Scheme, HttpContext.Request.Host);
string returnUrl = $"{leftUrl}/api/draft/yahooexchange";
string id = configuration["YahooClientId"];
string secret = configuration["YahooClientSecret"];
string url = $"https://api.login.yahoo.com/oauth2/request_auth?client_id={id}&redirect_uri={returnUrl}&response_type=code&language=en-us";
Response.Redirect(url);
}
[HttpGet("yahooexchange/{code}")]
public IActionResult yahooexchange(string code)
{
// Code that is supposed to be triggered with returnUrl from Yahoo
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential
// cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
// requires using Microsoft.AspNetCore.Http;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<IdentityDbCxt>(options => options.UseSqlServer(Configuration.GetConnectionString("IdentityDb")));
services.AddDbContext<DraftContext>(options => options.UseSqlServer(Configuration.GetConnectionString("FSDraftDb")));
services.AddIdentity<AppUser, IdentityRole>(opts =>
{
opts.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<IdentityDbCxt>()
.AddDefaultTokenProviders();
services.AddTransient<IEmailSender, EmailSender>();
services.AddTransient<IAPICall, APICall>();
services.AddTransient<IDraftService, DraftService>();
services.AddCors(options =>
{
options.AddPolicy("MyPolicy",
builder =>
{
builder.WithOrigins("*")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvc()
.AddRazorRuntimeCompilation();
services.AddMemoryCache();
services.AddSession();
services.AddSingleton<IHtmlSanitizer, HtmlSanitizer>();
services.AddControllersWithViews(/*options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())*/);
services.AddRazorPages();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1",
new OpenApiInfo { Title = "API", Version = "v1" });
});
services
.AddControllers()
.AddNewtonsoftJson();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostEnvironment env, IServiceProvider services)
{
//app.UseMiddleware<GCMiddleware>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStatusCodePages();
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseSession();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}");
endpoints.MapControllerRoute(
name: "angular_fallback",
pattern: "{target:regex(draft|data|keeper|adminapi|admin`):nonfile}/{*catchall}",
defaults: new { controller = "Home", action = "Index" });
endpoints.MapRazorPages();
});
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
app.UseSpa(spa =>
{
string strategy = Configuration.GetValue<string>("DevTools:ConnectionStrategy");
if (strategy == "proxy")
{
spa.UseProxyToSpaDevelopmentServer("http://127.0.0.1:4200");
}
else if (strategy == "managed")
{
spa.Options.SourcePath = "../ClientApp";
spa.UseAngularCliServer("start");
}
});
}
}
I've looked at a few sources, including one quite hopeful post here, but this did not solve my problem: Problems with CORS Response to preflight in dotnet core 3.1
In case it's helpful, I'm looking to do step 2: https://developer.yahoo.com/oauth2/guide/flows_authcode/
Edit: This is the documentation I used regarding CORS: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-5.0#attr
Try to use this syntax and move AddCors to the top of ConfigureServices. Assign name to UseRouting.
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
.......
....
app.UseRouting();
app.UseCors("MyPolicy");
app.UseAuthentication();
app.UseAuthorization();
.....
So I ended up figuring out a workaround and just created a new MVC controller, as my previous MVC app worked fine.
In addition, I realized that I had not updated my Yahoo Developer App for the new return URI. This did not fix my CORS problem, but it was preventing this from properly working.
Code from Angular to go to the new controller.
window.location.href = "https://localhost:5003/yahoo/index";
My MVC Controller:
public class YahooController : Controller
{
private IAPICall apiCall;
public YahooController(IAPICall aCall)
{
apiCall = aCall;
}
// getting session data on player values
private DraftService GetDS()
{
DraftService draftService = HttpContext.Session.GetJson<DraftService>("DraftService") ?? new DraftService();
return draftService;
}
// saving session data on player values
private void SaveDS(DraftService ds)
{
HttpContext.Session.SetJson("DraftService", ds);
}
[HttpGet]
public void Index()
{
string returnUrl = Url.Action("yahooexchange",
"yahoo", new
{
},
protocol: HttpContext.Request.Scheme);
string url = apiCall.YahooAuthUrl(returnUrl);
Response.Redirect(url);
}
public void yahooexchange(string code)
{
string content = apiCall.YahooGetToken(code);
var jContent = JsonConvert.DeserializeObject<JObject>(content);
string accessToken = jContent.GetValue("access_token").ToString();
string refreshToken = jContent.GetValue("refresh_token").ToString();
string leftUrl = string.Format("{0}://{1}", HttpContext.Request.Scheme, HttpContext.Request.Host);
DraftService ds = GetDS();
ds.YahooAccessToken = accessToken;
ds.YahooRefreshToken = refreshToken;
SaveDS(ds);
string url = leftUrl + "/draft/" + ds.DraftId;
Response.Redirect(url);
}
}
hello community I have a project created with asp.net core, blazor webassembly and Identity4, the local project works very well, I published it on an IIS server so that it could be seen from the internet, the project was loaded perfectly, the only detail is that when I enter the login it is loading, until I give it to empty cache and load in a forced way, I can enter the login form, then I enter my credentials and again it stays loading until I empty the cache again and enter the page that indicates I'm already logged in.
How can I enter the login form without emptying the cache and loading
forcefully?
When I click the login button, it sends me to this route but it's wrong:
connect/authorize?client_id=BlazorApp.Client&redirect_uri=https%3A%2F%2
this is the route that is fine to take me:
Identity/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id
this is my class startup:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SimulexContext>
(options => options.UseSqlServer(Configuration.GetConnectionString("SimulexConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
.AddProfileService<IdentityProfileService>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.Configure<PayPalConfiguration>(Configuration.GetSection("PayPal"));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAutoMapper(typeof(Startup));
services.AddScoped<NotificacionesService>();
services.AddScoped<IAlmacenadorDeArchivos, AlmacenadorArchivosLocal>();
services.AddHttpContextAccessor();
services.AddMvc().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
// 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.UseWebAssemblyDebugging();
}
else
{
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.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/api/config/notificacionesllavepublica", async context =>
{
var configuration = context.RequestServices.GetRequiredService<IConfiguration>();
var llavePublica = configuration.GetValue<string>("notificaciones:llave_publica");
await context.Response.WriteAsync(llavePublica);
});
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
}
this is my page appsettingsjson:
"IdentityServer": {
"Key": {
"Type": "Development"
},
"Clients": {
"BlazorApp.Client": {
"Profile": "IdentityServerSPA"
}
}
},
Can you share your startup for the blazor app?
I had the same issue a couple weeks ago but with an asp.net mvc app when integrating is4 with which had identity configured. So it could be same for you.
Enet's solution might work, but I have not tried it. Below is another solution which worked for me only if you have Identity configured in your Blazor app. Try setting the schemes in your services.AddAuthentication and AddOpenIdConnect:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "Oidc";
options.DefaultAuthenticateScheme = "Cookies"; // <-- add this line
})
.AddCookie("Cookies", options =>
{
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies"; // <-- add this line
})
;
In my application I'm using Microsoft Identity Authorize to load an AspNet Core view and require the user credentials to access the page.
My problem is that IIS Express executing throug VS 2019, works just fine but when I publish it on IIS (both problem in self-dependent / self-contained) every time that I made a request to access the view it ask me to make the login over and over again.
My Startup.cs file:
namespace SistemaGestor.SOO.Mvc
{
public class Startup
{
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.AddTransient<SOOContext>(srv => SOOContextFactory.CreateInstance(Configuration));
//Fernando Milanez 25/09/2019
services.AddTransient<IEmbarcacaoInterface,EmbarcacaoRepositorio>();
services.AddTransient<IBandeiraInterface, BandeiraRepositorio>();
services.AddTransient<IAutenticacao, AutenticacaoRepositorio>();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/Autenticacao/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
});
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddSessionStateTempDataProvider();
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions
{
SupportedCultures = new List<CultureInfo> { new CultureInfo("pt-BR") },
SupportedUICultures = new List<CultureInfo> { new CultureInfo("pt-BR") },
DefaultRequestCulture = new RequestCulture("pt-BR")
};
app.UseRequestLocalization(localizationOptions);
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
app.UseAuthentication(); //Requisito para ativar a autenticao, então é sempre assim adiciono o middlewaree depois defino para usar
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
In my Controller, I've input the [Authorize] attribute:
namespace SistemaGestor.SOO.Mvc
{
[Authorize]
//logical code below
}
I didn't post any error message because it does not show any error.
I am working on asp.net core signalr 1.1.0 under asp.net core 2.2 version. I want to
make the cross-domain request for web client and as well as a mobile client.
When I send request from javascript client, then this request blocked, and below error shows,
(index):1 Access to XMLHttpRequest at 'https://localhost:44373/chatHub/negotiate?token=12' from origin 'https://localhost:44381' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
My Javascript client code
var connection = new signalR.HubConnectionBuilder().withUrl("https://localhost:44373/chatHub?token="+12).build();
Signalr core service startup class code
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()/*WithOrigins("https://localhost:44381")*/
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//services.AddCors();
}
// 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();
}
else
{
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.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
//app.UseStaticFiles();
//app.UseCookiePolicy();
app.UseMvc();
}
builder.AllowAnyOrigin() its not working
builder => builder.WithOrigins("https://localhost:44381") its worked, but this is specific for this origin ,
I want to make AllowAnyOrigin()??
I got it working this way
On Configure services at the top
services.AddCors();
and in the Configure method
public class Startup
{
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
.AddDbContext<ChatContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 60000000;
});
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddMvcCore()
.AddAuthorization()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
var identityServerAuthOptions = Configuration.GetSection("Identity").Get<IdentityServerAuthenticationOptions>();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = identityServerAuthOptions.Authority;
options.RequireHttpsMetadata = identityServerAuthOptions.RequireHttpsMetadata;
options.ApiName = identityServerAuthOptions.ApiName;
});
var settings = new JsonSerializerSettings();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.ContractResolver= new CamelCasePropertyNamesContractResolver();
services.AddSignalR()
.AddJsonProtocol(options => {
options.PayloadSerializerSettings = settings;
});
services.AddTransient<IUserService, UserService>();
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//Data.AddData(app.ApplicationServices.GetService<ChatContext>());
app.Use(async (context, next) =>
{
if (string.IsNullOrWhiteSpace(context.Request.Headers["Authorization"]))
{
if (context.Request.QueryString.HasValue)
{
var token = context.Request.QueryString.Value.Split('&').SingleOrDefault(x => x.Contains("authorization"))?.Split('=')[1];
if (!string.IsNullOrWhiteSpace(token))
{
context.Request.Headers.Add("Authorization", new[] { $"Bearer {token}" });
}
}
}
await next.Invoke();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
app.UseSignalR(config =>
{
config.MapHub<UserHub>("/UsersHub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
}
}
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
Whole code can be found here. This used to work just fine for me. I haven't opened it lately though
Github Repo
I see two issues with your code. Let's tackle them one by one.
Allowing all origins for the entire application even though you need it only for the SignalR connection. Consider the below code if you want to apply CORS policy only for the signalR endpoint
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<UsersHub>("/UsersHub")
.RequireCors((policyBuilder) => policyBuilder
.WithOrigins("clientUrls")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
});
It is recommended to not allow all origins but if you have such a use case then the below workaround can fix your problem. This is the trick of using .SetIsOriginAllowed(_ => true)
.SetIsOriginAllowed(_ => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
Further if you want more information, have a look at this guide for more details.