Unable to resolve this CORS issue with a react frontend - c#

I have read multiple solutions but somehow none worked for me. I am still getting CORS error. I am using a react front end and tried all solutions related to the frontend. I think my problem has to do with the backend .NET
this is how my controller CORS settings look like. I tried the wild card but it does not work either, I am now trying with the frontend address and port and still having CORS issues. any suggestion ?
[EnableCors(origins: ALLOWED_CORS_ORIGINS, headers: "*", methods: "*", SupportsCredentials = true)]
public class TroubleshootingDataController : ApiController
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const string ALLOWED_CORS_ORIGINS = "http://localhost:3000/";
startUp.cs
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
if (!Debugger.IsAttached)
{
//ConfigureAuth(app);
GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnection", new SqlServerStorageOptions { SchemaName = "HangfireConnLiving" });
var filter = new BasicAuthAuthorizationFilter(
new BasicAuthAuthorizationFilterOptions
{
RequireSsl = true,
LoginCaseSensitive = true,
Users = new[]
{
new BasicAuthAuthorizationUser
{
Login = "",
PasswordClear = ""
}
}
});
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[]
{
filter
}
});
app.UseHangfireServer();
}
}
}
}

On the backend on the Startup.cs just add this
On configure.
public void Configure(IApplicationBuilder appBuilder, IWebHostEnvironment webHostEnvironment)
{
appBuilder.UseCors(); // this line must before the appBuilder.UseEndPoints();
}
On ConfigureServices, we configure the cors
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin();
builder.AllowAnyHeader();
builder.AllowAnyMethod();
});
});
}
Edit:
It seams you are using a diferent framework or something like that, that I'm used to work with.
try to add this
app.UseCors(CorsOptions.AllowAll);
before the if (!Debugger.IsAttached)

Related

Authorize attribute not working with IdentityServer4 and .NET Core 3.1

