I'm trying to design a web application that would user WCF services to access data and provide business logic. So in general the whole system would look like that:
UI (ASP.NET MVC)
BusinessLayer (WCF Services)
DataLayer (Entity Framework)
Date (SQL Server Database)
All parts of the system will resist on the same, closed environment, so I'm going to use Certificates to secure ASP.NET <-> WCF connection. Database connection would use standard EF securities, Connection String and Windows Authentication.
The application has to provide authentication and authorization functionality. I'm going to move most of that into ASP.NET, so there will be ValidateUserAuth() service method, which will be used to validate credentials, but the result (with UserRole that user belongs to) will be then used by ASP to create user session.
After that, every Service Method call needs to know the UserRole of current user, to return proper results (or say 'Access denied' if it's necessary). Problem is I don't want to pass that UserRole as a parameter for every Service Method! I'd like to make it happen automatically. Is it even possible with WCF?
All I need is:
Every service call made from ASP.NET app will be extended with User data taken from current ASP Session.
Service Method invoked by that call will be able to recieve that User data and use it to provide results according to user permissions.
All this would happen somekind on background, so there will be no additional UserDetails method parameter added to every Service Method exposed from Service.
I read a lot about WCF itself, but found anything that could met my requirements. I hope I just missed it and it's still possible.
Passing user roles from the client to the server in a plain form would be a design mistake. Clients could easily misuse your services by calling them freely, outisde of the application scope.
Why don't you rely on a role provider? This way all you pass from the client is the identity (could even be a forms cookie) and then at the server side you read all roles. You could probably even use a built in mechanism to cache roles in a role cookie.
Some time ago I wrote two tutorials on wcf securing with forms authentication so that integration between web pages and active clients is easy
http://netpl.blogspot.com/2008/02/clickonce-webservice-and-shared-forms.html
http://netpl.blogspot.com/2010/04/aspnet-forms-authentication-sharing-for.html
I decided to use MessageInspector for that:
On Client-side:
Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest
Dim requestMessageProperty = New HttpRequestMessageProperty()
Dim currentUser = Authentication.AuthenticatedStaffcareUser
If currentUser Is Nothing Then Throw New ApplicationException()
requestMessageProperty.Headers("UserName") = currentUser.UserName
requestMessageProperty.Headers("UserId") = currentUser.UserID
requestMessageProperty.Headers("UserRole") = currentUser.UserRole
requestMessageProperty.Headers("EffectiveDate") = currentUser.EffectiveDate
request.Properties(HttpRequestMessageProperty.Name) = requestMessageProperty
Return Nothing
End Function
And Server-side:
Public Function AfterReceiveRequest(ByRef request As Message, channel As IClientChannel, instanceContext As InstanceContext) As Object Implements IDispatchMessageInspector.AfterReceiveRequest
Dim messageProperty = CType(OperationContext.Current.IncomingMessageProperties(HttpRequestMessageProperty.Name), HttpRequestMessageProperty)
Dim userName = messageProperty.Headers("UserName")
Dim userId = Integer.Parse(messageProperty.Headers("UserId"))
Dim userRole = messageProperty.Headers("UserRole")
Dim effectiveDate = DateTime.Parse(messageProperty.Headers("EffectiveDate"))
Dim identity = New AppServerUserIdentity(userName, userId, userRole, effectiveDate)
Dim principal = New AppServerUserPrincipal(identity)
Threading.Thread.CurrentPrincipal = principal
Return Nothing
End Function
I also had to set custom AuthorizationPolicy to prevent standard one from overwriting Thread.CurrentPrincipal:
Public Function Evaluate(evaluationContext As EvaluationContext, ByRef state As Object) As Boolean Implements IAuthorizationPolicy.Evaluate
Dim principal = TryCast(Threading.Thread.CurrentPrincipal, AppServerUserPrincipal)
If principal Is Nothing Then
Return False
Else
evaluationContext.Properties("Principal") = principal
Return True
End If
End Function
Related
I don't know much details about WebService. But I often assume (due to the authentication layer and response format) the code below is a also web service in Spring MVC
#RequestMapping(value = "/rest/member/account",produces={"application/json"}, method=RequestMethod.GET)
public #ResponseBody String getAccountInfo() {
JsonObject account = new JsonObject();
// this queries local DB
account = restAccountService.accountJSON();
return account.toString();
}
Now I need to modify the above code with extra WebService from different application. This extra WebService does not exist yet, I am thinking of creating it with Spring MVC just like the earlier function.
Maybe something like below
...
// this is the so called WebService to different application
SomeWebService webService = new SomeWebService();
boolean valid = webService.checkIfUserValid(username,password);
....
//this queries local DB
if (valid)
account = restAccountService.accountJSON();
...
My questions are
Is the above a common way to do this ?
How do I handled the authentication to access the other WebService ? Because I need to ensure that the call must come from the originating app.
I'm looking for some guidance on how to implement authorization security for SignalR on a back end service running in a self-hosted (non-IIS) environment, that is called from a Web application. The backend app is basically a monitor that fires SignalR events back to the HTML based client. This all works fine (amazingly well actually).
However, we need to restrict access to the server for authenticated users from the Web site. So basically if a user is authenticated on the Web site, we need to somehow pick up the crendentials (user name is enough) and validation state in the backend app to decide whether to allow the connection as to avoid unauthorized access.
Can anybody point at some strategies or patterns on how to accomplish this sort of auth forwarding?
I am having similar issues here, as in my web app I use a simple cookie authentication system which uses an AoP style approach to check for any controllers with an attribute, then will get the current context (be it from the static HttpContext.Current or from the target invocation object depending on the type of interceptor) and then verify the cookie exists, it contains right data, then finally verify the token with the db or cache etc.
Anyway this approach can also be used for Signalr, although its a bit more long winded and you are using dependency injection. You would basically wrap the hub calls with the desired attribute, then set up your DI/IoC configuration to intercept these calls, then either get the hub instance within your interceptor and get the cookie (or your custom authentication mechanism) from the request, verify it is all valid or not, and if not then throw a new HttpException("403", "Not authenticated"); which should kick the user out and return back before it even hits your hub method, this way you can put the logic in one place (your interceptor, or a class the interceptor consumes) then just wrap any method that needs to use this authentication using your attribute.
I use Ninject and the interception extension, but most major DI frameworks these days have some form of IoC plugin/extensions, such as Autofac, Windsor, Spring etc.
If you were not happy going down the route of introducing DI and/or AOP to your current project, then maybe you could just create a custom hub instance which contains your authentication logic and then just use that in your hubs, so ok you will still be manually calling some authentication logic from within each hub method you want to protect, but its less code, so something like:
public class AuthorisableHub : Hub
{
private ISomeAuthenticationToken GetSomeAuthenticationTokenFromRequest(Request request) // probably a SignalR specific request object
{
// Get your token from the querystring or cookie etc
}
private bool IsAuthenticationTokenValid(ISomeAuthenticationToken token)
{
// Perform some validation, be it simple or db based and return result
}
protected void PerformUserAuthentication()
{
var token = GetSomeAuthenticationTokenFromRequest(Context.Request);
var isRequestValid = IsAuthenticationTokenValid(token);
if(!isRequestValid)
{ throw new HttpException(403, "<Some forbidden message here>"); }
}
}
public class MyFancyPantsHub : AuthorisableHub
{
public void TellAllClientsSomethingSecret(ISecret secret)
{
PerformUserAuthentication();
// Do stuff with the secret as it should have bombed the user out
// before it reaches here if working correctly
}
}
It is not perfect but would work (I think), also I am sure I once read somewhere that Hubs are newly instantiated for each request, and if this is indeed true, you could possibly just put this logic in your constructor if you want to apply the authentication to every action within the hub.
Hope that helps, or gives you ideas... would be interested in knowing how you did solve it in the end.
SignalR does not provide any additional features for authentication. Instead, it is designed to work with the authentication mechanism of your application.
Hubs
You should do authentication as you normally would and then use the Authorize attribute provided by SignalR to enforce the results of the authentication on the Hubs.
The Authorize attribute can be applied to an entire Hub or particular methods in the Hub. Some examples:
[Authorize] – only authenticated users
[Authorize(Roles = "Admin,Manager")] – only authenticated users in the specified .NET roles
[Authorize(Users = "user1,user2")] – only authenticated users with the specified user names
You can also require all Hubs to require authentication by adding the following method in the Application_Start method:
GlobalHost.HubPipeline.RequireAuthentication();
Persistent Connections
You can use the user object in the request to see if the user is authenticated:
request.User.IsAuthenticated
In my VS solution, I have two projects.One for the Web Interface, other for DataAcess and BusinessLogic. I know I can check if the currently logged-on user is Employee in Web Interface project like this from the code behind:
Dim isEmployee = User.IsInRole("Employee")
The problem is that I have a Class call UserManagement in my the DA and BL project which I want to check the currently logged-on user role also. I can't use Dim isEmployee = User.IsInRole("Employee") because it doesn't have aspx page.
What do I need to do to check the user role in my custom class?
Thank you.
You need to reference System.Web in your business project. Then do the following:
Dim context As System.Web.HttpContext = System.Web.HttpContext.Current
Dim isRole As Boolean = context.User.IsInRole("Admin")
or c#
System.Web.HttpContext context = System.Web.HttpContext.Current;
bool isRole = context.User.IsInRole("Admin");
In your web application, when you initially determine the role(s) for a user, that code should be calling business objects of some sort that make the determination. So the dependency is from your web app to your business layer (i.e. your web app requires your business layer), not the other way around.
I'm writing a silverlight application that resembles a shopping cart system.
This app can only be launched from the asp.net website after the user is logged in to the site.
Upon first load, the app will send a request to the backend through WCF service to retreive all the existing items in the shopping cart.
Therefore the silverlight app needs to know who the current user is, to find which shopping cart to load.
I found there are a couple of ways so far, but not happy with any of them:
using wcf aspnet compat. silverlight can ask who the current user is by asking the wcf service.
pass parameters from the page to xaml by using xaml.InitParameters and pass in the minimum amount of information to identify a user in a serialized format.
pass parameters through query string to xaml (apparently this is also possible)
Can anyone share the best practice to achieve this?
Thanks
We use the first solution in our projects. You haven't to invent any type of serialization format or so in this case. A disadvantage of this approach - extra async logic at startup.
The example of service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class UserInfoService : IUserInfoService
{
public UserInfo GetUserInfo()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
return null;
var userInfo = new UserInfo
{
Login = HttpContext.Current.User.Identity.Name,
Fullname = ...,
};
return userInfo;
}
}
Sending userid via initParams or query string is not good idea I think. Such things should be more hidden.
The real important thing is to verify user on server on each service call because anyone can call your services in similar way as your app.
HTH
The System.Security.Principal.WindowsIdentity.Impersonate function takes a System.intptr parameter, which seems awfully useless in my situation (with my limited understanding).
I am developing an intranet application that uses integrated security to authorize users page-by-page against a role associated with their Windows Identity. I have no passwords or anything of the sort. Simply a Windows username. For testing purposes, how could I possibly impersonate a Windows user based on their username? The "impersonate" method jumped out at me as obvious, but it takes an unexpected parameter.
Thanks in advance :)
In a nutshell, no. But you are on the right track. You either have to get the user by simulating a login with LoginUserA ( C Win32 Api ) or set your IIS site to Windows Authentication.
In that case, your Page will have a property named User of type IPrincipal, which you can then use to run as that user. For example ( sorry, C# code ).
IPrincipal p = this.User;
WindowsIdentity id = (WindowsIdentity)p.Identity;
WindowsImpersonationContext wic = id.Impersonate();
try {
// do stuff as that user
}
finally {
wic.Undo();
}
Are you using anything windows-specific from the WindowsPrincipal or is it just a handy way to get auth/auth without having to manage users? If you need to be windows-based, Serapth has the right method. If you are just really using it as a convenient auth/auth store, then you should probably write your code to interface with IPrincipal. You can then inject your own implementations of IPrincipal with the desired values into either the HttpContext.User or Thread.CurrentThread.Principal depending on the nature of your tests and app.
User tokens (the value that the IntPtr in Impersonate represents) represent more than the username. They are a handle into an internal windows context that include information about the user, authentication tokens, current security rights, etc. It's because of this that you can't impersonate without a password. You have to actually "log in" to create a token via the LogonUser method.
What I've done in the past is create a special "Test" user account with a password that I can just keep in code or a config file.
You shouldn't need to impersonate to check role membership. Simply use the constructor for a WindowsIdentity that takes a UPN (userPrincipalName) constructed from the username according to your UPN conventions, then create a WindowsPrincipal from that identity and use IsInRole to check membership in a group. This works on W2K3 and a Windows 2003 domain, but not in other circumstances.
var userIdentity = new WindowsIdentity( username + "#domain" );
var principal = new WindowsPrincipal( userIdentity );
var inRole = principal.IsInRole( "roleName" );
You may also want to consider using either the standard or a custom role provider that will allow you to use the Roles interface to check membership.