How to clear MVC 3 routing parameters on log out - asp.net

I have an ASP.net MVC 3 app using razor, and when using the default AccountController to log out the current user, I have picked up a security issue. After clicking log out (_LogoutPartial view), I get redirected to Log On page. Fine, but when I click Back on the browser, it allows me back into the web application and does not ask for me to log on.
The route with parameters is as follows:
routes.MapRoute(
"Person", // Route name
"Person/{profileName}/{action}/{id}", // URL with parameters
new { controller = "Person", action = "Index", id = UrlParameter.Optional } // defaults
);
//example http://localhost:1946/Person/JoeBlack/ListTeamMembers
It seems the {profileName} is still active in the session (?) and allowing the call to the controller. However the controller action {ListTeamMembers} has the [Authorize()] attribute, so Im not sure how its letting the user in...

When you press the Back button on your browser, the last page is retrieved from the cache of the browser. The server is never hit. The user is no longer authenticated. This means that if he attempts to perform some action and sends an HTTP request he will be redirected to the LogOn page. The way to prevent this from happening is by excluding all authenticated pages from the client browser cache. You could have a custom NoCache action filter for this job.

Related

Using OAuth2 in Blazor and redirect user based on claims to certain page after successfull login

I use keycloak (OAuth2/Open ID Connect) to authorize and authenticate users in a Blazor Server Side application. Furthermore I use the services.AddAuthentication().AddOpenIdConnect() extension from Microsoft.
After successfull login I want to redirect the user to a certain page based on its claims. So f.e. if the user has the claim "gis" he should be redirected to "/gis" or if he has the claim "claim123" he should be redirected to page "/page456".
Right now I solve this problem by overriding OnInitialized in the MainLayout.razor and look if the user has a certain claim and then redirect him with the help of the NavigationManager.
#inject NavigationManager navMan;
//...
protected override void OnInitialized()
{
if (_user.HasClaim(t => t.Type == "claim123"))
{
navMan.NavigateTo("/page456");
}
}
The problem with this approach is, that it will first load the default index home page ( "/" ) and then after a short period of time will reload to the new destination. This "flickering" is not very nice and I thought there must be a better way to directly redirect after successful "/openid-connect" redirection. Can I may use some kind of middleware or one of the OpenIdConnectEvents?

ASP.NET MVC Only use route to access controller action

I have a controller action that I only want users to be able to access by using a friendly route.
So instead of going to "/controller/action"
They should only be able to go to "/controller-action"
This is the route I have setup for this.
routes.MapRoute("ControllerAction",
"controller-action",
new { controller = "Controller", action = "Action" });
Is there any way to keep this route to this controller/action while redirecting requests from /controller/action to /controller-action ?
The reason this is being asked for is to keep our paths "less deep" for seo reasons.

Detecting forms authentication timeout in login page

When you have forms authentication setup to redirect to login.aspx when accessing a protected page, what's a good way to detect in login.aspx whether the user was sent there because they haven't logged on yet, or because their forms auth ticket is expired? I'd like to display a "you've timed out" message.
(I do not mention the word session in this question, because ASP.NET treats them so distinctly, however, if there is a good solution that involves session, I'm all ears)
I've solved this in the past by having another cooke "hasloggedin" set when a user logs in and then checks to see if that exists to determine if it's a timeout and then display an appropriate message. But, this has to be a common problem?
Forms authentication will automatically append a URL parameter 'ReturnURL', indicating what page (if any) triggered the redirection to the login page. Most websites have a 'Default.aspx' or 'index.html' etc as the default page. You can check the ReturnURL to see if it contains the default page, or some other page in your application.
EXAMPLE:
string refererURL;
if (page.Request.QueryString["ReturnURL"] != null)
{
refererURL = page.Request.QueryString["ReturnURL"].ToString();
}
//Check to see if user was redirected because of Timeout or initial login
//Where "Default.aspx" is the default page for your application
if (refererURL != "" && refererURL != (ResolveUrl("~") + "Default.aspx"))
{
//Show HTML etc showing session timeout message
}
else // User redirected here to to initial login
{
//Show HTML showing initial login HTML message etc
}

Is it possible to forward the current user's FormsAuthentication Identity to another Controller action?

I'd like to use ASP.NET MVC's views as mail template engine. For that, I am calling one controller action from another controller action using a System.ComponentModel.Component.WebClient, parse the returned web page and send it via e-mail.
In this scenario, is it possible to forward the current user's login credentials (I am using FormsAuthentication) to the controller action requested by the WebClient? User passwords are encrypted, so I can't just create a new NetworkCredentials instance with his user name and password.
Yes, you can just copy the .ASPXAUTH cookie from your current Request object to the WebClient
EDIT: I haven't actually tried this myself, so maybe the .ASPXAUTH cookie is removed from the Request object for security reasons.
But since you have access to the machine key, you can create your own cookies on the fly. Here's the code that should do it (I can't find the project where I actually did that)
var ticket = new FormsAuthenticationTicket(User.Identity.Name, true, 5);
string aspxAuthCookieValue = FormsAuthentication.Encrypt(ticket);
This code creates a forms authentication cookie for your current user name and with an expiration time of 5 minutes.
Instead of performing a http request, aren't you looking for something like "rendering a view to a string"

