ASP.NET Login via Custom WCF Rest Call - asp.net

I'm writing an admin panel in ASP.NET for an existing set of web service calls. My goal is to use some of the existing login stuff (locking out pages if your not logged in) etc but using my login mechanism. This works by hitting an http post request with a username and password, if you're good you get a session id back, if not you get a 401. Here is the WCF for that:
[WebInvoke(UriTemplate = "/Login/", Method = "POST")]
public String Login(User user)
{
// If we are good return sessiond id
// Otherwise throw 401 etc
So to get this working in ASP.Net what is needed?
I think this:
Implement a user that overrides MembershipUser and has a session id in it.
Implement a membership provider that overrides MembershipProvider and does all the WCF calls etc.
In the Web.Config set up a custom membership provider.
Is this correct or am I missing something major?

Rather than do this yourself, you might want to take a look at the WCF Authentication Services.
Before you go down this route, be aware that the authentication services supports login and logout, but that is about it. The usual Membership methods such as CreateUser aren't available. If you need them, you'll need to create three projects:
A WCF Service Application with a single service called WCFMembershipService that wraps the core Membership Provider requirements, i.e. calling Membership.Provider.Method(). Configure the standard SQLMembershipProvider in the web.config, and
A custom membership provider to be used in the ASP.NET application that calls your WCF service from step 1, and
An ASP.NET Application with the custom membership provider configured
You will find that the Membership and Role providers are extremely easy, but the Profile provider is more challenging, because you cannot serialize the default properties that the provider requires, such as SettingsPropertyValueCollection.
In this case, you would need to convert the SettingsPropertyValueCollection into a serializable type first, and then reconstruct it at the other end. Probably a Dictionary<string, string>() would suffice.

Related

Securing WCF service in an ASP.net financial application

I have an MVC4 ASP.net financial application with a WCF service. The current scenario isn't secure enough and I need you help with that.
The current scenario:
The user login using a login form, and I send his login details using a JSON object containing the UserID and the Password to the WCF service:
http://example.com:22559/Login
This returns a JSON object with a true or false.
If it's true, I use the ASP function
FormsAuthentication.SetAuthCookie(loginModel.UserID, loginModel.RememberMe);
to set authorization cookies to use it later.
When the user adds a new Transaction I send a JSON object containing the transaction details without the password to:
http://example.com:22559/AddTransaction
I depend here that the MVC controller will not allow the user to access the transaction page if he isn't logged in.
The problem is anyone can now sneak-out and add a transaction without entering the password!, using the URL:
http://example.com:22559/AddTransaction
What I need to do now, is to make the WCF service itself secured, but I don't know how to do that without making the user enter his username and password each time he adds a new transaction!, so, what is the proper way to do that?
Thanks in advance.
MVC 4's controllers typically use MemberShipProvider for authentication and RoleProvider for authorization. So your WCF services may share the providers.
For authentication, you don't need to do anything in WCF codes. For authorization, it is handy to decorate respective operation implementation function (not interface) with PrincipalPermissionAttribute with the Role name defined. The rest will be done in config and by runtime.
For more details, just search "membershipprovider wcf", you will find a lot articles/tutorials in MSDN, CodeProject and StackOverflow.
Please be mindful that in MVC 5 if you will be moving to MVC5, Identity 2.0 is used by default, and MembershipProvider and RoleProvider will not be their by default. Nevertheless, your WCF codes should remain unchanged, and only some elements under ServiceModel will need to adapt the new custom authentication and authorization, and the client codes should remain the same as well, no config change needed.

Passing Custom User Object to WCF

I've implemented a custom ASP.net membership provider to deal with forms authentication. The custom provider uses a custom User object for authentication and authorization. I was wondering If I can pass this object to each WCF call without adding it to the parameters list?
Since you are already using a MembershipProvider you can utalize that on wcf as well so both are secured by the same mechanism.
See this post on msdn.
Windows Communication Foundation (WCF)
developers can take advantage of these
features for security purposes. When
integrated into an WCF application,
users must supply a user name/password
combination to the WCF client
application. To transfer the data to
the WCF service, use a binding that
supports user name/password
credentials, such as the WSHttpBinding
(in configuration, the wsHttpBinding
Element) and set the client credential
type to UserName. On the service, WCF
security authenticates the user based
on the user name and password, and
also assigns the role specified by the
ASP.NET role.
Another option would be to create a custom IAuthorizationPolicy that pulls off your user via
OperationContext.Current.IncomingMessageHeaders.GetHeader<T>
And than setup your principal like the following:
evaluationContext.Properties[Constants.EvaluationContextPrincipal] = principal;
Here is some more information on creating a custom IAuthroizationPolicy. With this method you could achieve what you want without passing your user to the method.
Just be warned if you go this route a crafty person could end up impersonating the user by simply suppling a bogus user in your header.
Using the asp.net membership provider for wcf would most likely get you what you are really after plus adding some security.
You definitely should not add this to the parameters each method.
I do not know about your custom user object but as far as WS* and most security standards concerned, your user object will have username and password.
The answer depends on the binding you use. BasicHttpBinding or BasicHttpContextBinding can use HTTP authentication schemes while WsHttpBinding can use custom Message security which you can provide user name and password.
BasicHttpContextBinding is especially good since it can work with ASP NET session.

Authenticating ASP.NET web app with WCF service

Background
I have an ASP.NET web application that interacts with WCF services. The web application and the WCF services are under my control. The ASP.NET web application uses a custom implementation of the ASP.NET Membership Provider Model (with passwords stored in hashed form) to authenticate users who log in to the web application. Both the ASP.NET web application and WCF services have access to the same membership database.
Since the users will supply their password only once, and I don't want to have to store their passwords anywhere or annoy them by repeatedly asking them to resupply their password, I need an appropriate mechanism to authenticate the users with the WCF services.
Based on other questions and answers I have seen, I am considering a sort of "login session" approach, where a login session will be created in the custom membership database when the user initially logs in to the web application, with the login session identified by a GUID, and automatically expired after a period of inactivity. The login session GUID will be "remembered" by the web application for each logged in user (either stored in the Forms Authentication Ticket or in the session).
The WCF service will also provide its own login operation accepting a user name and password and returning a login session GUID as described above.
The WCF service would then accept the login session GUID for all other operations, and verify that the GUID represents a valid login session that has not expired before allowing the operation to proceed.
I have done quite a bit of background reading on this, and there is a lot of material on straightforward use of the UserName client credential type, but this would require the web application to remember the user's password, which doesn't seem like a great idea to me.
I've done some research and found material on MSDN, but this seems like a lot of effort to implement what (to me at least) seems like a pretty common usage scenario.
How to: Create a Custom Token
Question
Is the general approach of the "login session" described above reasonable?
If so, what is the best way to achieve it?
If not, can you suggest an alternative?
This is a very reasonable approach.
To do this you setup your service endpoint and configure it with your custom membership provider (You can do the same with SQL membership provider, it doesn't require a custom one).
On the web application you set up the Authenticate event of the Login control to instantiate a new service proxy and set the username/password in the ClientCredentials in the proxy.
Now when you make the call to the Service through the proxy WCF will pass these credentials through the secure channel to the service and use them for authentication.
Now you simply need to store the proxy in session and use it for future access to the service as it has the channel state and a private key.
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
bool Authenticated = false;
try
{
MyServiceClient proxy = new MyServiceClient("MyServiceEndpoint");
proxy.ClientCredentials.UserName.UserName = LoginControl.UserName;
proxy.ClientCredentials.UserName.Password = LoginControl.Password;
//It doesn't really matter what is called or what it does because
//Membership Provider for the Service does the authentication.
string retval = proxy.login("Logging in");
//Now that channel is established the proxy needs to be kept
//since it contains the channel state which includes a private key
Session["MyServiceProxy"] = proxy;
Authenticated = true;
}
catch (Exception ex)
{
//Login Error...
}
e.Authenticated = Authenticated;
}
There are two possible solutions I can think of:
Firstly, if the WCF service is an internal service, the web application can then send the name of the user that is asking for the data with each request.
The second is that you store the user name and hash of the password (or actual password) somewhere. Either in the session state or in the user cookie (a session cookie stored in memory passed to the user over https). Then pass the user name and password to the WCF service with each request.
See my answer on Storing password in forms authentication cookie - ASP.NET and WCF calls for a solution that does not require storing passwords.
Thanks to everyone for your input. I've settled on an approach (at least for now). It's fairly simple and works well for my purposes.
Using the "UserName" clientCredentialType, and an explicit service login method that returns a security token (token generation details omitted for brevity), the service client can decide whether to pass the genuine password as the password property on the client credentials or the security token instead (obtained from the login method). If the client is a web application the security token could be stored in the forms authentication ticket, or the session, or wherever.
Using the "Custom" userNamePasswordValidationMode and a custom implementation of UserNamePasswordValidator, the validator inspects the password to determine whether it is a valid security token or a password - if it's a password the client credentials are authenticated against the user store (SQL Server database), and if it's a security token then it is checked to ensure it is valid, hasn't expired, and belongs to the user name.
I would suggest to take a look at Geneva which is aimed at solving scenarios as yours. The basic idea is to require the same security token, by mean of an HttpModule, for both the WCF services and the ASP site. The token will be release after authenticating against your membership database and may contain useful info (claims) on the user.
For an intro you may read Bustamante's article.
Microsoft has a WCF service that you can use to authenticate users with ASP.NET Membership.
The code is actually built into the framework - you just need to create a .svc file to use it.
http://msdn.microsoft.com/en-us/library/bb398990.aspx

Limiting access to a WCF REST (webHttpBinding) Service using the ASP.NET Membership Provider?

I have found a lot of material on the web about using the ASP.NET Membership Provider with the wsHttpBindings, but I haven't seen any reference to using it with webHttpBindings.
I am looking for a system that will work in two scenarios:
The user is logged into an asp.net website and the website is making calls to the service.
The user accesses the service directly via REST.
Is this possible using the built in framework (i.e. just through configuration)? If so how do I configure the service? And how does the user pass the credentials to the REST service?
The best source I've found is here: http://www.leastprivilege.com/FinallyUsernamesOverTransportAuthenticationInWCF.aspx
The site also has tons of other information about setting up HTTP Modules to handle basic authentication (which I'm guessing you'll be using since it is kind of the standard).
The HTTP Module authentication method is located on Codeplex with sample code and everything here: http://www.codeplex.com/CustomBasicAuth
ADO.NET Data Services you mean?
You can build a QueryInterceptor for an entity and limit the access to it.
For example:
[QueryInterceptor("foo_entity")]
public Expression<Func<foo_entity, bool>> FilterMembership()
{
MembershipUser user = Membership.GetUser();
Guid userGuid = (Guid)user.ProviderUserKey;
return c => c.UserId == userGuid;
}

Custom .NET authentication, membership, profile provider for portlets?

I am wondering if it is possible to leverage the Authentication, Membership, and/or Profile provider features in .NET to help integrate .NET web apps into my company's enterprise portal. In a nutshell, the portal sends custom header values to any application that is 'behind' the portal for fields like the username, user profile data, and some access rights. One issue that we have with the portal is that we aren't able to leverage many of the .NET apps available on the web because they weren't designed to be "portal aware", primarily to trust that the user has already authenticated.
Would it be possible to somehow write a custom authentication provider (or maybe leverage forms auth somehow) to just look at the header (plus the IP) and automatically "authenticate" as that user? My thinking is that by writing a profile provider, possibly a membership provider, and somehow adding authentication I would be able to download cool components like the Oxite blog (.net mvc demo that i found), switch providers to my custom one, and leverage it behind my company's portal with minimal code changes.
Does this make any sense? I feel that I might not be understanding how these components fit into the puzzle.
I don't think you can do this with zero changes to the .Net app, but I think you might be able to do it with minimal changes. Please note that I'm assuming that the portal Gateways everything that is going to the .Net web application. That is, the portal gets every HTTP request from the browser, adds its own headers to it, submits the request to the .Net app, gets a reply from the .Net app, rewrites things some more, and then returns info to the browser.
I'm guessing that what happens now (the reason you wrote this question) is you embed this .Net app into a portlet on your portal, but when someone tries to browse to it, even though they are logged into your portal, they see the external .Net login screen inside the portlet box. Not very nice.
There are two steps that need to be taken here:
Re-do the login page for the .Net app to auto-login portlet users
Create a custom membership provider that works along with #1
1. Re-do the login page for the .Net app to auto-login portlet users
Find the login page for the .Net app. It'll probably be something like login.aspx. Copy it (and any associated codebehind files) to portallogin.aspx and portallogin.cs.
Open up the portallogin.aspx and portallogin.cs files. Get rid of all of the controls and code in there. Replace it with something like what you see below. Please note that everywhere you see PORTAL_SomeFunctionName, you'll need to replace that with code from your Portal's SDK that makes the appropriate function call.
const string specialpassword = "ThisPasswordTellsTheBackendSystemThisUserIsOK";
Page_Load()
{
if (PORTAL_IsLoggedInToPortal())
{
string username = PORTAL_GetCurrentUserName();
// Authenticate the user behind the scenes
System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);
System.Web.Security.FormsAuthentication.Authenticate(username, specialpassword);
}
else
{
throw new Exception ("User isn't coming from the Portal");
}
}
Next, edit web.config for the .Net application and tell it that the login page is portallogin.aspx instead of login.aspx.
That should take care of automatically attempting to log the user in.
2. Create a custom membership provider that works along with #1
This is where you will need to create a custom membership provider. In order for this to work, the .Net application you are working with has to make use of membership providers and allow for a custom membership provider to be used.
Create a new Membership Provider. You need to create a class and inherit from System.Web.Security.MembershipProvider. At a minimum, I think you'll need to implement the GetUser and ValidateUser functions and ApplicationName property. Below are some ideas for how they could look. The are many more functions that need to be overridden, but the stubs (with the accompanying NotImplementedException(s)) can probably be left alone.
public override string ApplicationName
{
get
{
return "Portal";
}
set
{
;
}
}
private const string specialpassword =
"ThisPasswordTellsTheBackendSystemThisUserIsOK";
public override bool ValidateUser(string username, string password)
{
// If the password being passed in is the right secret key (same
// for all users), then we will say that the password matches the
// username, thus allowing the user to login
return (password == specialpassword);
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
string email = PORTAL_getemailfromusername(username);
System.Web.Security.MembershipUser u = new MembershipUser(
this.name, username, username, email, "", "", true, false,
DateTime.Now(), DateTime.Now(), DateTime.Now(),
DateTime.Now(), DateTime.Now(), DateTime.Now()
);
return u;
}
You can also do similar implementations for the .Net RoleProvider and ProfileProvider if that functionality would be helpful in integrating with this .Net app. (the Role provider will provide group membership information, and the ProfileProvider will provide extra bits of profile info such as email address, zipcode, or whatever other properties you want it to provide for each user. This information would have to be lookuped up from a database or from the portal HTTP header information.
Other Considerations
Since you're using a third party authentication provider for this external .Net application, you need to figure out how you can tell this .Net application which users/groups are administrators. I can't tell you that - you'll have to find that out from the third party .Net application. This is needed if there are any permissions that are needed to do anything in this .Net application beyond having an account.
Since you're using this in a portal, there are a couple of ways it can be used. You can just have one big portlet that shows the entire .Net web application. You could also have lots of little portlets that show bits and pieces of the .Net web application. Either way, you'll have to consider that the portal might or might not render things properly when it puts a full .Net application inside a little portlet box on a portal page. If you get HTML that looks or works weird, it'll be annoying to fix it. You can either try to fix the original .Net web app to spit out different HTML, or you can add a module to IIS to rewrite the HTML on the fly (I'm not completely sure it's a module... you'll have to dig some on IIS to figure out how you might do this).
Whew!
I know this doesn't cover everything, but I've worked with setting up the Plumtree Portal (now BEA's Aqualogic User Interaction) as the authentication source for Microsoft's SQL Server Reporting Services, and I've implemented a Custom Authentication Provider for IIS based on user membership stored in a Dynamics NAV table. Hopefully my experience with these projects will be helpful to you in your integration of this external .Net application with your portal.
Tim
I think that's a great idea. You definately want the Membership provider, and ensure authentication is set to 'forms' for all the .Net web apps you download.
Also check out IIS7 because its newly constructed pipeline means you can leverage .Net based authentication types in any handler (CGI, etc). So for example, you can use Windows Authentication for your PHP applications.
One minor point that the other answers are missing, is that forms authentication in asp.net supports cross authentication.
The forms authentication tag has domain and cookie name properties.
matching these & using the same machine key, will allow cross authentication between apps.
Your membership provider for each site will need to point to the same datastore though.

Resources