AllowAnonymous attribute in SignalR - asp.net-mvc

[Authorize] // Derived from Microsoft.AspNet.SignalR.Authorize
public class ChatHub : Hub
{
[System.Web.Mvc.AllowAnonymous] // [AllowAnonymous] doesn't be supported here
public async Task GetEmails()
{
using (var db = new MyDbContext())
{
var users = await db.Users.ToListAsync();
var emails = new List<string>();
users.ForEach(x => emails.Add(x.Email));
Clients.All.getEmails(JsonConvert.SerializeObject(emails));
}
}
}
My problem: I'd set breakpoint to GetEmails method but it's never hit to. Then, I signed in and tried again, it's working.
That means: [System.Web.Mvc.AllowAnonymous] doen't work within [Microsoft.AspNet.SignalR.Authorize].
So, my question is: Why doesn't namespace Microsoft.AspNet.SignalR support [AllowAnonymous] attribute? And do I need to declare AllowAnonymous attribute for that?

I've solved the problem: Using [System.Web.Mvc.Authorize] instead of [Authorize]. Like this:
[System.Web.Mvc.Authorize]
public class ChatHub : Hub
{
[System.Web.Mvc.AllowAnonymous]
public async Task GetEmails()
{
// ...
}
}
I don't understand why but it should work perfectly :)

Related

How to inject service (AuthenticationStateProvider) in Blazor class

I'm struggling to inject a service (AuthenticationStateProvider) in a class in Blazor server. If I do it in a razor component, it is pretty simple:
#inject AuthenticationStateProvider AuthenticationStateProvider
and then
private async Task LogUsername()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
ClientMachineName = $"{user.Identity.Name}";
}
else
{
ClientMachineName = "Unknown";
}
}
However I need to do this, i.e. retrieve the authenticated user machine name, in a class instead of a razor component.
I tried for instance:
[Inject]
AuthenticationStateProvider AuthenticationStateProvider { get; set; }
public async Task LogUsername()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
ClientMachineName = $"{user.Identity.Name}";
}
else
{
ClientMachineName = "Unknown";
}
}
But this does not seem to work.
Any help would be much appreciated.
with Blazor server (.Net Core 3), this worked for me:
public class AuthTest
{
private readonly AuthenticationStateProvider _authenticationStateProvider;
public AuthTest(AuthenticationStateProvider authenticationStateProvider)
{
_authenticationStateProvider = authenticationStateProvider;
}
public async Task<IIdentity> GetIdentity()
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
return user.Identity;
}
}
You need to register this with the ASP.Net Core DI in Startup.ConfigureServices:
services.AddScoped<AuthTest>();
And then inject it on your .razor page:
#page "/AuthTest"
#inject AuthTest authTest;
<button #onclick="#LogUsername">Write user info to console</button>
#code{
private async Task LogUsername()
{
var identity= await authTest.IsAuthenticated();
Console.WriteLine(identity.Name);
}
You should see the logged-in username written to the ASP.Net output console.
Update
If you want to get the currently logged in user from within a separate class and you're not injecting that onto a blazor page, then follow the guidance here
Thanks again both #StephenByrne and #Dan - I'm almost there now with my requirements. This is my user service class and it works as expected:
public class AuthUser
{
private readonly AuthenticationStateProvider _authenticationStateProvider;
public AuthUser(AuthenticationStateProvider authenticationStateProvider)
{
_authenticationStateProvider = authenticationStateProvider;
var username = _authenticationStateProvider.GetAuthenticationStateAsync().Result;
FetchMyUser(username.User.Identity.Name);
}
public User MyUser { get; set; }
public void FetchMyUser(string machineName = "Unknown")
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(SettingsService.DBConnectionString2016))
{
MyUser = connection.QueryFirstOrDefault<User>($"SELECT FirstName FROM MyTable WHERE MachineName = '{machineName}' ;");
}
}
}
And then in Startup.cs I add this service as Scoped (this is important, as Dan pointed out below);
services.AddScoped<AuthUser>();
I can then use this service from a .razor component as follows:
#inject AuthUser authUser
Hello #authUser.MyUser.FirstName
The only remaining issue I have is that I don't know how to consume this service in another .cs class. I believe I should not simply create an object of that class (to which I would need to pass the authenticationStateProvider parameter) - that doesn't make much sense. Any idea how I could achive the same as I mentioned in the .razor file but in a .cs class instead ?
Thanks!
Check out the solution I had to this problem here:
Accessinging an authenticated user outside of a view in Blazor
This should solve your problem.
Edit: If you would like to get the information about the authentication state, what you should do is create a claim on the authentication state with the username or whatever detail you require in it, instead of creating a class and assigning the name to that. That way, in classes that need this information you can just inject a service class that gets all of the claims on the current authentication state. This really should all be done in a custom authentication state provider.
Example:
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
MyUser = //DB call to get user information
var claimsIdentity = new ClaimsIdentity(new[] { new
Claim(ClaimTypes.Name, MyUser.Name) }, "Authenticated");
var user = new ClaimsPrincipal(identity);
return new AuthenticationState(user);
}
Then in another service you would get the claims with the user information in it and inject that into any other service/class the information is needed.
public ApplicationUser(AuthenticationStateProvider authenticationStateProvider)
{
_authenticationStateProvider = authenticationStateProvider;
}
public async Task<string> GetLogin()
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
return authState.User.Claims.Where(c => c.Type == ClaimTypes.Name).FirstOrDefault().Value;
}
if you in your startup.cs add some services
services.AddScoped<TokenProvider>();
services.AddTransient<TokenRefreshService>();
services.Add<GraphServiceService>();
you can in a razor page inject them by their type
#inject TokenProvider _token
#inject TokenRefreshService _tokenrefresh
#inject GraphServiceService _graphservice
These service classes, you inject them in throught the constructor
public GraphServiceClass(AuthenticationStateProvider _AuthenticationStateProvider, TokenProvider _token)
{
AuthenticationStateProvider = _AuthenticationStateProvider;
token = _token;
}
I recommend this: ASP.NET Core Blazor dependency injection