I have a .NET Core 3.1 project using Identity and IdentityServer4 to implement the Resource Owner Password grant type. I can get the tokens no problem but the [Authorize] attribute isn't working, it just lets everything through. An important note is that my API and Identity server are in the same project. From comments online it seems like it might be a middleware order issue but I can't seem to find a combination that works. I've double checked that when no Authorization header is attached, the endpoint code is still hit.
Here's my Startup.cs file:
using System;
using System.Collections.Generic;
using IdentityServer4.Models;
using LaunchpadSept2020.App;
using LaunchpadSept2020.App.Repositories;
using LaunchpadSept2020.App.Repositories.Interfaces;
using LaunchpadSept2020.App.Seeds;
using LaunchpadSept2020.Models.Entities;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace LaunchpadSept2020.Api
{
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)
{
// Set up the database
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"),
b =>
{
b.MigrationsAssembly("LaunchpadSept2020.App");
})
);
services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = true;
});
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.ApiName = "launchpadapi";
options.Authority = "http://localhost:25000";
options.RequireHttpsMetadata = false;
});
services.AddIdentityServer()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"),
npgSqlOptions =>
{
npgSqlOptions.MigrationsAssembly("LaunchpadSept2020.App");
});
})
.AddInMemoryClients(Clients.Get())
.AddAspNetIdentity<User>()
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
.AddInMemoryApiResources(Resources.GetApiResources())
.AddInMemoryApiScopes(Resources.GetApiScopes())
.AddDeveloperSigningCredential();
services.AddControllers();
// Add Repositories to dependency injection
services.AddScoped<ICompanyRepository, CompanyRepository>();
services.AddScoped<IUserRepository, UserRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<User> userManager, RoleManager<IdentityRole> roleManager)
{
// Initialize the database
UpdateDatabase(app);
// Seed data
UserAndRoleSeeder.SeedUsersAndRoles(roleManager, userManager);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//app.UseHttpsRedirection();
app.UseRouting();
app.UseIdentityServer(); // Includes UseAuthentication
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
// Update the database to the latest migrations
private static void UpdateDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices
.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
{
context.Database.Migrate();
}
}
}
}
internal class Clients
{
public static IEnumerable<Client> Get()
{
return new List<Client>
{
new Client
{
ClientId = "mobile",
ClientName = "Mobile Client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = { new Secret("MySecret".Sha256()) },
AllowedScopes = new List<String> { "launchpadapi.read" }
//AllowAccessTokensViaBrowser = true,
//RedirectUris = { "http://localhost:25000/signin-oidc" },
//PostLogoutRedirectUris = { "http://localhost:25000/signout-callback-oidc" },
//AllowOfflineAccess = true
}
};
}
}
internal class Resources
{
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
{
Name = "role",
UserClaims = new List<string> {"role"}
}
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource
{
Name = "launchpadapi",
DisplayName = "Launchpad API",
Description = "Allow the application to access the Launchpad API on your behalf",
Scopes = new List<string> { "launchpadapi.read", "launchpadapi.write"},
ApiSecrets = new List<Secret> {new Secret("ScopeSecret".Sha256())},
UserClaims = new List<string> {"role"}
}
};
}
public static IEnumerable<ApiScope> GetApiScopes()
{
return new[]
{
new ApiScope("launchpadapi.read", "Read Access to Launchpad API"),
new ApiScope("launchpadapi.write", "Write Access to Launchpad API")
};
}
}
}
And my controller:
using System.Collections.Generic;
using System.Threading.Tasks;
using LaunchpadSept2020.App.Repositories.Interfaces;
using LaunchpadSept2020.Models.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace LaunchpadSept2020.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CompanyController : ControllerBase
{
private readonly ICompanyRepository _companyRepository;
public CompanyController(ICompanyRepository companyRepository)
{
_companyRepository = companyRepository;
}
[HttpPost]
[Authorize]
public async Task<ActionResult<CompanyVM>> Create([FromBody] CompanyCreateVM data)
{
// Make sure model has all required fields
if (!ModelState.IsValid)
return BadRequest("Invalid data");
try
{
var result = await _companyRepository.Create(data);
return Ok(result);
}
catch
{
return StatusCode(500);
}
}
[HttpGet]
[Authorize]
public async Task<ActionResult<List<CompanyVM>>> GetAll()
{
try
{
var results = await _companyRepository.GetAll();
return Ok(results);
}
catch
{
return StatusCode(500);
}
}
}
}
I think a general issue is that you mix IdentityServer in the same app as ASP.NET Identity, in general my experience is that it gets hard to know who is doing what and its hard to fully understand. I always recommend putting IdentityServer and the API in independent services. Just to get a clean separation of concerns.
For local API authentication you need the following additional configuration in Startup:
public void ConfigureServices(IServiceCollection services)
{
....
// After services.AddIdentityServer()
services.AddLocalApiAuthentication();
}
For reference see the docs.
And then you need to specificy the local API policy as part of the Authorize attribute on your API:
[Authorize(LocalApi.PolicyName)]
See a local API example.

How to properly use ISession and session cookie in angular - asp.net.core app

I'm middle in creating web app with frontend in Angular 7 and backend in ASP.NET.Core 2.1. On the server side I want to implement a simple session functionality. So in Startup.cs I configure session by using AddDistributedMemoryCache(), AddSession() and UseSession(). In server domain everything works fine. I have my session cookie and I have access to the session from controllers, repos etc.
Problem is when I call a html method from another domain (from front in Angular). In HtmlRequest there is no info about cookie session so HttpContext.Session.Id is always different. Can I send value of My.Cookie to front to create cookie on front side by ngx-cookie-service? Then this cookie will be send automatically to backend in HtmlRequest. How I can access to this cookie from backend? I can only get cookie from request and request from frontend doesn't have My.Cookie. Or exchange My.Cookie between front and backend should happen automatically?
For now I created my own static storage as singleton service but I think it should be better solution with use ISession.
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services
.AddCors(options =>
{
options.AddPolicy("MyCorsPolicy", policyBuilder =>
{
policyBuilder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowCredentials()
.AllowAnyHeader();
});
})
.AddDistributedMemoryCache()
.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
options.Cookie = new CookieBuilder { Name = "My.Cookie", HttpOnly = false, IsEssential = true, SameSite = SameSiteMode.None };
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app
.UseCors("MyCorsPolicy")
.UseSession()
.UseMvc(routes =>
{
routes.MapRoute("default",
"{controller}/{action}",
new { controller = "Home", action = "Index" });
});
}
}
FooController.cs
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase
{
[HttpGet]
public IActionResult FooGet()
{
return new OkObjectResult(new { message = HttpContext.Session?.GetString("Message") ?? string.Empty, sessionId = HttpContext.Session.Id });
}
[HttpPost]
public IActionResult FooPost(dynamic clientSite)
{
string message = clientSite["message"];
HttpContext.Session.SetString("Message", message);
return new OkObjectResult(true);
}
}
rest.server.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class RestServer {
private url = `http://${location.hostname}:60000\\api\\foo`;
constructor(private http: HttpClient) { }
public GetFoo(): Observable<any> {
return this.http.get(this.url);
}
public PostFoo(message: string): Observable<any> {
return this.http.post(this.url, {message});
}
}

