Rejecting an otherwise successful ws-federation login in a CookieAuthenticationProvider() - asp.net-mvc

I am working with an MVC5 application which is secured by the WsFederationAuthentication and CookieAuthentication OWIN components. As part of the login process, I inject some additional claims into the ClaimsIdentity returned from the SAML provider (ADFS in this case) during the OnResponseSignIn delegate of a CookieAuthenticationProvider supplied when I set CookieAuthenticationOptions during Startup. That allows me to manipulate the ClaimsIdentity in the OwinContext before it's used to generate the ticket. In practice, this works great for what I've needed to do in the past (mostly add custom claims depending upon values found in a database matched with data supplied from the incoming claim).
However, dependent upon circumstances that arise when OnResponseSignIn is executed, I now need to be able to reject the login in such a way that I can display an error to the user indicating why it's not possible for them to log in. This is proving to be much trickier than I had imagined.
These are the two main approaches I've tried:
Throw an exception in CookieAuthenticationProvider.OnResponseSignIn:
This works in that the authentication cookie is never set and my global error handler catches the exception and displays it to the user. However, I can't determine a way to effectively reject the identity before throwing the exception. So, my global error handler believes that the HttpContext is authenticated and renders the error view with elements that should not be rendered if the user is not authenticated. Nulling the OwinContext.Identity inside OnResponseSignIn does not have an effect as all I'm doing is clearing a value inside the OnResponseSignInContext which has no effect on the overall pipeline. So far as I'm aware, I can't mark the actual ClaimsIdentity itself as not authenticated as IsAuthenticated is set to true when you set the authentication type in the ClaimsIdentity ctor and can't be modified after that.
Add an "error" claim in CookieAuthenticationProvider.OnResponseSignIn as a bread crumb and then look for it in CookieAuthenticationProvider.OnValidateIdentity and throw the exception there.
This approach has the benefit of being able to clear the login (a benefit afforded by OnValidateIdentityContext.RejectIdentity()). So, now my error view renders correctly for an unauthenticated user. However, the problem here is that I can't figure out a way to prevent the authentication cookie created after OnResponseSignIn from being emitted to the client and set in the browser. The tools I have available in either of these delegates allow me to append and delete cookies from the OwinContext, but somehow this cookie is not one that I can do that with. It appears to be actually added to the response stream later in the pipeline where I can't change it. So, if I let OnResponseSignIn complete and then look for the cookie during the execution of OnResponseSignedIn, I don't see it there. Really not sure why that is -- looking at the source of CookieAuthenticationHandler, it looks as thought I should see it as it's added in between the execution of those two CookieAuthenticationProvider delegates. I also can't prevent the cookie from being generated in the first place as the CookieAuthenticationHandler will throw an exception if the ticket can't be serialized.
So, I either can get no cookie set and an as-if-authenticated error view or get an unauthenticated error view, but have a cookie set that effectively prevents the user from trying to sign in again.
Ultimately, I'm rolling around in the mud of the framework enough at this point that I feel like I must be approaching the problem incorrectly. What I'm looking for is an answer to this question:
How can I reject an otherwise valid WsFederation authentication postback and display an error to a user that explains what happened without emitting an AuthenticationTicket to the client?
I'm guessing there must be another point in the process (perhaps not in CookieAuthenticationProvider) where this can be done before the pipeline decides that authentication has succeeded.

Probably you use something like this in your Startup code:
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions())
The class WsFederationAuthenticationOptions provides the possibility to hook into the wsfed authentication (like the CookieAuthenticationProvider for the cookie authentication). You can set the Notifications property to a new instance of WsFederationAuthenticationNotifications. I recommend to use the SecurityTokenValidated property and set it to your implementation i.e. a new method matching the required Func<> definition. This is the point after the token passed validation and a ClaimsIdentity has been generated. Then you can do your additional checks and set the AuthenticationTicket of the methods parameter SecurityTokenValidatedNotification to null and modify the HttpResponse. Than the CookieAuthenticationHandler should not be triggered.

Related

MVC5 Strange bahavior of User.IsInRole in layout and controller

I've got some strange issue and I can't figure it out. I've got a login form in a view. This view is rendered inside layout. In this layout I use User.IsInRole("roleName") to custom it depending on current users role. It works ok.
There's a Login action in my project (building from mvc template) with statement "var result = await SignInManager.PasswordSignInAsync(...)".I check value of "result". If it equals SignInStatus.Success I use User.IsInRole("roleName") to determine users role and redirect him to some other action. And here's a problem. User.IsInRole("roleName") returns false. User.IsAuthenticated is also false. Why is that? How to deal with this situation?
I just want to redirect user to a specific action - each type of user has its own first page so I need to determine user role after he logs in.
Thank you for your help.
Internally at some point the real work of SignInManager.PasswordSignInAsync(...) calls AuthenticationManager.SignIn. Looking at the spec for this we see this comment...
// Summary:
// Add information to the response environment that will cause the appropriate
// authentication middleware to grant a claims-based identity to the recipient
// of the response. The exact mechanism of this may vary. Examples include
// setting a cookie, to adding a fragment on the redirect url, or producing
// an OAuth2 access code or token response.
My take on this is that the reason you can't query Roles, check authorization, or pretty much anything dealing with claims or identities is because the request doesn't have that information setup yet until a response containing the identity is first sent back to the client.
To accomplish what you want to do, just follow Chris Pratt's idea and query the DB for the roles instead of the identity.