asp.net core validation after filters

I want to run some custom logic for all APIs (asp.net core) that we have in our service before model validation but after model binding. Is this possible? I tried an ActionFilter but it gets called after validation. Resource filter also does not work for us. Appreciate your help.
Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. In that case, an automatic HTTP 400 response containing issue details is returned when model state is invalid.
One way to achieve what you want is to suppress this behavior.
Add the following code to ConfigureServices:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
Then you can add your code to the filter - eg:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
if(context.ActionArguments != null && context.ActionArguments.Count > 0)
{
//WARNING - you should add "safe" code to access the dictionary
//I have hardcoded the parameter name (data) here for sample only.
var model = context.ActionArguments["data"];
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
}
of course you need to apply the filter as well - in the example case below, I have applied it globally. You can be more specific if you want.
services.AddMvc(
options => options.Filters.Add(new SampleActionFilter())
).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
In your controller code, you can also further use the TryValidateModel method if you want, like so:
[Route("api/[controller]")]
[ApiController]
public class ProcessController : ControllerBase
{
[HttpPost]
public IActionResult Contact(FormDataModel data)
{
bool validated = TryValidateModel(data);
if (!ModelState.IsValid)
{
ModelState.AddModelError("", "Id cannot be empty..");
return Ok();
}
return Ok();
}
}
Hope this helps to solve your problem.

How to invalidate Web API cache from another Controller(ASP.NET Web API CacheOutput Library)

