I have two webapplication, one is a simple authenticationsite which can authenticate the logged in user and redirects him then to another site.
Therefore I have to pass ther userId (GUID) to the second application. Currently this is done via the URL but i would like to hide this id.
Has anybody an idea how to do this properly?
[EDIT]: I can't use the Session because of the ApplicationBoundaries (2 different Servers)
This sounds like a tricky situation.
There are however several options you can use but it all depends on what your application does.
Let's call WebApp1 your authenticate site, and WebApp2 your distination site once authenticated.
Can WebApp2 not call WebApp1 behind the scenes? (Services)
THe problem with passing this Guid between applications is it's going through clear text, and considering it's a user id, if anyone manages to intercept this they will have access to WebApp2 for life. Whether you pass it in a querystring or form variable, it's still vulnerable.
If you can't use WebApp2 to query WebApp1, you should consider WebApp1 creating a temporary Guid that expires. That would be much safer long term, but as it's clear text is still susceptible to attack. The 2 web apps will also need access to the same data store.
Ultimately, i think the AUthentication Site should be a service which WebApp2 can consume.
Users should login through WebApp2, which will call WebApp1 securely for authentication.
WebApp2 can then manage it's own session.
If you can't use cookies because it's cross domain then encrypt it, with a nonce.
Setup a shared secret/key between the two servers; send the encrypted GUID and nonce combination to the second server. Unencrypt, check the nonce hasn't already been used (to stop reply attacks), then use the unencrypted GUID.
If you want to be extra tricky have a web service on app1 where it can check the nonce was actually issued (at this point you're heading towards WSTrust and a single sign-on solution, which generally solve what you're trying to do)
Even with cookies, as they're easily edited/faked, you should have some form of checking.
You have two ASP.NET web applications, and one application does nothing but authenticate a user?
this sounds like a job for....
Web Services!
Create a new web service on the authentication app (They are the .asmx extension), and add a single method that takes in the user and password etc, and returns authentication info.
Then import the WSDL on your 2nd app, and call the 1st app like it was a method. It will simplify your code, and fix your issue.
An Example:
AuthenticateUserService.asmx goes on the Authentication app:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AuthenticateUserService : System.Web.Services.WebService
{
[WebMethod]
public bool AuthenticateUser(string username, string passhash)
{
// Fake authentication for the example
return (username == "jon" && passhash == "SomeHashedValueOfFoobar");
}
}
Once this is setup, fire up your main app, and right click the project and click "Add Web Reference".
Enter the url to the asmx on the authentication app, and Visual Studio will discover it and create a proxy class.
Once that is done, we can call that method like it was a local method in our main app:
protected void Page_Load(object sender, EventArgs e)
{
// Now we can easily authenticate user in our code
AuthenticateUserService authenticationProxy =
new AuthenticateUserService();
bool isUserAuthenticated =
authenticationProxy.AuthenticateUser("jon", SomeHashMethod("foobar"));
}
So, what does this really do?
It eliminates the client from the authentication process.
Your current process:
Client Enters credentials to AppA
AppA redirects the client to AppB
AppB redirects the client back to AppA if the credentials match.
Is replaced with a server side SOAP call between AppA and AppB. Now its like this:
Client enters credentials in AppA
AppA asks AppB if they are good
AppA serves proper content to the client.
Pass the GUID through a session, best way.
http://www.w3schools.com/ASP/asp_sessions.asp
OR, since it's 2 different servers, pass the information by POST method:
http://www.w3schools.com/aspnet/aspnet_forms.asp
The other possibility is to store the session state in a database on the local server, and remotely access that database from the other server to see if the user has successfully logged in and within session timelimit.
With that in mind, you can do the entire authentication remotely as well. Remotely connect to the local database from the remote server and check the login credentials from there...that way you will be able to store the session and/or cookie on the remote server.
I would recommend AGAINST the hidden field proposition, as it completely counteracts what you are trying to do! You are trying to hide the GUID in the URL but posting the same information in your HTML code! This is not the way to do it.
Best choice is the database option, or if not possible, then use HTTP POST.
Use session variables or HTTP POST instead of HTTP GET.
Instead of passing it via a query string you should create a hidden form field with its value and then post to your 2nd page, which can then grab the posted value and it will be hidden from the user.
If the servers have a common domain name, you can use a cookie.
EDIT: Cookies will just hide the ID visually, it is still accessible. Same with hidden fields or using POST rather than GET. So if the ID is confidental and you want to avoid to send it over the network unencrypted, you need a different approach.
A solution could be to encrypt the ID on the auth server with a key which is shared by the servers. Another solution could be to generate a random GUID on the auth server, and then let the auth server directly inform the other server (over SSL) which ID the GUID corresponds to.
go for session mangement or use a HTTP Post as said in the above post.
Related
A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.
But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?
In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.
For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?
I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.
If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.
Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.
If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.
Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.
Create an MVC action filter and add it as a global filter during Application_Start.
Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.
The global MVC filter does this:
Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.
If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.
The WebAPI filter does this:
Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.
If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.
Otherwise, throw a 403 Forbidden exception.
A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.
I have created a custom security extension describes here: http://msdn.microsoft.com/en-us/library/ms155029.aspx.
We have a application that store username, groups, etc.
The custom security extension fetch data from application. All works fine so far.
Now there is the new challenge:
I want to have multiple applications, that use the same report server.
My approach is, that my custom security extension connects different databases for authentication and authorization. I implemented this so far and in theorey this works fine.
I run in to one problem that make me headaches.
I thought, simple identify each connection with a custom table where I store the session (request cookie from my application, works in UILogon.aspx.cs and Logon.aspx.cs) and the domain (via Request.Url.Host in the same pages).
I store those data in a custom created table in ReportServer database and in all other methods I get the cookie session informationen to retrieve the info which domain it is to choose to correct database.
My problem: I loose the cookie information to identify the session.
I tried made a Singelton instance class to store this for each instance, but this is not "application" wide. So I thought, well somehow the SQL reporting services must hold the info about the session (probably in an own SSRS session cookie). If this is accessable I can store domain and ssrs session cookie to identify it.
So, how I can read / get the ssrs session cookie information in the methods.
E.g.
public bool CheckAccess(
string userName,
IntPtr userToken,
byte[] secDesc,
FolderOperation requiredOperation)
{
// here I need to know in which SSRS session I am
}
Anyone know how to get this OR how to identify unique a session so I can map the session to additional information?
Found a solution....
I send my own Session Cookie to the server to identify where I am !
<PassThroughCookies>
<PassThroughCookie>_cookie_session</PassThroughCookie>
</PassThroughCookies>
Then I have var cookie = HttpContext.Current.Request.Cookies["_cookie_session"]; always my cookie and can get the ID to identify the instance.
I have a website that holds one setting - a number that the user clicked.
I want the user to see this number and be able to change it at any time. Up until now I was using cookies to achieve this. But then I stumbled across an error : When user opens this address to my website :
http://ServerName/Pick
He sees a certain number. But when he opens this address :
http://ServerName.ServerDomain/Pick
He sees a diffirent number. And in the browser settings I see 2 cookies : one with the domain "ServerName" and one with the domain "ServerName.ServerDomain".
Is there any way to share the same cookie without relaying on wether the user specified a domain name? If not, is there a way to do this without cookies?
NOTE : I have full control over the client and server side (ASP.Net MVC)
You can't share cookies for different domains, but you can use one domain as a primary domain that will issue a cookie and to read cookie from other domains you should redirect to that domain, read cookie and redirect back with a value in query string. Similar, like google redirects all Sign-In requests from google.com, google+, docs etc to account.gooogle.com or microsoft from MSN, Hotmail, etc to login.live.com and then back.
My requirement is like that, I want to create an OAuth client for the website
https://onehub.com/. I got all the required information from that site but I don't know from where I need to start.
Application URI http://XYZ/SIDemo
Redirect URI http://XYZTest/SIDemo
Client ID -->9rtk1k9fsdgziiuvakujytuea6doxpk
Client Secret--> Only available immediately after the Client is created.
I am not getting what is Application URI and Redirect URI ,This website is basically used for Uploading and downloading files. I want Oauth for this site So that I can able to share files with my clients like for different clients different folder.
You should probably do some homework on OAuth.
The application URI is where your web app resides. I'm not sure of Onehub's API, but this is generally what happens in the OAuth flow -
i) You register your app and get client credentials (you already have those!). I'll talk of redirect_uri later.
ii) User clicks on a log in with onehub button that you have added to your website (which resides at the application URI).
iii) basically, you have delegated the login process to one-hub. Onehub's login page would open, it would prompt the user to accept/decline the permissions that your app has demanded.
iv) Now, once the user accepts -> Onehub would send an access token/authorization code (depending on the flow) to the redirect_uri link that you provide. So basically, you need to catch the access token in that page, hit Onehub's REST API (for which you are using OAuth) with the token and perhaps redirect to your own page once onehub returns the info to you.
I am new to MVC and actually new to web development all together. I have about 7 years of development experience but in services, database, object models, etc.. basically middle-tier and back-end development. I am trying to learn ASP.NET and decided to build a site using MVC3 for a personal site for myself. I will be hosting this from an account at dotnet-hosts.com. Here is my question... I don't have a domain and I will be using the built in membership provider. I noticed in the auto generated code that was created when I added the project template that in the AccountController in the method ChangePassword (ChangePasswordModel model) there is this line of code...
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
My question is specifically around User.Identity.Name, this looks like it would be returning the Windows user name just like Environment.UserName would. The Visual Studio template I used is the (Mobile Ready HTML5 MVC.NET) as I want to be able to support clients from any device...Windows PC, Apple, Windows Phone, iPhone, etc... If the call to User.Identity.Name is correct then I would like to ask how does this work on devices that are not Windows like an iPhone? If my assumption is correct that this will only work for Windows computers with a domain then how can I achieve this? would I need to perhaps use some caching? If so could I maybe grab the user name and their IP address to be used as the cache key from the Authentication page?
My high level question is... How do I get the current logged in user's userName regardless of the device/platform? I know this question is probably not written well and may be hard to understand... I apologize for that. I am new to web development and trying to get my feet wet and would like to start to the latest technology.
The call is correct. The User.Identity.Name is filled out by whatever authentication provider is in use - Windows authentication, Forms authentication, some custom authentication provider, or whatever. It isn't bound to a specific user "type". The authentication provider has the responsibility of making sure the Identity object corresponds to the current user on every request. Usually that part is taken care of using a combination of cookies and database.
The MVC template (although I haven't had a look at the template since MVC 2) uses ASP.NET's Membership class, which in turn uses a membership provider - for example SqlMembershipProvider or ActiveDirectoryMembershipProvider - the former stores your users' credentials (username and password etc.) in an SQL Server database, the latter uses Active Directory (i.e. primarily Windows logons). SqlMembershipProvider is the default, and MVC is set up to use a local SQLExpress database file as its user store.
The authentication provider that's implemented in the template project uses FormsAuthentication, which does the login procedure through a simple HTML form (the one in the LogOn view) and keeps the user signed in by way of an encrypted cookie. Works on any platform.
The setup for both FormsAuthentication and SqlMembershipProvider can be found in web.config (the one in the root of the site). There you can find the connection strings for the SQLExpress database (and e.g. change them to use a "real" SQL Server if needed), the timeout for logins etc.
(Note that you can do a lot of that configuration easily in a GUI through the "ASP.NET Configuration" button in the toolbar of Solution Explorer in Visual Studio - it also provides an easy way to set up the first users).
In short, it's all ready to go - and doesn't lock out non-Windows users.
Like you said User.Identity.Name is indeed correct. for returning the logged in users name. But the membership section like you said, provides only windows accounts. You can use similar without the user of windows accounts, to work in every scenario, and can still verify against windows if present. If you call it without membership, and follow the default MVC3 template it should work fine.
String Username = User.Identity.Name;
When you log on, using the template MVC3, it creates an authcookie. See account controller code. Here, two parameters are passed into it. The username, and to persist (when browser is closed - login is still cached).
The username is a string field, which is what is called by User.Identity.Name and infact, anything can be put into it, and is not in anyway linked to Windows login.
You could test the login via method you desire, and if yes, set a cookie using the authcookie method. (its encripted). And set the username to what ever you want. And if your verification of the user fails, dont create one, and redrect back to page.
See the example code. This is all from memory, as I dont have code infront of me for reference. But its all in the account controller, Login Action.
When the cookie is set, The users login state is cached for the session. You will need to ensure the user is logged in when visiting a webpage. Otherwise loggin in will be pointless. This is a simple attribute on the controller/action.
Note: dont do this to the Account/logon controller, as you wont be able to visit the logon page, as you are not logged in.
[Authorize]
public ActionResult DoSomething()
{
// ...
}
Hope I have helped.