ASP.NET MVC: A PreAuthorize-like event, Good throttle point for concurrent logons?

I want to allow a new login to 'kick' a prior login session by the same account, in ASP.NET MVC.
It seems pretty clear that I'll give each browser a cooking representing the session ID. I'll track the currently active session ID in a server-side cache. If an already-active user attempts to log in, I'll verify the business logic (username, password, has been at least 15 minutes since last activity), and then update the active session ID cached at the server.
Now my issue is, a browser is holding an invalid session ID. What is the best point for me to inject a rejection or redirect to sign-in for this scenario?
I could modify the AuthorizeAttribute, but it seems like there should be a cleaner place to do this that won't require me to search and replace all my Authorize attributes, for example via a Global.asax event, or a controller event (I've already extended Controller in my project).
For example, if PreAuthorize existed, I would write some code there to test the request's cookies for a valid user/session ID pair, and if it didn't exist, I could simply remove the authentication cookie from the request, which would result in a standard unauthorized redirection.
So after a bit of research, it seems that a custom AuthorizeAttribute would typically be the correct approach. However, in my case, since I already had a custom role provider implemented, it was just a line of code to do it there. This also benefited me because I only wanted session concurrency for a single role. A side effect is that any use of web.config to control access to static files by role is also in-effect for session concurrency.

How to grant ACL in Spring Security without an explicit authentication?

When I create a new entity I would like to grant ACL permissions (aka ACL entry) to this new entity. So far so easy :-)
The problem arises in the following scenario:
An end user can create the entity without being authenticated on the web site.
The service that persists this new entity hence runs without an authentication context.
But: to grant ACEs one needs to have an active authentication context.
Spring's JdbcMutableAclService uses SecurityContextHolder.getContext().getAuthentication() to obtain the current authentication, so there seems to be no way to circumvent this requirement.
Any ideas are greatly appreciated!
Found the answer myself:
In a web application there always is an authentication context. If a user is not authenticated the authentication is org.springframework.security.authentication.AnonymousAuthenticationToken which has a single granted authority: ROLE_ANONYMOUS.
Hence it is simple to grant this user the right to create ACLs. Just configure the PermissionGrantingStrategy to use this role to authorize requests.
The main answer does not work in the current version of Spring (5.3.22) and Spring Security (5.7.3). I doubt it even worked back in 2012, when the answer was posted since it does not make sense.
PermissionGrantingStrategy is a class that only contains the method bool isGranted(Acl, List<Permission>, List<Sid>, boolean) which decides if the principals in the List<Sid> can access the object with the corresponding Acl with any of permissions in List<Permission>.
This is the function that is called when a user want to access an object with a certain permission. This method determines if access is granted or denied.
This has nothing to do with allowing anonymous users to modify existing Acls. The actual problem comes from calling MutableAcl aclService.createAcl(ObjectIdentity) when the authentication context is empty. This is implemented by JdbcMutableAclService, provided by Spring. The problem is that MutableAcl JdbcMutableAclService.createAcl(ObjectIdentity) has this call Authentication auth = SecurityContextHolder.getContext().getAuthentication(); which forces the access to authorization context even though the Sid could be passed to the createAcl method, so the business logic would be able to createAcls for the chosen users passed in the arguments.
Instead, we have this call which makes it impossible to use Acls from an unauthenticated context if we want to keep using the Spring Classes.
So, the solution would be reimplement the JdbcMutableAclService so the createAcl method does not call the authentication context, instead it has an extra arguments to indicate the Sid of the user we want to create the Acls.
If anyone has any idea on how to do that it would be greatly appreciated.
I am trying to initialize my Acl tables programmatically when my web app starts, but I cannot do it because my initialization code does not have any authantication.

When does the .NET FormAuthentication ticket get checked and how do I tap into this event?