How to implement authorization using GraphQL.NET at Resolver function level?

I am looking for sample code and examples regarding how to implement authorization at resolver function level using GraphQL.NET and ASP.NET CORE 2.
Basically I am trying to prevent the execution of query if the request is not authorized.
Can anyone help me to get some good tutorials or code samples as reference for the implementation.
For graphql-dotnet/authorization, the page for AspNetCore has not been released, refer Add GraphQL.Server.Authorization.AspNetCore NuGet package #171.
You could implement Authorization.AspNetCore for your own use.
After implement Authorization.AspNetCore, you could configure the Authorize like:
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
Configuration = configuration;
Environment = hostingEnvironment;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
// 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.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
services.AddGraphQL(options =>
{
options.EnableMetrics = true;
options.ExposeExceptions = Environment.IsDevelopment();
//options.
})
.AddGraphQLAuthorization(options =>
{
options.AddPolicy("Authorized", p => p.RequireAuthenticatedUser());
//var policy = new AuthorizationPolicyBuilder()
// .
//options.AddPolicy("Authorized", p => p.RequireClaim(ClaimTypes.Name, "Tom"));
});
//.AddUserContextBuilder(context => new GraphQLUserContext { User = context.User });
services.AddSingleton<MessageSchema>();
services.AddSingleton<MessageQuery>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// 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");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseGraphQL<MessageSchema>("/graphql");
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions()
{
Path = "/ui/playground"
});
app.UseGraphiQLServer(new GraphiQLOptions
{
GraphiQLPath = "/ui/graphiql",
GraphQLEndPoint = "/graphql"
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Schema
public class MessageQuery : ObjectGraphType<Message>
{
public MessageQuery()
{
Field(o => o.Content).Resolve(o => "This is Content").AuthorizeWith("Authorized");
Field(o => o.SentAt);
Field(o => o.Sub).Resolve(o => "This is Sub");
}
}
For complete demo, refer GraphQLNet.
To get GraphQL.Net's authorization to work in ASP.NET Core, first install this package:
GraphQL.Server.Authorization.AspNetCore
In Startup.cs add the following in ConfigureServices. Make sure to add these using statements:
using GraphQL.Validation;
using GraphQL.Server.Authorization.AspNetCore;
public void ConfigureServices(IServiceCollection services)
{
//... other code
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services
.AddTransient<IValidationRule, AuthorizationValidationRule>()
.AddAuthorization(options =>
{
options.AddPolicy("LoggedIn", p => p.RequireAuthenticatedUser());
});
//... other code
}
Now you'll be able to use AuthorizeWith() at the resolver level to protect the field. Example:
public class MyQuery : ObjectGraphType
{
public MyQuery(ProductRepository productRepository)
{
Field<ListGraphType<ProductType>>(
"products",
resolve: context => productRepository.GetAllAsync()
).AuthorizeWith("LoggedIn");
}
}
You can also protect all queries by adding this.AuthorizeWith() to the top of the Query constructor like this:
public class MyQuery : ObjectGraphType
{
public MyQuery(ProductRepository productRepository)
{
this.AuthorizeWith("LoggedIn");
Field<ListGraphType<ProductType>>(
"products",
resolve: context => productRepository.GetAllAsync()
);
}
}
With that, any unauthenticated access to your GraphQL endpoint will be rejected.
Now in terms of logging someone in, there are many ways to do that. Here's a quick Cookie based authentication example:
Configure cookie based authentication in Startup.cs' ConfigureServices:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(o =>
{
o.Cookie.Name = "graph-auth";
});
Use mutation to log someone in:
public class Session
{
public bool IsLoggedIn { get; set; }
}
public class SessionType : ObjectGraphType<Session>
{
public SessionType()
{
Field(t => t.IsLoggedIn);
}
}
public class MyMutation : ObjectGraphType
{
public MyMutation(IHttpContextAccessor contextAccessor)
{
FieldAsync<SessionType>(
"sessions",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "password" }),
resolve: async context =>
{
string password = context.GetArgument<string>("password");
// NEVER DO THIS...for illustration purpose only! Use a proper credential management system instead. :-)
if (password != "123")
return new Session { IsLoggedIn = false };
var principal = new ClaimsPrincipal(new ClaimsIdentity("Cookie"));
await contextAccessor.HttpContext.SignInAsync(principal, new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMonths(6),
IsPersistent = true
});
return new Session { IsLoggedIn = true };
});
}
}

