I have 3 roles: Registered Users, Approved Users, and Admins.
Access to the site is only available to Approved users and Admins.
To restrict anonymous access I've added a filter in FilterConfig as follows:
filters.Add(new System.Web.Mvc.AuthorizeAttribute());
Now, for registered users I want them to redirect to a landing page saying:
Please contact one of the administrators to approve you.
I'm not really sure what's the correct way to do that.
I can setup authorize attribute on each of the controllers, but I'm not sure if that's a good way.
Also, I'm not sure where I should specify default redirect action based on the role.
I know that I can specify default redirect action in RouteConfig.cs but not sure where to specify a role.
StanK is right that having [Authorize] attribute will redirect all users who are not logged-in to the login page. That's half of your dillema.
From there you need to alter your logon method to check if a newly logged-in user has the right role (e.g. ConfirmedUser). This is tricky because User.IsInRole("ConfirmedUser") will always be false in your logon method. This is because the User object is populated by the http object, which will not be re-populated until the next re-cycle. Luckily, you can use the Roles.IsUserInRole(userName, "ConfirmedUser") to check if the user has the right role.
So, within your logon method, after authenticating user, log the user out and re-direct them to an [AllowAnonymous] method which informs them that they are not yet confirmed.
if (Roles.IsUserInRole(userName, "ConfirmedUser")
{
FormsAuthentication.SignOut();
return RedirectToAction("WarningMsg", "Home");
}
You should be able to use the [Authorize] attributes for this.
Restricted pages will have their controller or action decorated with [Authorize(Roles="Approved User,Admin")], the 'landing page' for registered users would be [Authorize(Roles="Registered User,Approved User,Admin")] and the Logon action would have [AllowAnonymous].
If the user is not authorised, they would be re-directed to Account/Login. You would need to build some logic in this action that redirects "Registered Users" who are already logged in to your landing page. Others should just see the standard login page.
EDIT
The logic to redirect "Registered Users" from the login page to the landing page would look something like this
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
if (User.Identity.IsAuthenticated && Roles.IsUserInRole("Registered User"))
return RedirectToAction("LandingPage");
ViewBag.ReturnUrl = returnUrl;
return View();
}
Related
When I decorate a method with an Authorize roles attribute it returns false everytime. I'm trying to limit access to an admin page for users in the "Admin" role only.
I have verified that the user im currently logged in as is in fact in the "Admin" role.
I have tried to use a custom authorization attribute. Same result. I can add the code if needed.
I have found that the authorization attribute works for Users but not for Roles.
I believe this problem is somehow tied into the fact that the following does not work in my application:
User.IsInRole("Admin").
However, this statement does work:
userManager.IsInRole(user.Id, "Admin")
Here is my code:
public class AdminController : Controller
{
//[AuthLog(Roles = "Admin")] //Custom authorization attribute
[Authorize(Roles = "Admin")]
public ActionResult Users()
{
return View();
}
}
Maybe this can help with debugging:
Microsoft.AspNet.Identity.Core: V.2.1.0
Microsoft.AspNet.Identity.EntityFramework: V.2.1.0
I am open to suggestions on anything else I can post from my project in order to debug easier. I have scoured the stack for 2 weeks now.
Update 1: How user is logged in
// POST: /account/login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(AccountLoginModel viewModel)
{
// Ensure we have a valid viewModel to work with
if (!ModelState.IsValid)
return View(viewModel);
// Verify if a user exists with the provided identity information
var user = await _manager.FindByEmailAsync(viewModel.Email);
// If a user was found
if (user != null)
{
// Then create an identity for it and sign it in
await SignInAsync(user, viewModel.RememberMe);
// If the user came from a specific page, redirect back to it
return RedirectToLocal(viewModel.ReturnUrl);
}
// No existing user was found that matched the given criteria
ModelState.AddModelError("", "Invalid username or password.");
// If we got this far, something failed, redisplay form
return View(viewModel);
}
private async Task SignInAsync(IdentityUser user, bool isPersistent)
{
// Clear any lingering authencation data
FormsAuthentication.SignOut();
// Create a claims based identity for the current user
var identity = await _manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Write the authentication cookie
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
}
The
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
unfortunately doesn't store any roles with the identity. Thus, when the identity is recreated from the cookie, you have no roles. To verify try
this.User.IsInRole("Admin")
and you'll get false, even though the userManager tells you otherwise.
There are multiple workarounds.
You could for example switch to any other identity persistor, like the SessionAuthenticationModule which could store your username and roles in the cookie. You could follow my tutorial on that.
Another approach would be to have an explicit role manager and use its feature that automatically causes your roles to be stored in another cookie, separate from the forms authentication cookie. This involves configuring the role provider and writing your own role provider that would be an adapter over the user manager.
Finally, you could forget forms authentication and use Identity's native way of issuing cookies, which would involve calling SignInAsync on the authentication manager.
I am struggling over this issue since yesterday.I am working on a web application which includes email service.Email includes 'link' to a certain page of that application.
Now:I have 2 scenarios:
1) If the user is logged in(in the application) already and he hit
the url he got in his email,the link will open perfectly.
2) If the user is not logged in(in the application) then the url
will not open and will redirect itself to the login page with the
functionality added in the BaseController.
*
Now what I want is when the user logs in after hitting the url and on
successfully login the user gets redirect to the link provided in the
Email.
*
for eg: If the user gets an email to the detail page of an employee,So on successfully login the user gets redirect to the Detail page of the employee.
Something like appending the redirecturl to the login page.
I think rewriting url is what I should be doing.
But I dont know how can I use that in this case.Help me out.
The default project template that comes with ASP.NET MVC 5 behaves exactly as you describe.
If you want to redirect to a custom login URL, reconfigure the LoginPath property of the CookieAuthenticationOptions object
LoginPath = new PathString("/Account/Login")
In the default template this is done in the Startup.Auth.cs class.
NOTE: If you are using an old version of ASP.NET MVC, the default project template behaved in the same way. But previously this was implemented using Forms Authentication, so in order to redirect to a custom login URL you would then have to set the loginUrl attribute of the <forms> tag in the Web.config file
By default if a user tries to access the authorized page when he is not authorized the automatically gets redirected to the log in page or the page which is configured in web.config file for the element. And you can see the query string returnUrl having the url that was tried to access initially get appended to the log in url.
To access the return url, include a new parameter as returnUrl and maintain the return url in a hidden field by model data to access on post back for redirection.
If the user is authenticated then on post back then redirect the user to the specified page what he intended to go for.
I don't remember exactly but few month ago I implemented similar functionality and i had to save returnUrl explicitly (due to MVC bug or something) - Refer this link
AccountController.cs - Snapshot
[HttpGet]
[AllowAnonymous]
public ActionResult Login(string returnUrl, string userName)
{
// You login method logic....
// Add this line to save the returnUrl value
ViewBag.ReturnUrl = returnUrl;
}
Login.cshtml - Snapshot
#using (Html.BeginForm("Login", "Account", FormMethod.Post ,new {ReturnUrl = ViewBag.ReturnUrl}))
{
<input type="hidden" name="ReturnUrl" value="#Request.QueryString["ReturnUrl"]" />
// .....
}
See if this helps in your case.
I am using ASP.NET MVC4 SimpleMembership and SimpleRoleProvider to determine authorization before exposing certain methods.
For example:
[Authorize(Roles = "Admin,Corporate")]
public ActionResult Edit(int id = 0)
{
//some code
return View(model)
}
If the user is not in the "Admin" or "Corporate" role (or their session has expired), they are correctly sent to the /Account/Login page.
However, one tester brought up a good point that once on this Login page, there is no hint as to why the user was sent here. If they simply aren't authorized to access the page they are trying to access, they keep logging in again and again and thinking the site is broken.
Ordinarily, I would add a property to the model or pass an optional parameter in the url with a message such as,
You do not have adequate permissions to access that page.
Please log in as an administrator.
or something to that effect. However, because the filter happens before they enter the method, where / how would I add the message?
I have actions in different controllers which are decorated with [Authorize(Roles = "admin")]
and they are working fine.But the problem is that they always redirect to login page even if the user is loged in.for example following actions redirect to login page even if the user is loged in
[Authorize(Roles = "admin")]
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
VAT vat = db.VAT.Find(id);
db.VAT.Remove(vat);
db.SaveChanges();
return RedirectToAction("Index");
}
Can I redirect to other page if user is loged in or stop redirecting the page
Debug your application and check roles assigned to user for which they are redirecting if they are not having admin roles then it will redirect to the login page.
Another thing you can do is that you can redirect to separate page saying "You don't have rights to view this page" or something like this.
Also following question answer might help you.
Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?
How to redirect [Authorize] to loginUrl only when Roles are not used?
Possibly the answer you're looking for.
I have implemented my own role provider, and I'm not using the default one. It works to the point that it can tell when someone should or should not be able to view a page.
However, can it do the following:
If a user is not logged in, redirect to my login page
If a user IS logged in but does not have the correct role, redirect to a different page
I haven't figured out how to do this with the Authorize attribute, all I have is:
[Authorize(Roles="Admin")]
Basically I need to redirect to a different page based on what part of the authorization fails.
I've looked to see if it were something in web.config but nothing obvious jumps out.
VoodooChild answered #1.
For #2 -
What you can do is check if the user is logged on the login page and display a different message or an entirely different page (or even do a redirect to a different action).
Alternatively you can create your own authorization attribute. This will require that you use this attribute everywhere instead of the default AuthorizeAttribute
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "ActionName" },
{ "controller", "ControllerName" }
});
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
Update:
Just thought of another method. When a redirect is done to login page from a different page, a querystring ReturnUrl is also passed. So you can also check if it contains something AND the user is authenticated, chances are the user didn't have permission to view that page.
Off the top of my head, if you are using FormsAuthentication then to answer your first question - yes If the user is not Authenticated or logged in then it can be redirected to the log on page:
Make sure you have this in web.config file (not sure if you need anything beside this, will look into it..)
<authentication mode="Forms">
<forms loginUrl="~/AccountController/LogOn" timeout="2880" />
</authentication>
To answer your second question: "If a user IS logged in but does not have the correct role, redirect to a different page"
The way we did this was, we used the System.Web.Security.Roles.GetRolesForUser(username); method to get the Roles and based on this we redirected the user to the correct view, after login.
Hope this helps!