ASP.net MVC cross application POST request - c#

I have several ASP.net MVC applications deployed on a single site in IIS. All of the applications are using forms authentication, and all of the applications are configured to use the same machine key.
One of the applications is a 'base site' which provides navigation to the other applications and is where login/logout functionality is being handled. As it stands, a user can log in on the base site and visit the other applications and they will still be authenticated, which is working as intended.
I have a logout form in the header of my shared layout views which submits a post request to the logout action in a controller belonging to the base site. When I submit this form from the base site, the logout works as expected. But if I try to submit the form from any of the other sites, I receive the error message:
"The anti-forgery cookie token and form field token do not match."
This is what my log off action looks like in my Security controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return Redirect("~/");
}
This is what my form looks like in the base site view:
using (Html.BeginForm("LogOff", "Security", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
<input type="submit" value="Log Off"/>
}
Because of the way the directories are set up the other sites use a slightly different version of the same form to call the logout action from the base site:
using (Html.BeginForm("LogOff", "../Security", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
<input type="submit" value="Log Off"/>
}
The base site is at the root of the directory, and the other applications are contained in their own folder within that root.
None of the views I have tried this on have any conflicting forms or antiforgery tokens, and the machine key among all the apps seems to be configured properly or else I don't think the authentication would be working at all. I am considering just redirecting to the base site and performing the logout action from there, but if there is another more simple solution that I have yet to come across that would be nice.

The AntiForgeryToken works by creating a hidden field and a Cookie with the same token (see this blod post). Since your form is being posted to another url, I think that the cookie is either not being transmitted along with the POST or your browser still has a cookie from a former request to your base site and therefore transmits a wrong one. Does this behaviour also occur after deleting the cookies (to ensure that no old ones are being used)?

What I have done to solve this issue is move the LogOff action into a custom controller class which all of my controllers were already inheriting from.
public abstract class BaseController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
//Redirect to home page when logging off.
return Redirect("~/");
}
}
And then simply changed my logout form to call the LogOff action from the current controller, rather than specifiying the Security controller in the base site:
using (Html.BeginForm("LogOff", "", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
<input type="submit" value="Log Off"/>
}
I am not sure if there is a way to get my original version to work or not, so for now I think I am going to go with this.

Related

How unauthorize access redirect user to login page

I am bit new in asp.net mvc. so i have a confusion where it is mention in project that unauthorize access redirect user to login page. before jumping to code i like to understand this and that is why i am positing this question where i am not able to post any code example rather i am looking for concept and guide line.
suppose i am developing a site with ASP.Net MVC core. i have created a empty project where i add two controller.
one is Home and login controller.
Home controller's index action is not protected by Authorized attribute but Home controller's product action is protected.
when user try to access product action then user should be redirected to login page if not signed in. so tell me how to setup project in classic mvc or mvc core where i will mention that user should be redirected to login page if user is not signed in.
i will not use identity rather i will check user credentials from db using ado.net.
please guide me step wise that what i need to follow.
Thanks
You can use type filter attributes to achieve that. For example if you have a BaseController class and it gets inherited in all your Controller classes, you can add a filter attribute there so that you can run your filtering's (for example: Redirect unauthorized user) before or after specific stages in the request processing pipeline.
[CheckAccessPublicStore]
public abstract class BaseController : Controller
{
}
If you want to only filter your Action method
[CheckAccessPublicStore]
public virtual IActionResult Product()
{
return new EmptyResult();
}
Then
public class CheckAccessPublicStoreAttribute : TypeFilterAttribute
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!await _permissionService.AuthorizeAsync(StandardPermissionProvider.PublicStoreAllowNavigation))
context.Result = = new RedirectToRouteResult(new RouteValueDictionary {
{ "controller", "Customer" },
{ "action", "LogIn" }
});
}
}
For more you can learn here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0

Can a user click a login hyperlink?

I can log a user out of my MVC Core application as follows:
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}
Identity server is called and the user is logged out as expected. Is it possible to log a user in by specifying a link? I realise the user will be automatically forwarded if they access a secure area of the website. However, is it possible for the user to say themselves that they wish to login (the return url would be the homepage i.e. home/Index).
I have spent the last hour researching this. I have found lots of examples explaining how to logout explicitly, however I have not found an example, which shows how to login explicitly.
Your question isn't really clear, but if I understand you properly, you're looking for the returnUrl query param. For example, if you want to send a user to /foo, but you want them to login in first, you can create a link like:
Go to Foo, but login first
The question is really unclear, but I will try:
At your Account controller (which is the secure area, right?) create a secured "Login" action.
[Authorized] //actually you don't need this attribute here when have it on controller level
//All this action does is authenticating the user and redirecting to home/Index
public ActionResult AutoLogin()
{
return RedirectToAction("Index", "Home");
}
Then in your layout add:
#Html.ActionLink(
"Login", "AutoLogin", "Account", routeValues: new{}, htmlAttributes: new {id = "loginLink"})

How to feed information to the filter context of the AuthorizeAttribute - HandleUnauthorizedRequest