Configure client caching when using OWIN static files

This is my Startup.cs where I map my index page to the route '/app'.
...
using Microsoft.Owin.FileSystems;
using Microsoft.Owin.StaticFiles;
using Microsoft.Owin.Diagnostics;
[assembly: OwinStartup(typeof(conApi.Startup))]
namespace conApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
////Set static files
ConfigureFiles(app);
//Enable Cors
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
public void ConfigureFiles(IAppBuilder app)
{
app.Map("/app", spa =>
{
spa.Use((context, next) =>
{
context.Request.Path = new PathString("/index.html");
return next();
});
spa.UseStaticFiles();
});
}
}
}
It works like a charm but I don't know how to configure the client caching. I would like to know how to set the Expires header if that is possible when using OWIN static files?
SOLUTION
Tratcher provided the link to the StaticFilesOptions class documentation etc which lead me to a solution. Added the StaticFilesOptions to the ConfigureFiles method like this:
public void ConfigureFiles(IAppBuilder app)
{
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = (StaticFileResponseContext) =>
{
StaticFileResponseContext.OwinContext.Response.Headers.Add("Cache-Control",new[] { "public", "max-age=1000" });
}
};
app.Map("/app", spa =>
{
spa.Use((context, next) =>
{
context.Request.Path = new PathString("/index.html");
return next();
});
spa.UseStaticFiles(staticFileOptions);
});
}
You can pass a StaticFilesOptions to UseStaticFiles. On the options use the OnPrepareResponse event to customize your responses. See http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.StaticFiles/StaticFileOptions.cs

Is RouteExistingFiles no longer part of asp.net 5?

I search github source and http://docs.asp.net/en/latest, but can't find any documentation for RouteExistingFiles. I've tried adding it onto routes.RouteExistingFiles, but this won't compile. Has this option be removed or rethought? Can it be accessed from the StartUp?
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
StaticFileOptions option = new StaticFileOptions();
FileExtensionContentTypeProvider contentTypeProvider = (FileExtensionContentTypeProvider) option.ContentTypeProvider;
contentTypeProvider.Mappings.Add(".yqs", "text/plain");
app
.UseStaticFiles(option)
.UseDefaultFiles()
.UseFileServer()
.UseMvc(routes =>
{
routes.MapRoute(
"YQ Controller",
"{*src}",
new { controller = "YQFile", action = "OnDemand" },
new { src = #"(.*?)\.(yqs)" }
);
});
}
}
Though I can't find any documentation, it seems this option isn't in asp.net 5. Now, it seems that routing and other configuration in StartUp.Configure() will take precedence over proceeding configuration. So, bringing the app.UseMvc() configuration forward in the chain will allow the route to take priority over app.UseStaticFiles().
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
app
.UseMvc(routes =>
{
routes.MapRoute(
"YQ Controller",
"{*src}",
new { controller = "YQFile", action = "OnDemand" },
new { src = #"(.*?)\.(yqs)" }
);
})
.UseStaticFiles()
.UseDefaultFiles()
.UseFileServer();
}
}

Categories

Resources