I made a Json web token to protect my services but now somebody has made a lot of queries on my data base using my login service. Are there a way to block queries that come to another server?
The queries or attacks to my server come from different ips and different countries.
I use angular 9 and asp.net core.
// POST: api/Login
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(UsuarioLogin usuarioLogin)
{
var _userInfo = await AutenticarUsuarioAsync(usuarioLogin.Usuario, usuarioLogin.Password);
if (_userInfo != null)
{
return Ok(new { token = GenerarTokenJWT(_userInfo) });
}
else
{
return Unauthorized();
}
}
Enable Cross-Origin Requests (CORS) in ASP.NET Core
There are three ways to enable CORS:
In middleware using a named policy or default policy.
Using endpoint routing.
With the EnableCors attribute.
CORS with named policy and middleware
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://example.com",
"http://xxxx.com");
});
});
// services.AddResponseCaching();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
// app.UseResponseCaching();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Related
I have an asp.net core application and I need to add elastic logging, I decided to use Serilog for it, but I need to add a Correlation id into logs messages, I can't do it within only HTTP correlation id header because I have service bus handlers that also need to have correlation id. So I did it with default asp request middleware, but still have logs without it
request finished/started logs aren't have correlation id
here is my serilog setup
hostBuilder.UseSerilog(
(_, _, loggerConfiguration) =>
{
loggerConfiguration
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Elasticsearch(ConfigureElasticSink(configuration, environment))
.ReadFrom.Configuration(configuration);
});
and request middleware
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
public CorrelationIdMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, ICorrelationIdService correlationIdService)
{
context.Request.Headers.TryGetValue(Constants.CORRELATION_ID_HEADER_NAME, out var correlationId);
correlationIdService.Set(correlationId);
context.Request.Headers.TryAdd(Constants.CORRELATION_ID_HEADER_NAME, correlationIdService.CorrelationId);
LogContext.PushProperty(Constants.CORRELATION_ID, correlationIdService.CorrelationId);
await _next(context);
}
}
UDP
My startup file
using IM360.Logger;
using InMarket360EmailServiceWebApi.WebUI;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLoggerServices();
builder.Services.AddWebUIServices();
builder.Host.UseExternalLogging();
var app = builder.Build();
app.UseExternalLogging(); //middleware being added in this extension method
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");
app.Run();
public partial class Program { }
Logger extenstions file
public static IServiceCollection AddLoggerServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddScoped<ICorrelationIdService, CorrelationIdService>();
serviceCollection.AddHeaderPropagation(opt => opt.Headers.Add(Constants.CORRELATION_ID_HEADER_NAME));
return serviceCollection;
}
public static WebApplication UseExternalLogging(this WebApplication webApplication)
{
webApplication.UseMiddleware<CorrelationIdMiddleware>();
webApplication.UseHeaderPropagation();
return webApplication;
}
Have any ideas?
According to this Microsoft document you should be able to apply attribute like [RequiredScope("SomeScopeName")] to either controller level or action level to protect the API. But when I try it in my API, it doesn't seem to have any effect at all - regardless what scope name I use (I made sure I don't have the scope by that name in the token), I always get right in to the API actions that I supposed to fail. But at the same time, my policy attributes, such as [Authorize(Policy = "PolicyName")], works just fine. What am I missing?
[ApiController]
[RequiredScope("AnyRandomName")]
public class MyApiController : ControllerBase
{
UPDATE
Here is my Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services.AddControllers();
services.AddSwaggerGen(opt =>
{
opt.CustomSchemaIds(type => type.ToString() + type.GetHashCode());
});
services.Configure<HostOptions>(Configuration.GetSection(HostOptions.HOST));
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
services.AddAuthentication("Bearer").AddJwtBearer(options =>
{
options.Authority = Configuration[HostOptions.IDENTITYGATEWAY];
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
services.AddTransient<gRPCServiceHelper>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseExceptionHandler("/error-local-development");
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "GroupDemographicEFCore v1"));
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
and here is my API controller
[ApiController]
[Authorize]
[RequiredScope("NoSuchScope")]
public class MyApiController : ControllerBase
{
public MyApiController([NotNull] IConfiguration configuration, [NotNull] ILogger<MyApiController> logger,
[NotNull] gRPCServiceHelper helper) : base(configuration, logger, helper)
{
}
[HttpGet]
[Route("/clients/summary")]
public async Task<IActionResult> ClientsSummaryGet()
{
...
Note that I applied the attributes here on the controller level. But it makes no difference if I move them down to action level - the RequiredScope attributes always gets ignored.
UPDATE-1
I left out the AddAuthorization from my last post update, as I believe it is irrelevant to my issue here. I added it back now, with a few of the policies that I use. Once again, these policies are all working fine, and I don't see how this is relevant to the issue I have.
services.AddAuthorization(options =>
{
options.AddPolicy("OperatorCode", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("OperatorCode");
});
options.AddPolicy("OperatorCode:oprtr0", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("OperatorCode", "oprtr0");
});
options.AddPolicy("Role:User+OperatorCode:oprtr0", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("User");
policy.RequireClaim("OperatorCode", "oprtr0");
});
options.AddPolicy("Role:Admin||Role:User", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("Admin", "User");
});
});
Here is the access_token header
Here is the body of access_token
All we need to do is add
services.AddRequiredScopeAuthorization();
For the RequireScopeAttrubute to work, which is what AddMicrosoftIdentityWebApiAuthentication does under the hood to get it to work anyway.
What you need to do is to add and configure authorization in Startup.cs like, like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("ViewReports", policy =>
policy.RequireAuthenticatedUser()
.RequireRole("Finance")
.RequireRole("Management")
);
});
The policy says that the user must be authenticated and be in both roles. In this example RequireAuthenticatedUser() is optional.
Then you can use that policy like:
[Authorize(Policy = "ViewReports")]
public IActionResult ViewReports()
{
return View();
}
To get the role claim to work, you must define what the name of your role claim is in the token, by doing this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
});
Otherwise the role might not be found, because OpenIDConnect and Microsoft have different opinion on what the claim should be called.
In the long run, using polices will gives you better and cleaner code, because if you need to change the scopes in the future, you need to update all controllers classes. With a policy , you change it in one place.
Also, according to this issue at GitHub, it says:
RequiredScopes just checks at the scp or
http://schemas.microsoft.com/identity/claims/scope claims.
This means that you might need to do some claims transformation (renaming) to get the RequiredScope to map to the scope claim in your access token.
My codes:
installing these 2 packages:
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="4.5.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="1.21.1" />
Startup.cs, adding code in ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
services.AddControllers();
}
don't forget these two lines in Configure method:
app.UseAuthentication();
app.UseAuthorization();
My test controller:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using System.Collections.Generic;
namespace WebApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize]
[RequiredScope("User.Read")]
public class HomeController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
[HttpPost]
public string getRes() {
return "hello world";
}
}
}
Test result :
==============================================================
To protect an ASP.NET or ASP.NET Core web API, you must add the
[Authorize] attribute to one of the following items:
The controller itself if you want all controller actions to be
protected The individual controller action for your API
According to this section's example,
adding [Authorize] before the line [RequiredScope("AnyRandomName")] ?
[ApiController]
[Authorize]
[RequiredScope("AnyRandomName")]
public class MyApiController : ControllerBase
{
I'm trying to build an ASP.NET Core app (.NET 5.0) with Angular 12 integrated in. Both of these are new technologies for me. I'm coming from a .NETFramework/AngularJS environment.
I thought this question might have solved my problem, but it does not.
I built the project with Individual Account authentication type. I created a Users web api controller and simply applied the [Authorize] attribute to the GetUsers() api. It works fine without the attribute, but with it, I get a 401 Unauthorized error while logged in. No roles or policies have been setup.
I would expect the user to be authenticated and be able to access this api through the browser while in an active session. I've tried Postman as well.
Here is the web api code that illustrates the [Authorize] attribute on the GetUsers api:
namespace BrochureManagement.api
{
[Route("api/private/users")]
[ApiController]
public class UserController : ControllerBase
{
// GET: api/<UserController>
[HttpGet]
[Authorize]
public object GetUsers()
{
var userService = new UserService();
return userService.GetUsers();
}
// GET api/<UserController>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<UserController>
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/<UserController>/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<UserController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
Here is the ConfigureServices and Configure methods in the Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<Role>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages()
.AddRazorRuntimeCompilation();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
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.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseAuthentication();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
//spa.UseAngularCliServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
}
});
}
Also, the app is configured to use HTTPS. Not sure why this doesn't just work, but hoping someone can help shed some light.
When you use .AddApiAuthorization() in IdentityServer, you have to pass a bearer token to the API when calling it or else you will get a 401 error. This means that you will not be able to call it directly through the browser because it does not automatically add the bearer token.
Check your IdentityServer logs to see why it is denying the request. Also, check your Angular app to make sure that it is adding the bearer token to the request headers.
I am currently working on an ASP.NET Web app with angular as a front end. As a base, the new template present in VisualStudio 2019 for ASP.NET angular, with Individual Authentication.
This runs on dotnet core 3.0 Preview 4.
First a user is created through the register interface of the template application. Then when a request is made to a controller of the backend, I would like to get the ApplicationUser that made the request.
Is that possible? Do I need to put any sort of token in the header of the http request in the frontend? Do I need to do anything special in the backend?
Currently, the http request in the controller looks like this.
import { Component, Inject } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Router } from "#angular/router";
import { error } from 'protractor';
#Component({
selector: 'app-classes-component',
templateUrl: './classes.component.html'
})
export class ClassesComponent {
public classes: Class[];
public http: HttpClient;
public baseUrl: string;
public courseCodeValue: string;
constructor(http: HttpClient, #Inject('BASE_URL') baseUrl: string, private router: Router) {
this.http = http;
this.baseUrl = baseUrl;
this.refreshCourses();
}
public refreshCourses() {
this.http.get<Class[]>(this.baseUrl + 'api/Courses/GetCourses').subscribe(result => {
this.classes = result;
}, error => console.error(error));
}
}
The Controller looks like this:
[Authorize]
[Route("api/[controller]")]
public class CoursesController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public CoursesController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[HttpGet("[action]")]
public IEnumerable<CourseDto> GetCourses()
{
var user = _userManager.GetUserAsync(User).Result;
// Here the user is null
return user.Courses.Select(item => new CourseDto
{
CourseCode = item.CourseCode,
CurrentGrade = item.CurrentGrade
});
}
}
The issue is that when I try to get the user that is making the http request with the usermanager, I get null. So I was wondering if I was missing something. Like prehaps some sort of token in the header of the request? Am I doing something wrong on the controller side?
EDIT: here is the Startup.cs code
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.AddMvc();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddMvc(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
// 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.UseDatabaseErrorPage();
}
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.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
You can use "User.Identity.Name" to get the UserId of the User making the request then pass it to FindByIdAsync().
var user = await _userManager.FindByIdAsync(User.Identity.Name);
or a db hit against the UserId(User.Identity.Name);
which ever works best for you.
Please let me know if this works.
I want to build an ASP.NET Service thats called from another server but it does not allow me to access the Service.
All you need is to insert this code in your startup.cs:
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
just keep in mind to use it above:
app.UseMvc()
Have fun!
You should take a look at this article to see all the available options:
simple setup
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
setup with middleware
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Shows UseCors with CorsPolicyBuilder.
app.UseCors(options => options.WithOrigins("http://example.com").AllowAnyMethod());
// other code may come here
}
CORS in MVC project
Action level
[EnableCors("AllowSpecificOrigin")]
public class HomeController : Controller
{
[EnableCors("AllowSpecificOrigin")]
public IActionResult Index()
{
return View();
}
}
Controller level
[EnableCors("AllowSpecificOrigin")]
public class HomeController : Controller
{
}
and much options like allowed origins, allowed HTTP methods, exposed response headers, how to handle credentials.
Try setting up yourself and come here with a specific problem if you encounter one.
Please make sure to install the nuget package
Install-Package Microsoft.AspNetCore.Cors
Configure the startup.cs similar to the following:
public class Startup
{
public Startup(IHostingEnvironment env)
{
//...
}
public void ConfigureServices(IServiceCollection services)
{
//...
//Register Cors policy
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddMvc();
//...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
app.UseCors("CorsPolicy"); //Make sure the policy name matches with the provided above
//...
}
}
You can configure CORS in Startup class in the ConfigureServices method `
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
}
apply it to every request
public void Configure(IApplicationBuilder app)
{
app.UseCors("MyPolicy");
}
you can apply on Controller or Method Also e.g add attribute
[EnableCors("MyPolicy")]