We are attempting to integrate an ASP.NET MVC site with our client's SSO system using PingFederate. I would like to use the built in FormsAuthentication framework to do this. The way I've gone about it so far is:
Set up my Web.config so that my FormsAuthentication LoginURL goes to my site's "BeginAuthentication" action on a "Security" controller. From this action, I set up some session variables (what URL was being accessed, for example, since Ping won't send this info back to me), and then redirect to our client's login page on an external site (www.client.com/Login for example).
From here, the authentication takes place and a cookie is generated on the same domain as the one that our application is running on which contains the unique identifier of the authenticated user, I've set it up so that once this happens, the Ping server will redirect to my "EndAuthentication" action on my "Security" controller.
In this action, I call my membership class's "ValidateUser" method which takes this unique identifier from the cookie and loads in the user on our application that this ID refers to. I save that logged in user in our Session (Session["LoggedInAs"], for example) and expire the cookie that contains the id of the authenticated user that the SSO system provided for me.
All of this works well. The issue I'm wondering about is what happens after our user has already authenticated and manually goes back to our client's login page (www.client.com/login) and logs in as another user. If they do that, then the flow from #2 above to number 3 happens as normal - but since there already exists an authenticated user on our site, it seems as though the FormsAuthentication system doesn't bother kicking off anything so I don't get a chance to check for the cookie I'm looking for to login as this new user. What I'd like to do is, somewhere in my Global.asax file (probably FormsAuthenticate_OnAuthenticate), check to see if the cookie that the SSO system sends to me exists, and if so, sign out of the application using FormsAuthentication.SignOut().
Another issue that seems to be related is that if I let my Session expire, the FormsAuthentication still seems to think I am authenticated and it lets me access a page even though no currently logged in user exists in my Session, so the page doesn't render correctly. Should I tap into the Session_End event and do FormsAuthentication.SignOut() here as well?
Basically, I want to know when the authentication ticket created by
System.Web.Security.FormsAuthentication.SetAuthCookie(..) gets checked in the flow of a request so that I can determine whether I need to SignOut() and force revalidation or not.
Thanks for any help. Sorry for the length of this message, trying to be as detailed as possible.
Mustafa
Welcome to the small section of Hades that is mixing session with formsauth.
If your needs are as complex as presented, you would get more sleep if you implement a full provider stack to share amongst the participating sites. Easier said than done, I know.
But to address your question:
from http://www.codeproject.com/Articles/39026/Exploring-Web-config-system-web-httpModules.aspx
On the way in....Check ticket and set identity #
app.AuthenticateRequest += System.Web.Security.FormsAuthenticationModule.OnEnter-->OnAuthenticate
On the way out... set the ticket and redirect as necessary
app.EndRequest += System.Web.Security.FormsAuthenticationModule.OnLeave
Reflector is your friend. ;-)
I don't know about a specific event for when the cookie is checked, but you could place the appropriate logic in Application_BeginRequest() and check the user's authentication state there.
Another issue that seems to be related
is that if I let my Session expire,
the FormsAuthentication still seems to
think I am authenticated and it lets
me access a page even though no
currently logged in user exists in my
Session, so the page doesn't render
correctly.
The life of the cookie (how long until ASP.NET feels it needs to ask for a password again) and how you are managing state are unrelated. The ASP.NET authentication is cookie based so that, should a developer want to, he could turn off viewstate, session, use no query strings or hidden fields and authentication still works.
If you want to tie the interval at which you request the password to how you are persisting data, then you will want your session expiration to be roughly the same as the cookie expiration, but they will never quite match up. It would be better to have two policies (one for how fast you throw away a users session data and one for how long you are willing to wait before you need to reask for a password)

Set authentication as true for the view of the control that performs the authentication in ASP.Net MVC

In the authentication control I have the following line to mark a user as authenticated in the system (after checking out the password):
FormsAuth.SignIn(userName, rememberMe);
and if I redirect, which is the standard behvaior, everything is ok. But if I show a view right away, the usual ways to check whether a user is authenticated:
Page.User.Identity.IsAuthenticated
Request.IsAuthenticated
don't work. They say the user is not authenticate. How can I make the authentication effective immediately or is there another way to check that would allow me to find out when the user just logged in?
FormsAuth.SignIn is a function which is generated when you create a new ASP.NET MVC project from Visual Studio.
That function simply calls FormsAuthentication.SetAuthCookie, which according to the docs, sets the authentication cookie in the response.
This explains why it works if you redirect (because the client will play back the cookie in the subsequent request), but not right after the call.
Redirecting is the right/conventional way to do this, but if you absolutely insist on checking authentication before a redirect, then you could create an IsAuthenticated flag in session state and refer to that when checking.
On your Controllers, you should be able to use the following to check if they're authenticated.
User.Identity.IsAuthenticated;
I would check to make sure that your AccountController is properly saving the Principal object as you move from page to page.
In addition to using the FormsAuth.Signin or FormsAuthentication.SetAuthCookie, you can also set the User.Identity manually in your sign-in control when the sign-in code executes. As written above, the reason is because the FOrmsAuth.SignIn simply sets the authentication cookie to be picked up next time in the Request_OnAuthenticate event. (Which simply decodes the cookie and sets the HttpContext.User property)

Resources