I know that there is an attribute when a user must be authorize or not. You can also place [AllowAnonymous] above it. See also code below:
[Authorize] // only when the user is authorize
public class AccountController : Controller
{
[HttpGet]
[AllowAnonymous] // also when the user in not authorize.
public ActionResult Login(string returnUrl = "")
{ /* Some code */ }
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{ /* Some code */ }
}
But is there also an attribute for allow anonymous only. For example: a login page only show when the user is not authorize?
I don't think there's an existing one, but there's no reason you can't roll your own. However, I would point out that it seems odd that you'd go to the extra effort to restrict content to an authenticated user:
public class AnonymousOnly : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// Do what you want to do here, e.g. show a 404 or redirect
}
}
}
Then just decorate your class/method with this new attribute:
[AnonymousOnly]
public ActionResult Login(string returnUrl = "")
{
// code
}
Related
SO. I have an Action method in a controller as here below:
[Authorize]
public ActionResult ToDo()
{
//Do some stuff
return View();
}
I would like to allow facebook crawler access the content of the razor view for ToDo action method. How is that possible? Your thoughts.
create two action with same name but overload that for example
[Authorize]
[Route("Home/Index/{status:boolean}")]
Public ActionResult Index(bool status)
{
}
[Route("Home/Index/{id:int}")]
Public ActionResult Index(int id)
{
}
and you can create customeActionInvoker
I've a admin dashboard project that need global authorization and I set it up on
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
}
That code make all my controller is being authorize.. And there is a controller that has [AllowAnonymous] attribute.. However I've sudden change request that an action on this controller need to be authorize..
[AllowAnonymous]
public class AuthController : Controller
{
[Authorize(Roles = "Admin")]
public ActionResult BumbaSection()
{
return View();
}
}
This is not working, I still can access this BumbaSection action.. Any idea?
Thanks
After I peek the authorize code, This part of code make the authorize not working :
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
//code here
if (filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true))
return;
//code here
}
Override this piece of code in authorize attribute class and my code is start working.. Maybe this will usefull for who that have some problem with me
Implementing a basic authorization and authentication layer is quite easy with ASP.NET MVC 4; it's all automatically generated with the 'ASP.NET MVC 4 Web Application'-project template.
However, I'm tasked with implementing some controller actions that require re-authentication and I'm aiming for a maintainable solution. Simply put in a user story, I'm trying to implement the following:
User logs on;
User navigates to a controller (attributed with [Authorize]) action which renders a form view;
User performs a POST by submitting the form;
An authentication form appears in which the user needs to re-authenticate using his/her username and password;
If authentication is succesfull, proceed with handling the POST-request.
Note that 'reauthentication' does not have to alter the state of the current user session.
Obviously, there are many ways to implementing this, but I feel like an implementation which looks similiar to the following (pseudo) sample would suit my needs.
[Authorize]
[InitializeSimpleMembership]
public class SpecialActionController : Controller
{
public ActionResult SpecialForm() { return View(); }
public ActionResult Succes() { return View(); }
[HttpPost]
[ReAuthenticate] /* <- Prompts user with reauthentication form before proceeding. */
public ActionResult SpecialForm(SpecialFormModel model)
{
if (ModelState.IsValid)
RedirectToAction("Succes");
else
return View(model);
}
}
Any suggestions?
Edit: I forgot to mention that any OAuth-related features are out of scope. External authentication is not an issue here and does not require support. In fact, with the current project I'm working on, all OAuth-related features are either removed or deactivated.
You should be able to do this using a combination of a custom AuthorizeAttribute and the Session. Override the AuthorizeCore method and let all the default authentication take place but introduce your own extra check (for re-authentication) e.g.
public class RecurringAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var reauth = (bool?)httpContext.Session["ReAuthenticated"];
var result = base.AuthorizeCore(httpContext) && (reauth ?? false);
httpContext.Session["ReAuthenticated"] = !result;
return result;
}
}
This should re-direct the user to the login page everytime they hit the action and they haven't re-authenticated. If the user has re-authenticated, we clear the session variable to force a login on the next request.
For this to work correctly, we need a hook to set the ReAuthentication session variable - I think the LogOn method in the AccountController would be the ideal place for this
public class AccountController : Controller
{
...
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
Session["ReAuthenticated"] = User.Identity.IsAuthenticated;
return RedirectToLocal(returnUrl);
}
...
}
}
Then all that's left to do is decorate our controller actions
[Authorize]
public ActionResult SomePrivateAction()
{
...
}
[RecurringAuthorize]
public ActionResult SomeSuperSecretAction()
{
...
}
You should find authorization will work as normal for any actions using the default AuthorizeAttribute and any actions decorated with the RecurringAuthorizeAttribute will be forced to login everytime they request the page, which includes page refreshes.
I tried to implement the hypothetical [ReAuthenticate]-attribute, but found myself relying on reflection too much. After putting some thought into a more manageable solution, I finally came up with the following:
ReAuth class
public sealed class ReAuth
{
#region Constructor
private ReAuth(Func<System.Web.Mvc.ActionResult> onSuccessAction)
{
this.onSuccessAction = onSuccessAction;
}
#endregion
#region Public static members
public static ReAuth CreateFor(HttpSessionStateBase httpSession, Func<System.Web.Mvc.ActionResult> onSuccessAction)
{
httpSession[sessionKey] = new ReAuth(onSuccessAction);
return GetFor(httpSession);
}
public static ReAuth GetFor(HttpSessionStateBase httpSession)
{
return httpSession[sessionKey] as ReAuth;
}
public static bool ExistsFor(HttpSessionStateBase httpSession)
{
return httpSession[sessionKey] as ReAuth != null;
}
#endregion
#region Public instance members
public bool ReAuthenticated { get; set; }
public System.Web.Mvc.ActionResult Handle()
{
if (ReAuthenticated)
return onSuccessAction();
else
return new System.Web.Mvc.RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary
{
{ "Controller", "#" }, /* Replace '#' with the name of the controller that implements the re-authentication form... */
{ "Action", "#" } /* Replace '#' with the name of the action on the aforementioned controller. */
});
}
#endregion
#region Private members
private const string sessionKey = "reAuthenticationSessionKey";
private readonly Func<System.Web.Mvc.ActionResult> onSuccessAction;
#endregion
}
Implementation
Suppose we have a hypothetical controller where the solution is applied:
public class AccountInfoController : System.Web.Mvc.Controller
{
/* snip... */
[HttpPost]
public ActionResult EditAccountInfo(AccountInfo model)
{
if (ModelState.IsValid)
return ReAuth.CreateFor(Session, () => { return Success(); }).Handle();
else
return View(model);
}
}
...and, we need a controller (essentially, a 'dumb' copy of the real AccountController that does not tamper with the Forms Authentication User Session state) in which the re-authentication takes place.:
public class ReAuthController : System.Web.Mvc.Controller
{
/* Snip... */
[HttpPost]
public ActionResult LogOn(LogOnModel model)
{
if (ModelState.IsValid)
{
ReAuth.GetFor(Session).ReAuthenticated = Membership.ValidateUser(model.User, model.Password);
return ReAuth.Handle();
}
return View(model);
}
}
As far as I know, this is a manageable solution. It does rely a lot on storing objects into session state. (Especially the object state of the controller which implements the ReAuth-class) If anyone has additional suggestions, please let me know!
I want to centralize authentication that developers are free to worry about the implementation of security.
I dont want to validate the authentication at each request like
public class HomeController : BaseController
{
public ActionResult Home()
{
if (Request.IsAuthenticated == true)
{
return RedirectToAction("Home", "Member");
}
return View();
}
public ActionResult Resources()
{
if (Request.IsAuthenticated == true)
{
return RedirectToAction("Resources", "Member");
}
return View();
}
}
Thanks
You can centralized your credential validation by using a BaseController for all your controller inside your application
public class BaseSupplierFlyersController : BaseController
{
//
// GET: /SupplierFlyers/
public ActionResult Index(string culture)
{
//Some action logic here
return View("Index");
}
}
In your BaseControler
public class BaseController : Controller
{
private bool IsLoged = false;
public BaseController()
: base()
{
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!IsLoged)
{
//Redirection du bad login.
//Affect the Result to cancelled previous Action execution
filterContext.Result = new RedirectResult(string.Concat("~/", ControllerConstants.Error,"/", ActionConstants.BadLogin));
}
base.OnActionExecuting(filterContext);
}
protected override void Execute(RequestContext requestContext)
{
//Validate Credential
ValidateCredential(requestContext);
//Traitement standard
base.Execute(requestContext);
}
private void ValidateCredential(RequestContext requestContext)
{
//Logic to validate credential here
IsLoged = true; //or false
}
}
Each time an Action from a Controller is fired the BaseController will validate credential in Execute method and in OnActionExecuting you can valide if the credential are valid and than take an Action like Redirect to an other page.
Looking at the code you wrote it seems like you are missing Action Filters that are part of asp.net mvc. There is one built in called Authorize that basically requires a request to be authenticated before the controller method is invoked, but creating your own is very simple.
Also in asp.net mvc 3 you can define global filters which will be part of every action invoked on your controllers. You can find some samples here
I have a controller decorated with an AuthorizeAttribute. The controller contains several actions that all require authentication apart from one action that requires some custom authentication provided by CustomAuthorizeAttribute.
My question is once I've added [Authorize] at the controller level can I override it (or remove it) with [CustomAuthorize] on just one action? Or do I have to remove [Authorize] from the controller level and add it individually to every other action?
I'm asking purely for convenience because I'm lazy and don't want to decorate every action with the AuthorizeAttribute.
[Authorize]
public class MyController : Controller {
//requires authentication
public ViewResult Admin() {
return View();
}
//... a lot more actions requiring authentication
//requires custom authentication
[CustomAuthorize] //never invoked as already failed at controller level
public ViewResult Home() {
return View();
}
}
In MVC 5 you can override the authorization for any action using the new attribute OverrideAuthorization. Basically, you add it to an action that has a different authorization configuration than the one defined in the controller.
You do it like this:
[OverrideAuthorization]
[Authorize(Roles = "Employee")]
public ActionResult List() { ... }
More information at http://www.c-sharpcorner.com/UploadFile/ff2f08/filter-overrides-in-Asp-Net-mvc-5/
In ASP.NET Core 2.1 there's no OverrideAuthorization attribute and the only thing you can do is make an action anonymous, even if the controller is not. More information at https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1
One option is to do it this way:
[Authorize(Roles = "Admin,Employee")] // admin or employee
public class XController : Controller
{
[Authorize(Roles = "Admin")] // only admin
public ActionResult ActionX() { ... }
[AllowAnonymous] // anyone
public ActionResult ActionX() { ... }
}
You can change the Order in which the attributes run (using the Order property), but I believe that in this case they will still both run unless one generates a result with immediate effect. The key is to have the least restrictive attribute applied at the highest level (class) and get more restrictive for the methods. If you wanted the Home action to be publicly available, for instance, you would need to remove the Authorize attribute from the class, and apply it to each of the other methods.
If the action has the same level of permissiveness, but has a different result, changing the order may be sufficient. For example, you would normally redirect to the Logon action, but for Home you want to redirect to the About action. In this, case give the class attribute Order=2 and the Home action attribute Order=1.
After way too much time, I came up with a solution. You need to decorate your controller with a custom AuthorizeAttribute.
public class OverridableAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var action = filterContext.ActionDescriptor;
if(action.IsDefined(typeof(IgnoreAuthorization), true)) return;
var controller = action.ControllerDescriptor;
if(controller.IsDefined(typeof(IgnoreAuthorization), true)) return;
base.OnAuthorization(filterContext);
}
}
Which can be paired with AllowAnonymous on an Action
[AllowAnonymous]
All you need to override the [Authorize] from the controller, for a specific action is to add
[AllowAnonymous]
to the action you want to not be authorized (then add your custom attribute as required).
See the comments / intellisense :
Represents an attribute that marks controllers and actions to skip the
System.Web.Mvc.AuthorizeAttribute during authorization.
Full Example
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Website
{
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (true)//Perform own authorization logic
return; //simply return if request is authorized
context.Result = new UnauthorizedResult();
return; //this is not authorized
}
}
[Authorize]
public class WebsiteController : Controller
{
[HttpGet]
[AllowAnonymous]//When this is added our Custom Attribute is hit, without it our attribute is not used as request already gets 401 from controller's Authorize
[CustomAuthorize]
public IActionResult Index()
{
return View(new ViewModel());
}
}
Note
This approach will not work if you want to use the standard [Authorize] attribute on your action, with a custom policy e.g.
[Authorize]
public class WebsiteController : Controller
{
[HttpGet]
[AllowAnonymous]
[Authorize("CustomPolicyName")] //Will not be run
public IActionResult Index()
{
return View(new ViewModel());
}
}
services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry", policy =>
policy.RequireAssertion(context =>
false //Custom logic here
));
});
...but if like the OP you want a Custom Attribute then you are good to go with my solution.
Override for all controllers when handling prototype and production environment.
So there is no need to remove the authorize of each controller.
app.UseEndpoints(endpoint =>
{
endpoint.MapControllers().WithMetadata(new AllowAnonymousAttribute());
});