When my AuthorizeAttribute is called, it sends the page to login like it should. But when login is completed, it goes out to home/index. This is super annoying. How can I keep that from happening?
Since the code path never goes into my controller, I don't have any control of what it's doing.
Using forms authentication, when you access a page that requires authentication, ASP.NET will redirect you to the login page, passing in the ReturnUrl as a parameter.
So, all you need to do is use that parameter to return the user to the place it was before.
public ActionResult Login(string username, string pass, string returnUrl)
{
// code to authenticate user here...
if (!string.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return RedirectToAction("Index", "Home");
}
You might also want to consider using FormsAuthentication.RedirectFromLoginPage
Redirects an authenticated user back to the originally requested URL or the default URL.
https://msdn.microsoft.com/en-us/library/ka5ffkce(v=vs.110).aspx
Notice that calling this method will also issue the authentication cookie, that you might be already doing. So you have to check you are not doing this twice.
Edit: See https://stackoverflow.com/a/1206728/722778
Perhaps you are generating your form specifying a controller an action (e.g. Html.BeginForm("Logon", "Account")?
In that case, try with only this in your view:
Html.BeginForm()
Or passing the ReturnUrl parameter yourselve:
Html.BeginForm("LogOn", "Account", new {ReturnUrl = Request.QueryString["ReturnUrl"] })

How to use ServiceStack authentication correctly in ASP.Net MVC controller

I'm having problem with getting ServiceStack [Authentication] attribute to work in ASP.Net MVC4 controller, pages / action methods with the attribute keep redirecting Users to the login page even after the login details are submitted correctly.
I've followed the SocialBootstrapApi example, with the difference being that all the authentication web service calls are made from the controllers:
this.CreateRestClient().Post<RegistrationResponse>("/register", model);
Other things that I've done so far:
Use my own user session implementation subclassing AuthUserSession (not too different from the example, but using my own implementation of User table)
Inherit ServiceStackController on my BaseController, overriding the default login URL
Enable Auth feature in AppHost with my user session implementation
Registration does work, user auth logic works (even though the session does not persist), and I can see the ss-id and ss-pid cookies in the request.
So my complete list of questions:
How do I make the [Authenticate] attribute work (or, what did I do wrong)?
How do I save and reuse the user session in an MVC controller? At the moment this.UserSession is always null.
How do I logout a user? this.CreateRestClient().Get<AuthResponse>("/auth/logout"); does not seem to work.
Update 1:
The session cookies (ss-id and ss-pid) gets created when I attempt to load the secured page (ones with [Authenticate] attribute), before any credentials get submitted. Is this the expected behaviour?
Update 2:
I can see that the session is saved in MemoryCacheClient, however trying to retrieve it in the base controller via this.Cache.Get<CustomUserSession>(SessionKey) returns null (where SessionKey is like: urn:iauthsession:1)
After much fiddling around, apparently the way to hook ServiceStack authentication is to call the AuthService via:
try {
authResponse = AuthService.Authenticate(new Auth{ UserName = model.UserName, Continue = returnUrl, Password = model.Password });
} catch (Exception ex) {
// Cut for brevity...
}
and NOT authResponse = this.CreateRestClient().Post<AuthResponse>("/auth/credentials", model);!
Where AuthService is defined in the base controller as:
public AuthService AuthService
{
get
{
var authService = ServiceStack.WebHost.Endpoints.AppHostBase.Instance.Container.Resolve<AuthService>();
authService.RequestContext = new HttpRequestContext(
System.Web.HttpContext.Current.Request.ToRequest(),
System.Web.HttpContext.Current.Response.ToResponse(),
null);
return authService;
}
}
Everything else (incl. session) works correctly now.
You can find how it could be done in the ServiceStack Use Cases repository. The following example is based on MVC4 but works perfectly for MVC3 either: CustomAuthenticationMvc.

How to capture the url in mvc-3 for redirection?

I am working on MVC-3 application. In my application if user will try to access any view which is decorated as [Authorize], user redirect to the logon view, there user will enter there credentials and redirect to the authorized view (where user wanted to go).
Problem : Now if user do not have an account then he will create one using register link. But after creating the new account user will redirect to the home. And i want him to redirect to the authorized view instead of home page. How can i do that?
You'll have to pass url from logon view to register controller, and then pass it around controller until user registers, and then redirect back. Pretty cumbersome.
Create a class that derives from AuthorizeAttribute, and override the OnAuthorization method. In this method, make the proper authorization checks, and if the user isn't authorized, execute the following code:
var route = new RouteValueDictionary(new { controller = "Account", action = "LogOn", returnUrl = filterContext.RequestContext.HttpContext.Request.RawUrl });
filterContext.Result = new RedirectToRouteResult(route);
Then, in the Login action in you authorization controller, add a parameter named returnUrl. After verifying that the user has provided the proper credentials, execute the following:
if (Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
Make sure to register the class to be used by MVC, by adding this to Application_start in Global.asax.cs:
GlobalFilters.Filters.Add(new AuthorizationAttribute());
where AuthorizationAttribute is the name of your derived class.
You can save in the user's session the authorization page he wanted to see before redirection ... Request.Url.AbsoluteUri. Try using a Filter.
One simple solution to your problem is to pass the current url to query string from login page (in register link) like this;
#Html.ActionLink("Register", "Index", "Account", new { returnUrl Request.Url.PathAndQuery }, null)
so, now you can catch this returnUrl like others in controller method parameter and you can then redirect user back to this returnUrl

Categories

Resources