In my application I am restricting some view and the user has to be logged in to view them. One way would be to check on every action if the user is logged in or not. But after a bit of research I found that asp.net MVS supports some global filter rules.
How do we use them? Ideally I would want to call a filter onBeforeAction and check if the user is logged in or not..
Is this a right approach? If yes, then can any body give me an example?
The easiest way is to add the Authorize attribute to your controller or action methods. For example:
public class MyController : Controller
{
//Normal action
public ActionResult DoSomethingForAnyone() { }
//Secured action
[Authorize]
public ActionResult DoSomethingOnlyForAuthorisedUsers() { }
}
Alternatively you can secure the entire controller and exclude actions you want to be accessible to anonymous users:
[Authorize]
public class SecureController : Controller
{
public ActionResult DoSomething() { }
[AllowAnonymous]
public ActionResult DoSomethingForAnyone() { }
}
Your [Authorize] will not work with the custom login. If you are using Form Authentication or other Authentication method than [Authorize] will work smoothly.
For custom login on success set
FormsAuthentication.SetAuthCookie([user name], false);
This will make your [Authorize] attribute to work properly.
And for logout use below statement
FormsAuthentication.SignOut();
If you follow the above solution than it will reduce your code as well as valid user check on before Action call.
Related
I created an asp.net core web api project, using the .net5 version, and I have a route like this.
[Route("api/detail")]
public IEnumerable<User> Get()
{
//TODO
return users;
}
[Route("api/detail")]
public IEnumerable<User> Get(string name)
{
//TODO
return users;
}
Although my request method is the same and the request parameters are different, the 500 error will be reported in swagger. Is there any way to solve it? Any help is greatly appreciated.
There could be multiple reasons why you're getting a 500 error. When I pasted your code into a new controller the first is error I received was:
Ambiguous HTTP method for action... Actions require an explicit HttpMethod binding for Swagger
It's telling you that you need to decorate each action in the controller with an HttpMethod binding, like [HttpGet]. More on that in a second...
The next issue is that you're using [Route] to bind two different action methods to the exact same route with the same HttpMethod. That's not possible in an API controller.
Conflicting method/path combination... Actions require a unique
method/path combination for Swagger
My preferred method for routing is to use Attribute routing with Http verb attributes.
The first step would be to move the route attribute to the controller. I'm going to assume you've created a DetailsController:
[Route("api/[controller]")]
[ApiController]
public class DetailsController : ControllerBase { }
Now, update your actions. Remove the [Route] attribute, replace with the HttpGet attribute, and add the name parameter to your second endpoint. I also prefer to return an IActionResult:
[HttpGet]
public IActionResult Get()
{
//TODO
return Ok(users);
}
[HttpGet("{name}")]
public IActionResult Get(string name)
{
//TODO
return Ok(users);
}
Note that parameters are identified by using curly braces around the variable {name} in the Http method attribute. Both endpoints work and are accessible through swagger. I urge you to read the linked page above for a better understanding of the possible routing options (linked again).
Consider that I have .NET Controller with Policy-based authorization:
public class ImportantController: Controller {
[HttpGet]
[Authorize(Policy = "CanAccessVIPArea")]
public IActionResult ShowInformation() {
...
return OK(VipData);
}
[HttpPost]
[Authorize(Policy = "CanChangeVIPData")]
public IActionResult SaveInformation([FromBody] VipData) {
...
return CreatedAtAction(...);
}
}
Obviously, the real example is much more complex; I apologize if my simplification leads to too much make-believe in it. Also, real application is SPA with Angular front end; but I don't think it makes any difference for the purposes of this question.
When the user calls ShowInformation() I show a lot of data. On that page I have Save button that calls SaveInformation(). Authorization middleware checks for the right policy and it all works fine.
The problem is that by the time the user presses Save, she entered a lot of data, only to find out that she doesn't have the permissions to save. Obviously, leading to bad experience. I want to check for permissions on SaveInformation in the middleware that gets invoked when the user calls ShowInformation. I would prefer not to check for the hardcoded policy because it is on the server and it can change (we have pretty sophisticated permission management system that manipulates permissions at runtime). Invocation of SaveInformation is in the same Angular service as ShowInformation, and it is very easy to check...
I would like to invoke something like /api/SaveInformation?dryrun that will short-circuit the pipeline after authorization middleware with success or failure.
You can inject an IAuthorizationService and ask to evaluate a policy by name:
public class ImportantController: Controller
{
private readonly IAuthorizationService authorization;
public ImportantController(IAuthorizationService authorization)
{
this.authorization = authorization;
}
public async Task<IActionResult> ShowInformation()
{
// ...
var result = await authorizationService.AuthorizeAsync(User, "IsLucky");
return OK(VipData);
}
}
My pratice is to include all permission claims in the id token, when the user first login to the system, the id token will return to the client side. The client side then render the page according to the permission claims.
I've got two actions in my ASP.NET Core Web API application's controller:
[HttpGet("get-all")]
public IActionResult GetAll() { ... }
and
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
...
return RedirectToAction("GetAll");
}
Delete action always redirects to itself and never to GetAll. Why so? In the same time similar redirect from Post action works ok.
Can't find any docs on the subject. Any help?
Have you tried to use RedirectToActionResult? Like this (change ControllerName with your actual Controller's name ):
RedirectToActionResult("GetAll", "ControllerName", null);
Documentation
Hope you'll find this useful.
I have a startup class with following code
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType="ApplicationCookie",
LoginPath = new Microsoft.Owin.PathString("/auth/login")
});
}
}
When I run my project showing me
The webpage at
/auth/login?ReturnUrl=%2Fauth%2Flogin%3FReturnUrl%3D%252Fauth%252Flogin%253FReturnUrl%253D%25252Fauth%25252Flogin%25253FReturnUrl%25253D%2525252Fauth%2525252Flogin%2525253FReturnUrl%2525253D%252525252Fauth%252525252Flogin%252525253FReturnUrl%252525253D%25252525252Fauth%25252525252Flogin%25252525253FReturnUrl%25252525253D%2525252525252Fauth%2525252525252Flogin%2525252525253FReturnUrl%2525252525253D%252525252525252Fauth%252525252525252Flogin%252525252525253FReturnUrl%252525252525253D%25252525252525252Fauth%25252525252525252Flogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252Fauth%2525252525252525252Flogin%2525252525252525253FReturnUrl%2525252525252525253D%252525252525252525252Fauth%252525252525252525252Flogin%252525252525252525253FReturnUrl%252525252525252525253D%25252525252525252525252Fauth%25252525252525252525252Flogin%25252525252525252525253FReturnUrl%25252525252525252525253D%2525252525252525252525252Fauth%2525252525252525252525252Flogin%2525252525252525252525253FReturnUrl%2525252525252525252525253D%252525252525252525252525252Fauth%252525252525252525252525252Flogin%252525252525252525252525253FReturnUrl%252525252525252525252525253D%25252525252525252525252525252Fauth%25252525252525252525252525252Flogin%25252525252525252525252525253FReturnUrl%25252525252525252525252525253D%2525252525252525252525252525252Fauth%2525252525252525252525252525252Flogin%2525252525252525252525252525253FReturnUrl%2525252525252525252525252525253D%252525252525252525252525252525252Fauth%252525252525252525252525252525252Flogin%252525252525252525252525252525253FReturnUrl%252525252525252525252525252525253D%25252525252525252525252525252525252Fauth%25252525252525252525252525252525252Flogin%25252525252525252525252525252525253FReturnUrl%25252525252525252525252525252525253D%2525252525252525252525252525252525252Fauth%2525252525252525252525252525252525252Flogin%2525252525252525252525252525252525253FReturnUrl%2525252525252525252525252525252525253D%252525252525252525252525252525252525252Fauth%252525252525252525252525252525252525252Flogin%252525252525252525252525252525252525253FReturnUrl%252525252525252525252525252525252525253D%25252525252525252525252525252525252525252F
has resulted in too many redirects. Clearing your cookies for this
site or allowing third-party cookies may fix the problem. If not, it
is possibly a server configuration issue and not a problem with your
computer.
I cleared my cookie but still unchanged.
I agree with #StephenMuecke this is from a
endless redirect loop.
If the page you are trying to land need authentication to view, by either having [Authorize]
on the controller class or :
[Authorize]
public class AccountController:Controller
{
or at the controller ActionResult:
[Authorize]
public ActionResult Index
This will redirect the user to the login page.
If this does not allow anonymous authenitcation, it is immpossible for a user to land on the page
without being logged in.
[Authorize]
public ActionResult Login(string message, string returnUrl)
{
Hence the app keeps redirecting the user to the login page continously until there is
some type of overflow.
You need to use this:
[AllowAnonymous]
public ActionResult Login(string message, string returnUrl)
{
ReturnUrl=%2Fauth%2Flogin%3FReturnUrl%3D%252Fauth%252Flogin%253FReturnUrl%253D%25252Fauth%
The return url requires authentication, so login, which requires authentication.. and so on.
This same prinicple works throughout your entire project.
I've made my own authorize attribute, and this is what it looks like
public class RedirectAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "NotExist" }));
}
}
}
So if the user isn't authenticated, I want them to get redirected to the NotExist controller. I've debugged and it seems that unauthorized users get in the if clause, which is correct. But I've also tried doing this with logged in users, and they get in the if clause as well which is wrong.
I dont understand why this is happening. It makes me hesitate about whether my log-in didnt work. Is this the right way of logging a user in?
FormsAuthentication.SetAuthCookie(acc.username, false);
I've never made a log-in system in asp.net mvc before, so please tell me what I'm doing wrong.
Edit:
It seems that the default [Authorized] attribute isn't working either... I really think the problem lays in the log in:
[HttpPost]
public ActionResult Login(User acc)
{
if(ModelState.IsValid)
{
if (Validate(acc.username, acc.password))
{
FormsAuthentication.SetAuthCookie(acc.username, false);
return RedirectToAction("Index", "System");
}
}
ModelState.AddModelError("IncorrectDetails", "Wrong details. Please try again.");
return View(acc);
}
The custom authorize attribute looks correct.
Since you are setting the cookie yourself I would guess you are not using the built-in membership provider.
If you set the cookie yourself, you also need to read the auth cookie and set the Identity and Principal objects on each request. Otherwise, HttpContext.User.Identity.IsAuthenticated will always be false, which seems to be what you are experiencing.