I have one View called SignIn that contains two partial views for authentication. One is for OpenID and the other is for logging in to my site using an internal account.
The Action for the OpenID form goes to an OpenIDSignIn() Action while the other just points to SignIn(). Everything works great except for when a user clicks a link to a view that requires them to be logged on [Authorize] etc..
I see the returnUrl in the QueryString however this value is not available to the current controller action, due to the fact that they will be either be logging in with OpenID or normally, thus caling the ActionResult associated with either of those views.
Just for clarification the returnUrl is the one that gets thrown in there from FormsAuthentication and is used when doing a RedirectFromLoginPage etc..
public ActionResult SignIn(string returnUrl)
{
if(!string.IsNullOrEmpty(returnUrl))
{
if(UrlUtil.IsLocalUrl(returnUrl))
{
Session.Add("ReturnUrl", returnUrl);
}
else
{
return RedirectToAction("SignIn", "Account");
}
}
return View();
}
This seems to be a viable workaround. However I hate session variables!
Related
.net core 2.0
I have a HomeController with Views, ect. From a same/different controller that has issued a GET to another web site, I need to "start/redirect" to my HomeController. Maybe a bit hard to explain but I'm trying to simulate an Oauth flow where I need to check to see if a user is logged on and if not redirect the user's browser to my signon page. Seems like it ought to be simple out I'm not having any luck. So if I have the code below, can I start up a new browser session with my logon page:
public IActionResult Logon()
{
return View();
}
HttpGet("auth")]
public void Get(string client_id, string redirect_uri, string state, string scope, string response_type)
{
if(!isloggedOn)
{
LocalRedirectPermanent("Logon");
}
}
Turns out Postman will not redirect to a http URL. You have to be able to get the redirect on https. Buried not too deep in the documentation.
I have a super simple Authentication Attribute that I'm trying to implement in an ASP.NET MVC 5 application and I'm having some trouble. I want the attribute to be applied globally, except for specific actions within a controller (for example the login form and the home page).
I've tried decorating the action with the [OverrideAuthentication] attribute with no luck. It gives me a redirect loop error because the application is still running the authentication on the login form, and keeps trying to redirect back to the login form over and over.
Has anyone else seen this behaviour? Any idea what I've stuffed up here?
By way of example, I've created a super simple filter that is currently unimplemented:
public class BasicAuthenticationAttribute
: ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
throw new NotImplementedException();
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
throw new NotImplementedException();
}
}
If I decorate my controller/action like this:
[BasicAuthentication]
public class AccountController : Controller
{
[HttpGet]
[OverrideAuthentication]
public ActionResult Login()
{
return View();
}
}
I get a not implemented exception when I navigate to the Login action, even though that action shouldn't be running the authentication code at all. Have I misunderstood how overrides are supposed to work?
I think you have confused authentication with authorization (as many people do). It doesn't make sense to make a [BasicAuthenticationAttribute] and register it globally, because authentication only happens upon login, not on every request.
Authorization is what takes place after the user has logged in to check whether the user has the required privileges to do a specific action, and it makes sense to do authorization globally. Authorization in MVC is handled by the [AuthorizeAttribute] and you can inherit it if you need to customize the way the authorization check is done. You can also register it as a global filter.
The [AllowAnonymousAttribute] works in conjunction with [AuthorizeAttribute], and basically tells it to skip the authorization check. It should also be noted that the [AllowAnonymousAttribute] will have no effect unless it is used with the [AuthorizeAttribute].
I hope someone will be able to put me on the right track, been trying to resolve this now for hours.
I am currently in the process of redeveloping a web application and I would like to use the MVC4 attributes for managing access to the various parts of the application.
The issue I am having is that the Authentication & Permissions are all handled by middle-ware applications that the web app has to interface with.
I was wondering if even with this restriction would I be able to use the security attributes & letting the web app know that the user is Authenticated.
Yes, you will be able to use existing Authorize attribute. All you have to do is write a custom Membership and Role providers that will use your existing services instead of relying on the default SQL database.
If you don't want to go through all this hassle you could also write a custom authorization attribute (deriving from AuthorizeAttribute) and inside the AuthorizeCore method call your service to check whether the current user has the desired roles.
Definitely. Not only is it possible, but also it's pretty easy. And if you can think of ASP.NET Roles as "activities", then you don't need to derive anything; everything you need is built in.
These examples assume securityService is the service that communicates with your middle-ware applications, and has two methods, GetUser and GetUserRoles:
Your Login action method
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (!ModelState.IsValid) return View();
var user = securityService.GetUser(model.Username, model.Password);
if (user == null)
{
ModelState.AddModelError("", "Username or password are incorrect.");
return View();
}
FormsAuthentication.SetAuthCookie(user.Username, model.Remember);
return Redirect(returnUrl);
}
In your Global.asax.cs
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
{
string username = User.Identity.Name;
string[] roles = securityService.GetUserRoles(username);
IIdentity identity = new GenericIdentity(username);
Context.User = new GenericPrincipal(identity, roles);
}
}
That's it. Login handles the authentication (when the user logs in), while Application_AuthenticateRequest handles the authorization (on every request). You then proceed to decorate your action methods with Authorize(Roles = "XYZ") making sure "XYZ" matches what comes back from your GetUserRoles method.
I want to make a custom AuthorizeAttribute that includes a Message property. The problem is, my FormsAuthentication redirects to the specified loginUrl. How can that View get access to the attribute's Message property?
for example, I have this action using my custom AuthorizeAttribute
[Authorize(Message="You must be logged in to see user settings.")]
public ActionResult Settings()
{
return View();
}
which gets redirected to /Account/LogOn (thanks to the FormsAuthentication settings in web.config) if the user is not logged in. I want to show the "You must be logged in to see user settings" on the LogOn View so the user knows why they were redirected to the LogOn page
One option would be to put the value of your Message property into TempData in the HandleUnautherizeRequest method of your custom AuthorizeAttribute. Then in the LogOn action on your Account controller take the value from TempData and put it into the ViewBag or your model so that the View has access to it.
AuthorizeAttribute:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Controller.TempData["MessageFromMyAttribute"] = this.Message;
}
AccountController
public ActionResult LogOn()
{
ViewBag.AttributeMessage = TempData["MessageFromMyAttribute"];
return View();
}
Because MVC is doing a redirect behind the scenes the value in TempData will persist across the redirect.
Do the following:
Create your own attribute, that inherits from AuthorizeAttribute
In your filter, add the message to TempData
In the action that you redirect to when login is required, get the message from TempData and pass it to the view.
This is a duplicate of How to RedirectToAction in ASP.NET MVC without losing request data
Hi, I have come into a problem which is making me scratch my head a little bit. Basically I have a login page Login.aspx , which has username and password fields, as well as an important little checkbox. The login is handled in the AccountController Login method. The code currently is as follows:
[AcceptVerbs(HttpVerbs.Post)]
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification =
"Needs to take same parameter type as Controller.Redirect()")]
public ActionResult LogOn(string userName, string password, string returnUrl,
bool sendStoredInfo)
{
if (!this.ValidateLogOn(userName, password)) {
return View();
}
this.FormsAuth.SignIn(userName, false);
if (!String.IsNullOrEmpty(returnUrl)) {
return Redirect(returnUrl);
} else {
return RedirectToAction("Index", "Home");
}
}
Basically, if the line return Redirect(returnUrl); fires, then it will end up in another controller, the OpenIDController, and it is that situation where the sendStoredInfo bool becomes important. But the problem is I have no reference to it when I'm in the OpenIDController. How can I send this value across?
Change the call to:
return RedirectToAction("LoginFailed", new { sendFlag = sendStoredInfo });
The controller action method signature could be something like:
public ActionResult LoginFailed(bool sendFlag)
{
...
}
Also consider using TempData to pass data from controller to controller. This can be advantageous as you wont have to expose the bool sendFlag interface potentially to the user.
Code in the first controller:
TempData["sendFlag"] = sendStoredInfo;
return RedirectToAction("LoginFailed");
Code in the second controller:
public ActionResult LoginFailed()
{
bool sendFlag = TempData.ContainsKey("sendFlag")? TempData["sendFlag"]: false;
}
Because of the nature of redirects, you can only perform a GET operation.
This means that you have to pass the parameter as part of the query string.
So you would redirect to a url like http://host/dir/page?sendStoredInfo=true
Then, you can chose to have it part of your method signature in the other controller, or, you can choose to access it directly using the HttpRequest exposed by the HttpContext for the operation.
You can also call the RedirectToAction, as per this previous question:
How to RedirectToAction in ASP.NET MVC without losing request data
As far as my knowledge serves me well, four different methods exist to handle passing data between controllers in asp.net MVC. They are 1. ViewData 2. ViewBag 3. TempData and 4. Sessions. If you may like a relatively good explanation besides a downloadable sample, please take a look at here