I'd like to share authentication between two websites as I slowly rewrite functionality from the old one and transition to the new one. I found an answer on here to do that: Sharing Authentication between ASP.NET sites
The problem is that when I set the domain property, the authentication cookie stops working. The user still is authenticated succesfully, and the cookie appears to be created and set correctly... but when the site is loaded the user is forced to the login screen.
I'm using the standard MVC generated authentication code, which works fine as long as domain is not set:
public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
If I debug and look at the FormsAuthentication object it looks like the domain and everything else is set correctly.
So what could be causing this, and how do I fix it?
Could you please try with setting a machine key in your web.config? It must be the same within every web site.
http://aspnetresources.com/tools/machineKey
Related
I have an ASP.NET 3.5 application that I recently extended with multiple membership and role providers to "attach" a second application within this application. I do not have direct access to the IIS configuration, so I can't break this off into a separate application directory.
That said, I have successfully separated the logins; however, after I login, I am able to verify the groups the user belongs to through custom role routines, and I am capable of having identical usernames with different passwords for both "applications."
The problem that I am running into is when I create a user with an identical username to the other membership (which uses web.config roles on directories), I am able to switch URLs manually to the other application, and it picks up the username, and loads the roles for that application. Obviously, this is bad, as it allows a user to create a username of someone who has access to the other application, and cross into the other application with the roles of the other user.
How can I mitigate this? If I am limited to one application to work with, with multiple role and membership providers, and the auth cookie stores the username that is apparently transferable, is there anything I can do?
I realize the situation is not ideal, but these are the imposed limitations at the moment.
Example Authentication (upon validation):
FormsAuthentication.SetAuthCookie(usr.UserName, false);
This cookie needs to be based on the user token I suspect, rather than UserName in order to separate the two providers? Is that possible?
Have you tried specifying the applicationName attribute in your membership connection string?
https://msdn.microsoft.com/en-us/library/6e9y4s5t.aspx?f=255&MSPPError=-2147217396
Perhaps not the answer I'd prefer to go with, but I was able to separate the two by having one application use the username for the auth cookie, and the other use the ProviderUserKey (guid). This way the auth cookie would not be recognized from one "application" to the other.
FormsAuthentication.SetAuthCookie(user.ProviderUserKey.ToString(), false);
This required me to handle things a little oddly, but it simply came down to adding some extension methods, and handling a lot of membership utilities through my own class (which I was doing anyhow).
ex. Extension Method:
public static string GetUserName(this IPrincipal ip)
{
return MNMember.MNMembership.GetUser(new Guid(ip.Identity.Name), false).UserName;
}
Where MNMember is a static class, MNMembership is returning the secondary membership provider, and GetUser is the standard function of membership providers.
var validRoles = new List<string>() { "MNExpired", "MNAdmins", "MNUsers" };
var isValidRole = validRoles.Intersect(uroles).Any();
if (isValidRole)
{
var userIsAdmin = uroles.Contains("MNAdmins");
if (isAdmin && !userIsAdmin)
{
Response.Redirect("/MNLogin.aspx");
}
else if (!userIsAdmin && !uroles.Contains("MNUsers"))
{
Response.Redirect("/MNLogin.aspx");
}...
Where isAdmin is checking to see if a subdirectory shows up in the path.
Seems hacky, but also seems to work.
Edit:Now that I'm not using the username as the token, I should be able to go back to using the web.config for directory security, which means the master page hack should be able to be removed. (theoretically?)
Edit 2:Nope - asp.net uses the username auth cookie to resolve the roles specified in the web.config.
I have tried many options and this is my last resort to see if any of the community members have any ideas.
I have .NET MVC 5 application in which I use a Filter to force HTTPS on each unsecured request.
Here is the scenario:
Access my application at say, http://portal.mywebsite.com
It is redirected to third party (auth0) SSO provider for authentication. If the user is not already authenticated, he is redirected to the SSO login page.
The user enters valid credentials, authenticated.
The above scenario works perfectly. But the issue is If I access the same application with https say https://portal.mywebsite.com, it fails. To be precise, it fails to retrieve a ExternalIdentity (ExternalCookie) on the server.
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (externalIdentity == null)
{
throw(new Exception("Could not get the external identity. Please check your Auth0 configuration settings and ensure that " +
"you configured UseCookieAuthentication and UseExternalSignInCookie in the OWIN Startup class. " +
"Also make sure you are not calling setting the callbackOnLocationHash option on the JavaScript login widget."));
}
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true }, CreateIdentity(externalIdentity));
return RedirectToLocal(returnUrl);
}
Also, accessing the application with https on my test environment works and not the production environment.
All my web applications are hosted as Azure WebRoles.
I tried Fiddler to watch the requests between working and non-working to see if I can find any useful information in identifying the issue but no success.
Any thoughts or ideas that I could try to help me narrow down the cause?
Thanks in advance!
There is a bug in Microsoft's Owin implementation for System.Web. The temporary fix is addressed here at github.com/KentorIT/owin-cookie-saver
Someone had the same issue .AspNetApplicationCookie and ASP.NET_SessionId not created
Title should say it all.
Here's the code to set the cookie:
// snip - some other code to create custom ticket
var httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encodedTicket);
httpCookie.Domain = "mysite.com";
httpContextBase.Response.Cookies.Add(httpCookie);
Here's my code to signout of my website:
FormsAuthentication.SignOut();
Environment:
ASP.NET MVC 3 Web Application
IIS Express
Visual Studio 2010
Custom domain: "http://localhost.www.mysite.com"
So when i try and log-off, the cookie is still there. If i get rid of the httpCookie.Domain line (e.g default to null), it works fine.
Other weird thing i noticed is that when i set the domain, Chrome doesn't show my cookie in the Resources portion of developer tools, but when i dont set the domain, it does.
And secondly, when i actually create the cookie with the custom domain, on the next request when i read in the cookie from the request (to decrypt it), the cookie is there, but the domain is null?
I also tried creating another cookie with the same name and setting the expiry to yesterday. No dice.
What's going on? Can anyone help?
I believe if you set the domain attribute on the forms element in you web.config, to the same as the one in your custom cookie, it should work. (EDIT: that approach won't work because the SignOut method on FormsAuthentication sets other flags on the cookie that you are not, like HttpOnly) The SignOut method basically just sets the cookie's expiration date to 1999, and it needs the domain to set the right cookie.
If you can't hardcode the domain, you can roll your own sign out method:
private static void SignOut()
{
var myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
myCookie.Domain = "mysite.com";
myCookie.Expires = DateTime.Now.AddDays(-1d);
HttpContext.Current.Response.Cookies.Add(myCookie);
}
An authentication cookie is just a plain cookie; so you would remove it the same way you would any other cookie: expire it and make it invalid.
I had a similar problem. In my case, I was storing some userData in the AuthCookie and experienced the same effects as described above, and upon authentication at each request, reading the cookie and putting the userData in a static variable. It turned out in my case that the data was being persisted in the application. To get around it, I had to first clear my static variable, and then expire the cookie. I used the following in the LogOff method of my AccountController:
AuthCookie.Clear(); //STATIC CLASS holding my userdata implemented by me.
Response.Cookies[FormsAuthentication.FormsCookieName].Expires = DateTime.Now.AddYears(-1);
Response.Cookies[FormsAuthentication.FormsCookieName].Value = null;
return RedirectToAction("Index", "Home");
Hope this helps.
UPDATE
On a hunch after submitting, I replaced the middle two lines with:
FormsAuthentication.SignOut();
... and it worked fine where it didn't before.
Note:
AuthCookie.Clear();
... does not touch the AuthCookie, it just resets the static class I wrote to default values.
Again, hope this helps.
We have a lot of domains running on one IIS WebSite/AppPool.
Right now we are in the process of implementing SSO with Windows Identity Foundation.
in web.config the realm has to be set with
<wsFederation passiveRedirectEnabled="true" issuer="http://issuer.com" realm="http://realm.com" requireHttps="false" />
My problem is that the realm is dependent on which domain the user accessed the website on
so what I did is that I set it in an global action filter like this
var module = context.HttpContext.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as WSFederationAuthenticationModule;
module.Realm = "http://" + siteInfo.DomainName;
My question is. When I set the realm like this, is it set per user instance
or application instance.
Scenario.
User A loads the page and the realm get set to domain.a.com.
User B is already logged in on domain.b.com and presses login.
Since user A loaded the page before User B pressed login, user A will hit the STS
with the wrong realm set.
What will happen here?
If this is not the way to set the realm per user instance, is there another way to do it?
I have already solved the problem.
I set PassiveRedirectEnabled to false in web.config
I set up the mvc project to use forms authentication, eventhough I dont.
I do that so that I will get redirected to my login controller with a return url everytime a controller with [Authorize] is run.
In my login controller I do
var module = HttpContext.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as WSFederationAuthenticationModule;
module.PassiveRedirectEnabled = true;
SignInRequestMessage mess = module.CreateSignInRequest("passive", returnUrl, false);
mess.Realm = "http://" + Request.Url.Host.ToLower();
HttpContext.Response.Redirect(mess.WriteQueryString());
This is definitely not really how it should be, for me it feels like Windows Identity Foundation is lagging behind, both in documentation and microsoft technology wise, no examples for MVC.
For other MVC people i recommend them to not use the fedutil wizard, and instead write the code and configuration themself
I'm coming across a peculiar request: I have a website that uses Forms Authentication, but it now needs to grab the WindowsPrincipal (Windows Authentication) at some point in order to reuse it.
My first instinct was to create a new page, and to disable Anonymous Access on IIS for that precise page. When I'm on that page, Request.ServerVariables["LOGON_USER"] gives me the current Windows login name as expected.
Unfortunately, Context.User still gives me a GenericPrincipal.
Any idea on how I could get the current WindowsPrincipal in a FormsAuthentication Application? (recreating it by asking the user for his password is not an option)
Found it: Context.Request.LogonUserIdentity is what should be used in this scenario.
It will return the windows user that made the request if Anonymous Access is disabled on IIS (otherwise it'll return the IIS anonymous user).
For those interested on how to reuse it:
lblUserName.Text = WindowsIdentity.GetCurrent().Name;
// will return the ASP.Net user (that's useless to us)
WindowsIdentity id = Context.Request.LogonUserIdentity;
WindowsImpersonationContext ctx = id.Impersonate();
try
{
lblUserName.Text = WindowsIdentity.GetCurrent().Name;
// will return the correct user
// (...) do your stuff
}
finally
{
ctx.Undo();
}