I got two website, domain1.com and domain2.com, on domain1.com I have a generichandler that will write a response which tells whether or not the user is authenticated using:
string json = "{\"status\":\"" + HttpContext.Current.User.Identity.IsAuthenticated + "\"}";
HttpContext.Response.Clear();
HttpContext.Response.ContentType = "application/jsonp"
HttpContext.Response.Write(json);
now if I view the generichandler using my browser everything works fine, if the user is logged in it will return "status":"true" otherwise "status":"false"
the problem starts when I send a request from domain2.com to this generichandler using $.get(), the result will always be "status":"false" regardless whether or not the user is logged in. Is this because there are some kind of cross domain violations? I really dont understand this, please help me, thank you.
When you use ASP.NET Forms Authentication a ASPXAUTH cookie is created for that domain.
Cookies are sent with each request so ASP.NET will look for the ASPXAUTH cookie (which doesn't exist in domain2.com) and determine that the user is not authenticated.
You might have to look into a cross domain SSO solution that fits your needs. Have a look here: http://www.codeproject.com/Articles/106439/Single-Sign-On-SSO-for-cross-domain-ASP-NET-applic
Related
Within my web application I have registered Google as a single sign-on provider:
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions {
ClientId = "8765.......apps.googleusercontent.com",
ClientSecret = "Secret"
})
My app doesn't allow users to sign-up/register (instead their accounts are created by an administrator, but they can later link their account up with Google).
In my "Sign in with Google" controller, I am trying to issue a Challenge() to redirect to Google. This might not be thecorrect approach:
string redirectUri = "http://localhost:55262/SSO/Google/ProcessToken"; // actually created in code, but shown as string for clarity
AuthenticationProperties properties = new AuthenticationProperties();
properties.RedirectUri = Server.UrlEncode(redirectUri);
Context.GetOwinContext().Authentication.Challenge(properties, "Google");
This correctly sends the user to Google, but Google then presents Error: redirect_uri_mismatch, saying that:
The redirect URI in the request: http://localhost:55262/signin-google
did not match a registered redirect URI.
I've seen this error before when the return URI collection in the Google control panel does not contain the redirect_uri specified.
If I debug in VS2015, I can see the redirect_uri property being set correctly in the AuthenticationProperties, but it seems that OWIN/Katana is not passing it to Google. Instead, when I hit Google, the return_uri is the default one used by OWIN/Katana. The one I set is being ignored.
The Google request details seem to confirm this:
scope=openid profile email
response_type=code
redirect_uri=http://localhost:55262/signin-google
What am I doing wrong here please? Should I not be using Challenge() to allow users to link up their local application account with Google?
To provide additional information on the accepted answer...
Its okay to ignore /signin-google
It emerges that the /signin-google URI is internally-managed by OWIN/Katana. You, as a developer, do not need to be concerned by it, but you do need to add it in the Google developer console as an Authorized redirect URI.
In the Google request, note that OWIN always passes the redirect URI to Google as /signin-google, regardless of what custom URI you set in the AuthenticationProperties.RedirectUri property. Although at first this may seem like a bug/problem, it has a major advantage in that OWIN can manage all callbacks via a single callback URI. Your callback URI is not forgotten about either (see below)!.
So what about your own redirect URL?
Well, that's where the AuthenticationProperties() come into play. By specifying your own callback URL like so...
AuthenticationProperties properties = new AuthenticationProperties { RedirectUri = "https://my.app.com/custom/callback/uri" };
...after OWIN has examined the Google token and extracted the necessary details, the user is then redirected to your specified URL.
This was where I was getting confused, as I didn't understand what to do with /signin-google, when in actual fact no action was taken. This applies to both MVC and webforms - you do not need to concern yourself with what gets passed to Google. However, if using webforms, or specifying authorization rules in web.config, you will need this to prevent returning users hitting the logging page again:
<location path="signin-google">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
Here is all the code you need to send the user to Google, and return the token containing their details:
Outbound
Send the user to Google from a controller, button click event, page load, anything (regardless of your ASP/hosting stack):
// set the callback, for after OWIN finishes examining what comes back from Google
AuthenticationProperties properties = new AuthenticationProperties { RedirectUri = "https://www.myapp.com/some/callback/uri" };
// send the user to Google
Context.GetOwinContext().Authentication.Challenge(properties, "Google");
// Stop execution of the current page/method - the 401 forces OWIN to kick-in and do its thing
Response.StatusCode = 401;
Response.End();
Inbound
The user is returned from Google after validating their identity
Microsoft.AspNet.Identity.Owin.ExternalLoginInfo loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
Note that the OWIN's Open Authentication have predefined methods. In another words, in localhost:port/signin-google, the OWIN awaits for calling the signin-google by the external authentication service (Although you can't find its implementation inside the project). The signin-google is a valid and working path and I prefoundly exhort you not to change it (due to avoid writing a new implementation as a controller action).
I had similar trouble, After spending many weary days, finally, I found out the problem comes from the original user's URL which is effective on the sent redirect_uri by the OWIN. Clearly:
If you type www.site.com → redirect_uri equals to
www.site.com/signin-google
If you type site.com → redirect_uri equals to
site.com/signin-google
And Google will return redirect_uri_mismatch Error for one of the above cases based on entered redirect URLs in Console. I think your problem comes from this reality too and the solution is setting any possible URLs in console.
I am using Spring to create my webapp. I have a scenario like:
An ExternalService sends a GET request to my Controller that has a mapping /DoOperation with some user info as param. I get the param check the user if he is logged into my system or not if NOT i send him to OpenId verification with returnUrl, which is in same webapp say /Authenticated.
The ExternalService does not provide a returnUrl (Not related to OpenId returnUrl) and forces to respond the same request it made at /DoOperation.
Now, How could I keep the very HttpServletResponse (if there is a way, apparently there is not) so that I can write a response once I have gone somewhere else e.g. at OpenID page to verify user and then /Authenticate in this case... so that ExternalService could read it.
Could Servlet Filter help here?
We use Sharepoint 2007 as our internal portal. I'm currently developing a custom app (asp.net MVC2) and I've been asked to have the login process mimic Sharepoint, where a user is initially logged in using SSO but then can opt to logout and provide different credentials.
Any blogs/guides on how to do this?
UPDATE:
Thanks to reflector I was able to find a way to do this, but it doesn't totally work.
First off, I'm testing this on IIS running on my local Windows 7 machine.
I have setup the "LogOff" action to do the following:
var current = System.Web.HttpContext.Current;
var response = current.Response;
object obj2 = current.Items["ResponseEnded"];
if ((obj2 == null) || !((bool)obj2))
{
current.Items["ResponseEnded"] = true;
response.StatusCode = 401;
response.Clear();
response.Write("401 UNAUTHORIZED");
response.End();
}
This partially works. When I click "logOFf" I get prompted for credentials. Oddly enough, when debugging, I can see the method gets called twice (this is an MVC action).
But, even when providing valid credentials I still can't log back in. After the third try I get a 401 page.
My only thought here is somehow it's trying to use Kerberos to authenticate and since I don't have Kerberos setup on this machine it fails. But, when I first access the site from IE it just passes me IE credentials on (the SSO) and everything works fine, so I'm not sure why a second authentication fails.
asp.net membership provider is a great choice. There are a lot of options for authentication methods, based on your question though I think the .net membership provider would be your best bet. If you are using simply windows authentication, even sharepoint has its faults as IE will attempt to change the login back to the windows account user.
http://msdn.microsoft.com/en-us/library/yh26yfzy.aspx
I have an app with multiple subdomains, subone.parent.com, subtwo.parent.com.
I have a logon page at parent.com/login. When a user logs in I redirect them to the proper domain based on which one they are a member of. This works fine.
FormsAuthenticationTicket ticket = new FormsAuth...
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Domain = subone.parent.com
Response.Cookies.Add(cookie)
This properly authenticates the user for subone.parent.com and not subtwo.parent.com. However I would like to do the following.
If the user goes back to parent.com, I would like to know that they are logged in and redirect them back to subone.parent.com.
Is there a best practice for accomplishing this? Or do I have to set another cookie for parent.com?
I'm working in asp.net mvc if it matters.
THanks!
You can share cookies across domains like you are trying to do, but its not straight forward, example here.
Another options is to set the cookie to be ".parent.com" rather than specifying the sub-domain explicitly and use the cookie store the details of the sub-domain. Then you can access the cookie from any of your sub-domains (and parent assuming its www.parent.com).
If your using MVC, you can pretty easily create a custom filter and add to the www.parent.com controllers to check for the existence of the cookie, and if so redirect to the sub domain the cookie specifies. Further details of filters here.
I would set the cookie for the explicit domain as you have there because that maintains any security information within that specific domain's cookie. You can also add a non-encrypted cookie at the *.parent.com level that holds information about which domains have been authenticated. There is no real way to tie this together though without using maybe timestamps and having a logical connection between the applications (ie - sub2 has a session timeout of 20 minutes so if the domain + valid timestamp occur in the parent cookie it would be valid, however this is business logic).
I'm not sure the reasoning behind the disconnect between domains, but you might actually prefer to have a single cookie that has encrypted text behind encrypted text. EG:
1) Sub1 logs in, sets the parent.com cookie as valid. Sends a piece of user data to an authentication web service.
2) The authentication service recognizes sub1 as the sender, encrypts the user data, and adds it to a custom cookie object.
3) The custom cookie object constructs a composite string on a unique split character (or sequence) and makes it available to the service method.
4) The service, using the forms encryption, encrypts the entire ticket and sends it back to the original login.
That way each server would be able to unencrypt the global ticket, but each piece of data would be encrypted using a common algorithm but a server based salt. So if sub2 attempts to read the cookie data from sub1, it gets the encrypted version rather than raw data.
you could share the same session on all subdomains. That is the code we use to accomplish that :-)
void MasterPage_Unload(object sender, EventArgs e)
{
///ASP.NET uses one cookie per subdomain/domain,
///we need one cookie for _all_ subdomains.
if (Context.Response.Cookies["ASP.NET_SessionId"] == null)
return;
var sessionCookie = new HttpCookie("ASP.NET_SessionId", Context.Session.SessionID);
sessionCookie.Domain = ".yourdomain.com" ;
Context.Response.SetCookie(sessionCookie);
}
inside the Page_Load method is:
Unload += MasterPage_Unload;
it works great :-)
robert
I have multiple asp.net sites. When a user logs unto one of the sites, I want to store a cookie telling me that a user has logged on. When the user later visits one of the other sites I have, I would like to read the cookie from that site.
AFAIK you neither can read cookies from or write cookies to other sites, so what could a workaround be?
Perhaps making a redirect to http://www.othersite.com/SaveCookie.aspx ?
Give me some ideas :-)
One of our clients has exactly the same requirement (logging into multiple sites on different domains), complicated by the fact that one of the sites requires that the user is logged in to a classic ASP application, a .NET 1.1 application and a .NET 3.5 application running on different hardware, but under the same domain...
We've basically implemented a system of round-robin style redirects, where each domain logs the user in, then bounces them on to the next domain until they return to the original domain at which point they are redirected to their original request.
So (pages and domains changed to protect the innocent):
User requests www.example1.com/page1.aspx
A cookie is set that tells us the user was attempting to access page1.aspx, and the user is sent to the www.example1.com/login.aspx
The user logs in, and is then redirected to www.example2.com/processlogin.aspx?token=EncryptedToken
ProcessLogin.aspx checks for a cookie telling it where to direct the user, if it can't find one, it decrypts the token, logs the user in on example2.com, and then redirects them to www.example1.com/processlogin.aspx?token=EncryptedToken (or example3.com - repeat as required)
As in 4, ProcessLogin.aspx checks for the cookie, finds it, deletes it and redirects the user to /page1.aspx.
If the user later on visits a page on www.example2.com, before the authentication ticket timeout, they will still be logged in on that site as well.
Edit to respond to comment
That depends on how you are making the "request to the other pages". If you make the request from your code behind, what you're doing is effectively setting the cookie on the server, rather than on the users browser.
Cookies need to be issued by the server to the client browser, and that is done in the headers of the page response - so you need to direct the users browser to a page on the other site to issue the cookie from that domain.
You could generate a request to the other page in an IFrame, or try and do it in a self closing pop-up window - but that has other issues like pop-up blockers, flickering windows, etc.
After some investigation we found that a round-robin set of redirects like this was the simplest and most reliable solution.
A very basic code setup:
An .aspx page, containing a Login control, with a method "OnLoggedIn" attached to the LoggedIn event of the control:
void OnLoggedIn(object sender, EventArgs e){
string returnUrl = Request.QueryString["returnUrl"];
// Create new cookie, store value, and add to cookie collection
HttpCookie myCookie = new HttpCookie("WhereTo");
myCookie["ReturnUrl"] = ReturnUrl;
Response.Cookies.Add(myCookie);
// Redirect user to roundtrip login processor on next domain.
// Work out domain as required.
string redirect = GetNextDomain();
// Add encoded user token
redirect += "?token=" + EncodeUserToken();
// Redirect the user, and end further processing on this thread
Response.Redirect(redirect, true);
}
Then on both servers you have ProcessLogin.aspx, that has something like this in it:
protected void Page_Load(object sender, EventArgs e){
// Look for redirect cookie
if (Request.Cookies["WhereTo"]["ReturnUrl"] != null){
// Save value from cookie
string redirect = Request.Cookies["WhereTo"]["ReturnUrl"];
// Delete original cookie by creating an empty one, and setting it
// to expire yesterday, and add it to the response.
HttpCookie myCookie = new HttpCookie("WhereTo");
myCookie.Expires = DateTime.Now.AddDays(-1d);
Response.Cookies.Add(myCookie);
// Redirect the user, and stop processing
Response.Redirect(redirect, true);
}
// Still here, so log in and redirect
string encryptedToken = Request.QueryString["token"];
if (!string.IsNullOrEmpty(encryptedToken)){
// Decrypt token, and log user in
// This will vary depending on your authentication mechanism
PerformLogin(encryptedToken);
}
// Redirect user to roundtrip login processor on next domain.
// Work out domain as required.
string redirect = GetNextDomain();
// Add encoded user token - no need to recalculate, it will be the same
redirect += "?token=" + encryptedToken;
// Redirect the user, and end further processing on this thread
Response.Redirect(redirect, true);
}
You're looking for a Single Sign-On (SSO) solution.
If it's possible for you to host your sites at different subdomains below the same domain, you can save cookies that are shared for the whole domain, e.g.:
"site1.yourdomain.com" and
"site2.yourdomain.com"
can both read cookies saved to the domain "yourdomain.com"
Another alternative is to tell the other site about the login via a request to it, as in your redirect suggestion. You could do this in several ways, e.g. by loading the page in an iframe, sending the data directly from one server to another, and so on. None of these are particularly elegant, though, and in the case of login, as Tomas Lycken says, you should really be going for a proper SSO implementation.