Forms Authentication and POST requests from AJAX

We have an ASP.NET app protected by forms authentication. The app uses MS AJAX heavily to call its web-services.
When the forms authentication times out, and a GET-request happens - all is fine (the user is redirected to a login page).
BUT when the forms authentication times out and a POST-request happens (ajax) - no redirect happens, instead the app returns "401 unathorized" and the browser prompts for username and password (not a login form, but a browsers built-in dialog). Of course entering ANY username/password never helps.
How do I handle this?
UPDATE: After looking with firebug, I just found out that regular POST requests redirect to login fine, it's only web-service calls that throw "401 Unauthorizes".
The difference between a regular request and web-service is URL. Which is "page.aspx" for a regular post-request and "service.asmx/MethodName" for webservices...
Ok, answering my own questin.
After looking into this issue and researching a bit more I found that when a web-app is protected by Forms-Authentication and the user is not authenticated, this is what happens:
If it's a GET-request - the user is
redirected to the login page.
If it's a POST-request to a page - the user is
redirected to the login page.
If it's a POST-request to a web-service - the
user gets 401-unauthorized
Thats how ASP.NET works
And if a web-service is called by AJAX (xmlHttpRequest object) and returns 401 - of course the browser shows a pop-up login box.
Now, what should you do is add some code to Application_PostAuthenticateRequest that will prevent throwing 401 for webservices.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (Request.RequestType == "POST" //if its POST
&& !User.Identity.IsAuthenticated //if user NOT authed
&& !HasAnonymousAccess(Context) //if it's not the login page
)
{
//lets get the auth type
Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
SystemWebSectionGroup grp = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
AuthenticationSection auth = grp.Authentication;
//if it FORMS auth
if(auth.Mode== AuthenticationMode.Forms)
{
//then redirect... this redirect won't work for AJAX cause xmlHttpRequest can't handle redirects, but anyway...
Response.Redirect(FormsAuthentication.LoginUrl, true);
Response.End();
}
}
}
public static bool HasAnonymousAccess(HttpContext context)
{
return UrlAuthorizationModule.CheckUrlAccessForPrincipal(
context.Request.Path,
new GenericPrincipal(new GenericIdentity(string.Empty), null),
context.Request.HttpMethod);
}
I see two solutions:
(1) "Heart beat" mechanism. On each page include a script that will "ping" the server by some dummy ajax request, like:
<script>
setInterval(ping, 60000); // based on comment by John
function ping()
{
$.get('/do/nothing');
}
</script>
This way the session shouldn't expire as long as the browser window is open.
(2) On each ajax request check the status of the response. If the response has "401 unauthorized" code (or any other code different that 200), that means that the session expired and instead of loading the response into some dialog box in the page redirect the user to login page.
Conclusion based on comments:
The best solution would be to combine the two above mechanisms. Heartbeat mechanism will help to keep the session alive as long as the page is displayed in the browser. But in doesn't guarantee that for sure. The connection to the server can be broke and reopened when the session is expired. So you should check the response status anyway.

Resources