How to allow secure login across multiple domains - http

I have a web based application that allows a user to point their DNS at my IP and my app will serve up their content based on the domain name I see in the HTTP host header. I should point out this is done by the application, not by the actual HTTP server(apache), it is a rebranded app sort of thing. The problem I have is that I would like the users to be able to login through a form on the served page and somehow stay within the domain of the user. This is easy, unless you want security. I would have to have a SSL cert installed for every domain to pull this off. Right now I can do it by submitting the form to a domain with an SSL cert installed, but due to browser security I can't exactly set the required cookies on the original domain.
Does anyone know a way I can securely log in users through the app that does not involve installing a lot of ssl certs. I can think of some convoluted ways using redirects or other mechanisms, but it is not that clean. I don't mind a submit to the secure url and a redirect, it's just setting the cookie can't be done.

I've done this before using the following method...
Create auth key on server 1.
create_auth_key
expires = time + expire_time
data = username + '|' + password + expires
secret = 'my secret key'
hash = md5( data + secret )
key = base64( data ) + hash
On server two you pass the newly created authkey
valid_auth_key(key)
hash = key[-hash_size:]
b64data = key[:-hash_size]
data = base64decode( b64data )
data_hash = md5( data + secret )
if data_hash != hash:
return false # invalid hash
data_parts = data.split('|')
user = data_parts[0]
password = data_parts[1]
expires = data_parts[2]
if now > expires:
return false # url expired
return true
It's kind of quick and dirty but only relies on simple data passed via URL. The down side is that a specific url is all that's required to login and someone could share that url for a period of time. You also have to make sure your expiration time is not greater than the time difference between servers.

A common trick is to pass data in the URL. Facebook Connect does this. You can redirect from one domain to the other with a session token in the URL and then verify the token (perhaps convert to a cookie) when the request comes in on the other domain. Edit: the MSDN article that Facebook links to has much more detail.

Related

ASP Identity : Binding Sessions To Request Url (Sub Domains)

I don't think the title of the question is particularly accurate but that's how best i could title it.
Without summarizing, I have an MVC app hosted on Microsoft Azure. The app was built for multiple institutions (each connecting to a separate database) but the Login Module (Asp Identity) is in a central database (users are identified by their institution code). So during deployment, a sub domain is created (still pointing to the app on azure).
My problem is, the app has no regard for the Request Url, the sessions are maintained across domains. This is a serious problem because i cache User data (by session). So if a user Logs in on "domain1.myapp.com" , then opens another tab , logs into "domain2.myapp.com" , all data cached for the user logged in for "domain1" will be used for the user logged in at "domain2". The app doesn't bother to get data for the user in "domain2" since the key for that data value is already present in the session cache.
Okay, I hope the problem is understood. How do i get past this.
Ideas ? implementation ?
EDIT 1
I insert data into the cache by,
HttpRuntime.Cache.Insert("KEY", "VALUE", null, DateTime.Now.AddMinutes(30),Cache.NoSlidingExpiration);
Your caching strategy needs to change when you cache per user and per domain. The simplest approach is just to add the domain and user name to the cache key, which will keep each cache in a separate per user and per domain bucket. Make sure you put a delimiter between the values to ensure uniqueness of the key.
var domain = HttpContext.Request.Url.DnsSafeHost;
var user = HttpContext.User.Identity.Name;
var key = "__" + domain + "_" + user + "_" + "KEY";
HttpRuntime.Cache.Insert(key, "VALUE", null, DateTime.Now.AddMinutes(30),Cache.NoSlidingExpiration);
Note that if you use HttpContext.Session, it will automatically put different domain information into separate buckets because it is based on a cookie (which by default is domain specific). However, HttpContext.Session comes with its own set of problems.

Python requests module login using session