I have used ASP.NET Web API CacheOutput Library for my asp.net project for web API and it working fine, but have another controller from where I have a POST method and I would like to invalidate my cache from that controller.
[AutoInvalidateCacheOutput]
public class EmployeeApiController : ApiController
{
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100)]
public IEnumerable<DropDown> GetData()
{
//Code here
}
}
public class EmployeesController : BaseController
{
[HttpPost]
public ActionResult CreateEmployee (EmployeeEntity empInfo)
{
//Code Here
}
}
I would like to invalidate Employees Cache when there is add\update in employee controller.
It is little tricky, but you can get it in this way:
1. On your WebApiConfig:
// Registering the IApiOutputCache.
var cacheConfig = config.CacheOutputConfiguration();
cacheConfig.RegisterCacheOutputProvider(() => new MemoryCacheDefault());
We will need of it to get the IApiOutputCache from GlobalConfiguration.Configuration.Properties, if we let the default properties' setup happen the property with the IApiOutputCache won't exists on MVC BaseController request.
2. Create a WebApiCacheHelper class:
using System;
using System.Linq.Expressions;
using System.Web.Http;
using WebApi.OutputCache.Core.Cache;
using WebApi.OutputCache.V2;
namespace MideaCarrier.Bss.WebApi.Controllers
{
public static class WebApiCacheHelper
{
public static void InvalidateCache<T, U>(Expression<Func<T, U>> expression)
{
var config = GlobalConfiguration.Configuration;
// Gets the cache key.
var outputConfig = config.CacheOutputConfiguration();
var cacheKey = outputConfig.MakeBaseCachekey(expression);
// Remove from cache.
var cache = (config.Properties[typeof(IApiOutputCache)] as Func<IApiOutputCache>)();
cache.RemoveStartsWith(cacheKey);
}
}
}
3. Then, call it from your EmployeesController.CreateEmployee action:
public class EmployeesController : BaseController
{
[HttpPost]
public ActionResult CreateEmployee (EmployeeEntity empInfo)
{
// your action code Here.
WebApiCacheHelper.InvalidateCache((EmployeeApiController t) => t.GetData());
}
}

How to test that an MVC action is only accessible via HTTP POST?

I am using MvcContrib-TestHelper to test the routing on my app. I have an action which is restricted to HTTP POST only:
public TestController
{
[HttpPost]
public ActionResult Example()
{
return View();
}
}
And here is an example of a test that should fail:
[TestFixture]
public class RoutingTests
{
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
RouteTable.Routes.Clear();
Application.RegisterRoutes(RouteTable.Routes);
}
[Test]
public void TestWithGet()
{
var route = "~/Test/Example".WithMethod(HttpVerbs.Get);
route.ShouldMapTo(r => r.Example());
}
}
However, the test passes! I've seen one other unanswered question (sorry, wrong link) where this was also raised, and it seems like the functionality is broken. What's a better way to test that this route is accessible via POST only?
It looks like you are just trying to test the ASP.NET MVC framework there. I dont think that such test will bring value...
use this code:
var controller = new HomeController();
var methodInfo = controller.GetType().GetMethod("MrthodName");
var attributes = methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true).Cast<ActionMethodSelectorAttribute>().ToList();
attributes - this is list accept verbs

I want to use session in the asp.net mvc controller constructor

I'm new to Mvc.
Sorry to my english. ^^
I have some question about asp.net MVC session in the controller.
The Scenario things that I want to do is like follows..
First of all, My development circumstance is entityframework and mvc3.
When Someone logged in each one has different database. So, Each has connect different database.
So, Each person has his own session value which is database connection string. So far so good.
I have simple database Repository and at the each repository's constructor can change database connection.
At controller which calls Repository class, I need session value. But As I know Controller's construction can't keep session value. right?
I want your good advice. Thanks in advance.
Code samples are below:
public class MasterRepository
{
DBEntities _db;
public MasterRepository(string con)
{
_db = new DBEntities(con);
}
}
public class TestController : Controller
{
private string con;
MasterRepository _db;
public TestController()
{
_db = new MasterRepository(Session["conn"].ToString()); // Session is null I want to solve this Part...
}
public ActionResult Index()
{
string con = Session["conn"].ToString(); // Session is assigned.
return View();
}
}
These should explain what's happening to cause Session to be null, and give you a few possible solution options:
Is ASP.NET MVC Session available at any point durign controller construction
Why my session variables are not available at construction of a Controller?
Session null in ASP.Net MVC Controller Constructors
I think you have missed out the "service" part of the controller - service - repository pattern:
http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx
But when you go down this path you will probably also need to learn IoC as well.
Then your code would look more like:
public class MasterRepository
{
public Foo GetAllFoo()
{
return ObjectContextManager.GetObjectContext().AsQueryable().ToList();
}
}
public class MasterService
{
MasterRepository _repository;
public MasterService(MasterRepository repository) // use IoC
{
_repository = repository;
}
public Foo GetAllFoo()
{
return _repository.GetAllFoo();
}
}
public class TestController : Controller
{
MasterService _service;
public TestController(MasterService service) // use IoC
{
_service = service;
}
public ActionResult Index()
{
var model _service.GetAllFoo();
return View(model);
}
}

Resources