I have a intranet application which i'm able to login using username/password.
Im trying to access the same application using python requests passing my username/password but that does not work. Its a difficult task to get a application support.
Meanwhile, I want to see if I can login using the session the web application created which is in my other browser.
How can I just pass the session using the requests module.
You may need to send data among username and password (usually this data is generated randomly by the server and are inside <input type="hidden"> form elements) which we prevent the CSRF (cross-site request forgery) attack. You may also need to send cookies the Web server send to you.
So, to solve your problem you need to:
Create a requests session to handle cookies;
Request the login page (where the form is) and extract CSRF data;
Post CSRF data among your credentials to the form action URL.
The code will be like this:
import requests
LOGIN_URL = '...' # put URL here
# replace 'user' and 'pass' with form field names for credentials
CREDENTIALS = {'user': '...', 'pass': '...'}
session = requests.session()
# get cookies and CSRF data
response = session.get(LOGIN_URL)
csrf_data = ... # extract data from response.text in a dict
# post data
csrf_data.update(CREDENTIALS)
# you may change LOGIN_URL below with form's 'action' URL if they differ
response = session.post(LOGIN_URL, data=csrf_data)
# now check response.text to see if you're logged in
There are many ways to extract CSRF data, such as: simple string manipulation, regular expressions or more specialized libraries such as lxml and BeautifulSoup (it depends on your input HTML and familiarity with the methods).

Meteor.user() on iron-router server side

How can check, on server side route, if user is logged?
I would add check on 'before', but Metor.user() don't work here.
thanks in advance.
p.s. I have found How to get Meteor.user() to return on the server side?, but not work on iron-router
I'm afraid that this is not possible. I guess that the problem comes from the fact that you're trying to connect to the server with two different protocols - both literally and in logically - so there is no obvious way to relate this two actions.
There is, however, a pretty simple solution that may suit your needs. You'll need to develop a simple system of privileges tokens, or secret keys, or whatever you call them. First, create a server method
var Secrets = new Meteor.Collection("secrets"); // only on server!!!
Meteor.methods({
getSecretKey: function () {
if (!this.userId)
// check if the user has privileges
throw Meteor.Error(403);
return Secrets.insert({_id: Random.id(), user: this.userId});
},
});
Then, you can now use it on the client to get the secretKey which attach to your AJAX request (or something), either within the HTTP header or in the URL itself. Fear not!
They will all be encrypted if you're using HTTPS.
On the server side you can now retrieve the secretKey from the incoming request and check if it is present in the Secrets collection. You'll know then if the user is granted certain privileges or not.
Also you may want to remove your secret keys from the collection after some time for safety reasons.
If what you're looking to do is to authenticate the Meteor.user making the request, I'm currently doing this within the context of IronRouter.route(). The request must be made with a valid user ID and auth token in the header. I call this function from within Router.route(), which then gives me access to this.user:
###
Verify the request is being made by an actively logged in user
#context: IronRouter.Router.route()
###
authenticate = ->
# Get the auth info from header
userId = this.request.headers['x-user-id']
loginToken = this.request.headers['x-auth-token']
# Get the user from the database
if userId and loginToken
user = Meteor.users.findOne {'_id': userId, 'services.resume.loginTokens.token': loginToken}
# Return an error if the login token does not match any belonging to the user
if not user
respond.call this, {success: false, message: "You must be logged in to do this."}, 401
# Attach the user to the context so they can be accessed at this.user within route
this.user = user
###
Respond to an HTTP request
#context: IronRouter.Router.route()
###
respond = (body, statusCode=200, headers={'Content-Type':'text/json'}) ->
this.response.writeHead statusCode, headers
this.response.write(JSON.stringify(body))
this.response.end()
This code was heavily inspired by RestStop and RestStop2. It's part of a meteor package for writing REST APIs in Meteor 0.9.0+ (built on top of Iron Router). You can check out the complete source code here:
https://github.com/krose72205/meteor-restivus

ACS - bypassing user redirection to IdP?

I have only recently been looking into ACS, AAL, WAAD and I would like to avoid redirecting users to the login page of their IDP. I want to keep my users within my site and present them with a dropdown to choose who they wish to authenticate with and an area to request a username and password, then acquire token via code. Is this possible?
I have been reviewing some sample applications and produce a quick mock-up, but cant seem to get things working e.g.
_authContext = new AuthenticationContext("https://littledeadbunny.accesscontrol.windows.net");
string enteredEmailDomain = UserNameTextbox.Text.Substring(UserNameTextbox.Text.IndexOf('#') + 1);
IList<IdentityProviderDescriptor> idpdList = _authContext.GetProviders("http://littledeadbunny.com/NonInteractive");
foreach (IdentityProviderDescriptor idpd in idpdList)
{
if (String.Compare(ServiceRealmDropDownList.SelectedValue, idpd.Name, StringComparison.OrdinalIgnoreCase) == 0)
{
Credential credential;
credential = new UsernamePasswordCredential(enteredEmailDomain, UserNameTextbox.Text, PasswordTextbox.Text);
_assertionCredential = _authContext.AcquireToken("http://littledeadbunny.com/NonInteractive", idpd, credential);
return;
}
}
Using the code above, when I try to use the Windows Azure Active Directory User (admin), i get the error "Data at the root level is invalid. Line 1, position 1." where I attempt to acquiretoken.
When I use Google, I get an error "0x8010000C: No identity provider matches the requested protocol".
If there is a working sample? if I am doing something obviously wrong, I would appreciate the correction.
This is not supported for passive identity providers. IdPs like Google, Facebook, etc. don't want other people collecting credentials for them, as this leads to security issues and possible phishing attacks. They also don't support it because they need to be able to show a permission dialog (that screen that asks the user if they want to release data to you) which they can't do without the browser redirecting to them. Furthermore, Google in particular supports two-factor auth, which you couldn't replicate, and generally collecting credentials opens up whole cans of worms around other UI problems such as incorrect or forgotten passwords.
This is also generally a bad user experience, because your users are fairly likely to already be logged in to Google and have cookies there. If so, and if they've already consented to your app, they would just be silently redirected back to you. In your scenario, even if the user is already logged in they'd still have to provide a username/password.
The correct way to do these sorts of logins is to render a browser control in your app that allows the user to log in at their IdP, which is what AAL helps with.
I had the same error, executing a powerscript solved that error
PS C:\windows\system32> $replyUrl = New-MsolServicePrincipalAddresses
-Address https://mydomain.accesscontrol.windows.net/
PS C:\windows\system32> New-MsolServicePrincipal -ServicePrincipalNames
#("https://mydomain.accesscontrol.windows.net/") -DisplayName
"MyDomain Namespace" -Addresses $replyUrl
But i'm stuck anyway with a 403 permission error
If you get any further i would like to know how :)

Issue with Response.Redirect to another site

I'm creating a gateway app which will control access to various other apps (tools).
On visiting the site the user is identified and a list of tools they have access to is displayed. Clicking the link takes the user to the tool. The URL is affixed with a token as a querystring. The token is encrypted.
On arriving at the tool site the system checks to see if there is a querystring with a token. It checks to see if the token is valid (the date is part of the encryption). If OK then the token is also saved as cookie which is valid for 8h and access is granted.
If the user hits the Tool site directly from a bookmark the system once again checks to see if there is a token and that it is valid. If no token is passed as a querystring then the system will see if it still has a valid cookie. If there is no valid token or cookie the site invokes a response.redirect to the gateway together with two querystrings t and r. t is the tool's numeric ID and r is the Tools URL.
What should happen is that the user will be redirected to the Gateway which will check to see if the user has access to tool id t and if the have redirect back to r with a fresh token appended as a querystring.
My code has the following....
Private GatewayURL As String = "http://GatewayURL/default.aspx?t=2&r="
Private ToolURL As String = "http://ToolURL/default.aspx"
In my page load I have….
…
If AuthenticationPass = False Then
'We are not authenticated...
Response.Redirect(GatewayURL & ToolURL, True)
End If
…
Unfortunately when this is triggered I get the following error...
Invalid path for child request 'http://GatewayURL/default.aspx'. A virtual path is expected.
I've run out of ideas on resolving this.... any help appreciated.
You should URL encode your query string parameters:
Response.Redirect(GatewayURL & HttpUtility.UrlEncode(ToolURL), True)
so that you redirect to:
http://GatewayURL/default.aspx?t=2&r=http%3A%2F%2FToolURL%2Fdefault.aspx
instead of:
http://GatewayURL/default.aspx?t=2&r=http://ToolURL/default.aspx
The second is a pretty broken url.

Resources